Mybatis
自定义插件
Mybatis怎么自定义实现插件?
自定义mybatis插件的本质就是使用拦截器去干预mybatis的执行:我们可以通过一个Interceptor拦截接口去管理mybatis的四个对象
-
Executor:它负责调用 StatementHandler 操作数据库,并把结果集通过 ResultSetHandler 进行自动映射,另外它还处理了二级缓存的操作
-
基本功能:改、查,没有增删的原因是所有的增删操作都可以归结到改。
-
缓存维护:这里的缓存主要是为一级缓存服务,功能包括创建缓存Key、清理缓存、判断缓存是否存在。
-
事务管理:提交、回滚、关闭、批处理刷新。(一般我们都交由spring管理,不会使用mybatis的)
-
-
StatementHandler:拦截 SQL 语法构建的处理,用于执行SQL语句,另外它也实现了 MyBatis 的一级缓存;
-
ParameterHandler:拦截参数的处理。
-
ResultSetHandler:拦截结果集的处理。
数据脱敏插件
数据脱敏,是对数据进行处理,因此我们只需要通过拦截器Interceptor对ResultSetHandler进行拦截处理即可
定义一个枚举类
/**
* 继承JDK8提供的一个函数式接口
* 该接口的apply方法返回值是Function第二个参数,参数是该Function的第一参数
*/
public interface DesensitizationFunction extends Function<String,String> {
}
定义一个枚举类来设置各种脱敏方式
/**
* 制定脱敏的方式
* 考虑到对不同的属性进行脱敏,我们可以使用枚举类
*/
public enum DesensitizationWay {
//通过正则表达式规定脱敏策略
USERNAME(s -> s.replaceAll("(\\S)\\S(\\S*)","$1*$2")),
ID_CARD(s -> s.replaceAll("(?<=\\w{3})\\w(?=\\w{4})", "*")),
PHONE(s -> s.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2")),
ADDRESS(s -> s.replaceAll("(\\S)\\S*(\\S)","$1****$2")),
;
private final DesensitizationFunction desensitizationFunction;
DesensitizationWay(DesensitizationFunction desensitizationFunction) {
this.desensitizationFunction = desensitizationFunction;
}
public DesensitizationFunction getDesensitizationFunction() {
return desensitizationFunction;
}
}
自定义一个注解
将这个定义的注解用到实体类上,设定字段脱敏的规则
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.FIELD) //作用在属性上
@Retention(RetentionPolicy.RUNTIME) //运行的时候生效
public @interface Desensitization {
//脱敏的方式
DesensitizationWay way();
}
定义拦截器
-
继承Interceptor拦截器
-
通过@Intercepts注解指定要拦截哪个类那个方法,args中的值是拦截方法的参数
-
通过@Component将拦截器注入的spring容器
-
重新intercept方法
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.sql.Statement;
import java.util.List;
import java.util.stream.Stream;
@Intercepts(@Signature(type = ResultSetHandler.class,method ="handleResultSets",args = Statement.class))
@Component
public class DesensitizationPlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
//先获取到要脱敏的结果集
List<Object> result=(List<Object>)invocation.proceed();
//将结果集进行便利脱敏处理
result.forEach(this::desensitization);
//返回脱敏后的结果
return result;
}
//脱敏
private void desensitization(Object obj){
//获取要脱敏对象的class
Class<?> sourceClass=obj.getClass();
//获取脱敏对象的结果集
MetaObject metaObject= SystemMetaObject.forObject(obj);
//看那些属性使用了Desensitization注解
Stream.of(sourceClass.getDeclaredFields())
.filter(field -> field.isAnnotationPresent(Desensitization.class))
.forEach(field -> tuoMin(metaObject,field) );
}
private void tuoMin(MetaObject metaObject, Field field) {
String name=field.getName();
Object value=metaObject.getValue(name);
//字段属性必须是字符串,值不为空才能够进行脱敏
if(String.class==metaObject.getGetterType(name) && value !=null){
Desensitization desensitization=field.getAnnotation(Desensitization.class);
DesensitizationWay type=desensitization.way();
Object obj=type.getDesensitizationFunction().apply((String)value);
metaObject.setValue(name,obj);
}
}
}
业务代码
实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class DUser {
private Integer id;
@Desensitization(way = DesensitizationWay.PHONE)
private String phone;
@Desensitization(way = DesensitizationWay.ADDRESS)
private String address;
@Desensitization(way = DesensitizationWay.ID_CARD)
private String card;
@Desensitization(way = DesensitizationWay.USERNAME)
private String username;
}
接口类:此处省略mapper.xml的编写,根据自己需要写即可
@Repository
@Mapper
public interface MyBatisMapper {
//查询所有用户
List<DUser> selectAllUser();
}
controller类
@RestController
@RequestMapping("/mybatis")
public class MybatisController{
@Resource
MyBatisMapper myBatisMapper;
/**
* 此接口返回的数据就是脱敏后的数据
*/
@GetMapping("/tuoMin")
public String tuoMin(){
List<DUser> list=myBatisMapper.selectAllUser();
return list.toString();
}
}
sql打印插件
mybatis提供的sql语句打印功能,参数是?代替的,下面案例将展示打印完整的sql语句
定义拦截器
-
要制作sql打印插件我们可以通过拦截Executor或者StatementHandler获取到sql语句对象
-
下面我们通过拦截Executor的query方法和update方法来实现打印sql的功能
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
@Intercepts({
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
@Component
public class SqlPlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
//获取MappedStatement,MappedStatement可以理解为xml的一个查询/更新语句节点<select|update|delete|insert>
MappedStatement mappedStatement=(MappedStatement) invocation.getArgs()[0];
//获取sql语句的参数
Object parameter=invocation.getArgs()[1];
/*
*获取Mybatis提供的BoundSql拿到sql语句
* 第一个query中有BoundSql,可以直接通过invocation取出来,
* 但第二个query和update没有BoundSql参数,我们还可以通过MappedStatement拿到boundSql
*/
BoundSql boundSql=mappedStatement.getBoundSql(parameter);
//拿到configuration对象
Configuration configuration=mappedStatement.getConfiguration();
//计算sql的执行时间
long start = System.currentTimeMillis();
Object result=null;
try{
/*
*Object intercept(Invocation invocation)是实现拦截逻辑的地方,
* 内部要通过invocation.proceed()显式地推进责任链前进,也就是调用下一个拦截器拦截目标方法。
*/
result=invocation.proceed();
}finally {
long end=System.currentTimeMillis();
System.out.println("执行时间为:"+(end-start)+"毫秒");
//获取我们要执行的sql
String sql=getSql(configuration,boundSql);
System.out.println("执行sql为:"+sql);
}
return result;
}
private String getSql(Configuration configuration, BoundSql boundSql) {
//如果sql为空的话
String sql=boundSql.getSql();
if(sql==null || sql.length()==0){
return "没有可以执行的sql语句";
}
//格式化sql语句,我们自定义美化mybatis提供的sql打印格式
sql=FormatSql(sql);
/*
* 将?替换成我们真正的参数
* boundSql.getParameterMappings();可以拿到#{}中的值
* TypeHandlerRegistry:获取参数类型的处理器【int/string/对象等等】
*/
Object parameterObject=boundSql.getParameterObject();
List<ParameterMapping> parameterMappings=boundSql.getParameterMappings();
if (!parameterMappings.isEmpty() && parameterObject!=null){
TypeHandlerRegistry typeHandlerRegistry=configuration.getTypeHandlerRegistry();
//如果参数是基本类型(不包含对象类型的参数)
if(typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())){
sql=replaceSql(sql,parameterObject);
}else { //当参数复杂类型,对象等
//MetaObject对象就是我们传入的对象参数
MetaObject metaObject=configuration.newMetaObject(parameterObject);
for(ParameterMapping parameterMapping:parameterMappings){
//拿到对象的属性名称
String propertyName=parameterMapping.getProperty();
if(metaObject.hasGetter(propertyName)){
Object obj=metaObject.getValue(propertyName);
sql=replaceSql(sql,obj);
} else if (boundSql.hasAdditionalParameter(propertyName)) {
/*
*还有一种情况就是在xml中写sql时,如果用到了foreach标签,
* 里面我们是通过#{对象.属性}的方式来获取参数的。
* 这种方式的参数是动态的,参数没有固定的名称
* boundSql.hasAdditionalParameter(propertyName用来判断是否有动态参数
*/
Object obj=boundSql.getAdditionalParameter(propertyName);
sql=replaceSql(sql,obj);
}
}
}
}
return sql;
}
//对参数的不同类型做不同的处理
private String replaceSql(String sql, Object parameterObject) {
String result;
//如果参数是字符串类型需要加上单引号
if(parameterObject instanceof String){
result ="'"+parameterObject.toString()+"'";
}else if(parameterObject instanceof Date){
//如果参数是时间类型
result="'"+new SimpleDateFormat("yy-MM-dd HH:mm:ss").format(parameterObject.toString())+"'";
}else {
result=parameterObject.toString();
}
//将?替换为处理后的参数
return sql.replaceFirst("\\?",result);
}
//如果我们想自定义sql的输出格式,我们就可以在下面方法进行自定义
private String FormatSql(String sql) {
//将换行或多个连续空格换为一个空格
return sql.replaceAll("[\\s\n]+"," ");
}
}
测试接口
//查询所有用户
List<DUser> selectAllUser();
//根据id查询用户
DUser selectById(Integer id);
//根据提供的id集合查询数据
List<DUser> selectByIds(@Param("userIds") List<Integer> userIds);
//动态查询,sql中通过条件if进行判断
List<DUser> selectByCondition(DUser user);
//批量插入用户
int insertUsers(@Param("users") List<DUser> users);
<!-- 查询所有用户-->
<select id="selectAllUser" resultType="DUser">
select * from DUser;
</select>
<!-- 根据id查询用户-->
<select id="selectById" resultType="DUser" parameterType="DUser">
select * from DUser where id=#{id};
</select>
<!-- 根据集合中的所有id查询对应的用户-->
<select id="selectByIds" resultType="DUser">
select * from DUser where id in
<foreach collection="userIds" item="userId" separator="," open="(" close=")">
#{userId}
</foreach>
</select>
<!-- 动态查询,sql中通过条件if进行判断-->
<select id="selectByCondition" resultType="DUser" parameterType="DUser">
select * from DUser
<where>
<if test='id !=null and id !=""'>
id=#{id}
</if>
<if test='username !=null and username !=""'>
and username=#{username}
</if>
<if test='phone !=null and phone !=""'>
and phone=#{phone}
</if>
</where>
</select>
<!-- 批量插入用户-->
<insert id="insertUsers">
insert into DUser (phone,address,card,username) values
<foreach collection="users" item="user" separator=",">
(#{user.phone},#{user.address},#{user.card},#{user.username})
</foreach>
</insert>
调用接口和测试结果如下
@GetMapping("/printSql/{index}")
public Object printSql(@PathVariable int index){
switch (index){
case 1:
//查询所有用户
return myBatisMapper.selectAllUser();
case 2:
//根据id查询用户
return myBatisMapper.selectById(2);
case 3:
//根据id集合查询用户
Integer[] list=new Integer[]{1, 2, 3, 4, 5};
return myBatisMapper.selectByIds(Arrays.asList(list));
case 4:
//根据if判断查询数据
DUser user=new DUser();
user.setId(2);
user.setUsername("韩非");
return myBatisMapper.selectByCondition(user);
case 5:
List<DUser> users = new ArrayList<>();
for (int i = 0; i < 4; i++) {
DUser user1=new DUser();
user1.setPhone("11111111111");
user1.setUsername("测试");
user1.setAddress("xxxxxxxxxxxxxxx");
user1.setCard("222222222222222222");
users.add(user1);
}
return myBatisMapper.insertUsers(users);
}
return "请输入1到5的数字";
}
执行时间为:9毫秒
执行sql为:select * from DUser;
执行时间为:3毫秒
执行sql为:select * from DUser where id=2;
执行时间为:7毫秒
执行sql为:select * from DUser where id in ( 1 , 2 , 3 , 4 , 5 )
执行时间为:2毫秒
执行sql为:select * from DUser WHERE id=2 and username='韩非'
执行时间为:13毫秒
执行sql为:insert into DUser (phone,address,card,username) values ('11111111111','xxxxxxxxxxxxxxx','222222222222222222','测试') , ('11111111111','xxxxxxxxxxxxxxx','222222222222222222','测试') , ('11111111111','xxxxxxxxxxxxxxx','222222222222222222','测试') , ('11111111111','xxxxxxxxxxxxxxx','222222222222222222','测试')
mapper的编写方式
mapper编写有哪几种方式?
下面的案例也可以是看做在Spring中整合Mybatis的方式,总共有三种方式。
-
使用 MapperFactoryBean
-
接口实现类继承SqlSessionDaoSupport
-
使用Mapper扫描器MapperScannerConfigurer
案例环境准备
pom文件
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
</dependencies>
<!--在build中配置resources,来防止我们资源导出失败的问题-->
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
Integer id;
String userName;
Integer age;
Integer sex;
String createTime;
}
接口层
UserMapper.java
public interface UserMapper {
public List<User> selectUser();
}
UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dongjixue.dao.UserMapper">
<select id="selectUser" resultType="com.dongjixue.pojo.User">
select * from user
</select>
</mapper>
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--实体类位置-->
<typeAliases>
<package name="com.dongjixue.pojo"/>
</typeAliases>
<mappers>
<!-- 添加映射文件到Mybatis的全局配置文件中 -->
<mapper resource="com/dongjixue/dao/UserMapper.xml" />
</mappers>
</configuration>
resource/spring-config.xml
使用不同方式来编写mapper,spring-config.xml的编写略有差异,详细见下。
测试
public class MyTest {
@Test
public void selectUser() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
UserMapper userMapper=(UserMapper)context.getBean("userMapper");
List<User> users=userMapper.selectUser();
System.out.println(users);
}
}
输出结果
[User(id=1, userName=张三, age=18, sex=1, createTime=2022-05-03), User(id=2, userName=红莲, age=16, sex=0, createTime=2022-05-14)]
SqlSessionDaoSupport方式
mapper第一种编写方式:接口实现类继承 SqlSessionDaoSupport
UserMapperImpl.java
使用SqlSessionDaoSupport方式必须写一个类实现UserMapper接口,其它方式不用写这个实现类
public class UserMapperImpl extends SqlSessionDaoSupport implements UserMapper{
@Override
public List<User> selectUser() {
return getSqlSession().getMapper(UserMapper.class).selectUser();
}
}
spring-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--1.数据库连接池-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/learn_vue?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!-- spring 和 mybatis 整合 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 指定 mybatis 要连接的数据源 -->
<property name="dataSource" ref="dataSource"/>
<!--关联Mybatis-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<!--注入mapper接口实现类-->
<bean id="userMapper" class="com.dongjixue.dao.UserMapperImpl">
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
</beans>
MapperFactoryBean方式
mapper第二种编写方式:使用MapperFactoryBean映射器的方式
我们为啥要使用MapperFactoryBean
为了代替手工使用 SqlSessionDaoSupport 或 SqlSessionTemplate 编写数据访问对象(DAO)的代码,MyBatis-Spring 提供了一个动态代理的实现——MapperFactoryBean。这个类可以让你直接注入数据映射器接口到你的 service 层bean 中。当使用映射器时,你仅仅如调用你的 DAO 一样调用它们就可以了,但是你不需要编写任何 DAO 实现的代码,因为 MyBatis-Spring将会为你创建代理。
也就是说dao层的对数据访问对象被映射器直接注入到了service层,因此这个dao实现类就没必要写和注册了,从而简化操作,那么sqlsession也就不需要了吧。
spring-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--数据库连接池-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/learn_vue?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!--配置SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!--关联Mybatis-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<!--注入dao层接口-->
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.dongjixue.dao.UserMapper"/>
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
</beans>
目录结构
MapperScannerConfigurer方式
mapper第三种编写方式:MapperScannerConfigurer 自动扫描 将Mapper接口生成代理注入到Spring
MapperScannerConfigurer 和 MapperFactoryBean的比较
上面案例我们提到过MapperFactoryBean也能生成Mapper接口代理,但是却有一个缺点就是每一个接口都需要我们去注册一下。MapperScannerConfigurer 直接就能扫描到一个包下面的所有接口
<!--MapperFactoryBean:每一个接口都需要这样注册-->
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.dongjixue.dao.UserMapper"/>
<property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
<!-- MapperScannerConfigurer :扫描的是一个包下的所有程序 -->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.dongjixue.dao"/>
</bean>
spring-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--1.数据库连接池-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/learn_vue?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!-- spring 和 mybatis 整合 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 指定 mybatis 要连接的数据源 -->
<property name="dataSource" ref="dataSource"/>
<!--关联Mybatis-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<!-- 配置 Mapper 接口扫描 -->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.dongjixue.dao"/>
</bean>
</beans>
注意
注 意 , 没 有 必 要 去 指 定 SqlSessionFactory 或 SqlSessionTemplate , 因 为 MapperScannerConfigurer 将会创建 MapperFactoryBean,之后自动装配。