Mybatis的基础和高级应用

475 阅读13分钟

「转载」原文链接请点击

基本应用

1.快速入门

1.1.官网 :www.mybatis.org/mybatis-3/

1.2.开发步骤

1.2.1.添加Mybatis的坐标(添加pom.xml依赖)

<!--mybatis-->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.5</version>
</dependency>
<!--mysql驱动-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.6</version>
    <scope>runtime</scope>
</dependency>
<!--单元测试-->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>
<!--日志-->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.12</version>
</dependency>

1.2.2.创建user数据表

...

1.2.3.编写user实体

...

1.2.4.编写映射文件UserMapper.xml与标签说明

<!--
	<?xml ...>	映射文件DTD约束头
	<mapper>	根标签
		namespae	命名空间,与语句ID标识id组成查询的标识
	<select>	查询操作
	<update>	更新操作
	<insert>	添加操作
	<delete>	删除操作
		id				语句ID标识,与命名空间一起组织成查询标识
		resultType		查询结果对应的实体类型
		parameterType	查询对象类型
-->
<?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="UserMapper">
    <select id="find" resultType="com.otoom.pojo.User" parameterType="com.otoom.pojo.User">
        select * from User WHERE username = #{username}
    </select>
</mapper>

1.2.5.编写核心文件SqlMapConfig.xml

<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN“ "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <properties resource="jdbc.properties"/>
   	<!--环境,默认环境为:development-->
    <environments default="development">
    	<!--环境配置-->
        <environment id="development">
            <!--当前事务交给JDBC处理-->
            <transactionManager type="JDBC"/>
            <!--使用mybatis提供的连接池-->
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                    <property name="url" value="${jdbc.url}"/>
                    <property name="username" value="${jdbc.username}"/>
                    <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
    <!--引入配置文件-->
    <mappers>
        <!--使用指定文件路径
        <mapper resource="com/xxx/mapper/UserMapper.xml"/>
		-->
        
        <!--
			指定包名,会扫描这个文件夹下所有的xml
			这个xml的路径包名在resource中,需要和接口的包名一致
			如: resources目录下:com/xxx/mapper 存放xml文件
		-->
		<package name="com.xxx.mapper"/>
    </mappers>
</configuration>

1.2.6.使用

public static void main(String[] args){
    //获取配置信息
    InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml"); 
    //获取工厂类
    SqlSessionFactory sqlSessionFactory = 
        new SqlSessionFactoryBuilder().build(resourceAsStream); 
    //开启sql会话
    SqlSession sqlSession = sqlSessionFactory.openSession(); 
    //开始执行对应操作
    User query = new User();
    query.setUsername("Tom"); 
    int insert = sqlSession.insert("userMapper.find", query);
    //提交事务 
    //sqlSession.commit(); //CUD操作需要提交事务
	//关闭会话
    sqlSession.close();
}

1.2.7.sqlConfig.xml核心配置文件标签说明

environments

数据库环境的配置,支持多环境配置

environment

环境变量

transactionManager

事务管理器,有两种类型:

 		1.  JDBC:使用了JDBC的提交和回滚设置,依赖于从数据源得到的链接来管理事务作用域
 		2.  MANAGED:这个配置几乎什么都没有做,它从不提交和回滚一个链接,而是让容器去管理事务的整个生命周期。默认情况下它会关闭连接,然后有些容器不希望关闭,所以需要将 closeConnection 属性设置为false来阻止它的默认行为
dataSource

数据源,有三种类型:

	1. UNPOOLED:非连接池类型。这个数据源实现只是每次请求时打开和关闭连接

 		2. POOLED:连接池类型。这种数据源实现利用"池"的概念将JDBC连接对象组织起来
 		3. JNDI:这个数据源实现是为了能在如EJB或者应用服务器这类的容器中使用,容器可以集中或在外部配置数据源,然后放置一个JNDI上下文引用
property

属性

propertys

属性,该标签可以加载额外配置的properties文件,如resource文件夹下的jdbc.properties。使用方式为:${jdbc.driver} 可以引用对应的值

mappers

映射器,作用是加载sql的映射:

	1. 使用相对于类路径:
		
```xml
<mapper resource="com/xxx/mapper/UserDao.xml"/>
```

2. 使用完全限定资源定位符(URL):

```xml
<mapper url="file:///user/xx/project/xxx/UserDao.xml"/>
```

3. 使用映射器接口实现类的完全限定类名:

```xml
<mapper class="com.xxx.UserDao"/>
```

4. 使用包内的映射器接口实现全部注册为映射器:

xml存放的包名,需要和接口存在的报名保持一致,如resources目录下,有包 com.xxx.mapper,所有xml文件需要存放到这个目录下

```xml
<package name="com.xxx.mapper"/>
```

2.复杂映射开发

2.1.准备

  • 实体(对应数据库表)
public class User{
    private Integer id;
    private String userName;
    
    //一个用户,有多个订单(一对多)
    private List<Order> orderList;
    //用户角色(多对多)
    private List<Role> roleList;
}
public class Order{
    private Integer id;
    private Integer userId;
    private String orderTime;
    
    //一个订单,只有一个用户(一对一)
    private User user;
}
public class Role{
    private Integer id;
    private String roleName;
}
public class UserRole{
    private Integer userId
    private Integer roleid;
}
  • Mapper接口
public class OrderMapper{
    List<Order> findAll();
    //查询一对一
    List<Order> finUserAndOrder();
}
public class UserMapper{
    User findById(Integer userId);
    //查询一对多
    List<User> findAll();
    //查询多对对
    List<User> findAllUserAndRole();
}
public class RoleMapper{
    //查询多对对
    List<Role> findByUserId(Integer userId);
}

2.2.一对一查询

OrderMapper.xml编写

<mapper namespace="OrderMapper">
    
    <!--一对一映射配置-->
    <resultMap id="orderMap" type="Order">
    	<result property="id" column="id"/>
    	<result property="userId" column="userId"/>
    	<result property="orderTime" column="orderTime"/>
        
        <!--
			这里配置User对象里面的user属性的User对象与表字段的映射关系
			实现把user和order的一对一关系映射
		-->
        <association property="user" javaType="User">
            <result property="id" column="userId"/>
            <result property="username" column="username"/>
        </association>
    </resultMap>
    
    <!--
		resultMap: 手动配置实体属性与表字段的映射关系
	-->
	<select id="finUserAndOrder" parameterType="Order" resultMap="orderMap">
    	select * from order o,user u where o.userId = u.id
    </select>
</mapper>

###2.3.一对多查询

从用户角度出发,查询一对多结果;

UserMapper.xml的编写

<mapper namespace="UserMapper">
    
    <!--一对多映射配置-->
    <resultMap id="userMap" type="User">
        <result property="id" column="id"/>
        <result property="username" column="username"/>
    	
        <!--
			这里配置orderList属性的Order对象与表字段的映射关系
		-->
        <collection property="orderList" ofType="Order">
            <result property="id" column="oid"/>
            <result property="userId" column="userId"/>
            <result property="orderTime" column="orderTime"/>
        </collection>
    </resultMap>
    
    <!--
		resultMap: 手动配置实体属性与表字段的映射关系
	-->
	<select id="findAll" parameterType="User" resultMap="userMap">
    	select *,o.id oid from user u left join order o on o.userId = u.id
    </select>
</mapper>

2.4.多对多查询

用户和角色的关系,就是多对多查询;

UserMapper.xml

<mapper namespace="UserMapper">
	<!--一对多映射配置-->
    ...
    
	<!--多对多映射配置-->
    <resultMap id="userRoleMap" type="User">
        <result property="id" column="id"/>
        <result property="username" column="username"/>
        <!--
			这里配置roleList属性的Role对象与表字段的映射关系
		-->
        <collection property="roleList" ofType="Role">
            <result property="id" column="rid"/>
            <result property="roleName" column="roleName"/>
        </collection>
    </resultMap>
    
	<select id="findAllUserAndRole" parameterType="User" resultMap="userRoleMap">
    	select u.*,r.*,r.id rid from user u 
        	left join user_role ur on u.id = ur.userId
        	left join role r on r.id = ur.roleId
    </select>
    
</mapper>

2.5.测试复杂映射

public static void main(String[] args){
    //获取配置输入流
	InputStream inputStream = Resources.getResourceAsSteam("splMapConfig.xml");
	//获取sql会话
	SqlSession sqlSession = SqlSessionFactoryBuilder.build(inputStream).openSession();
	UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    //执行查询
    
    /*
    一对一
    */
    List<Order> orderList = orderMapper.finUserAndOrder();
    for(Order order : orderList){
        System.out.println(order)
    }
    
    /*
    一对多
    */
	List<User> userList = userMapper.findAll();
    for(User user : userList){
        System.out.println(user)
    }
    
    /*
    多对多
    */
	List<User> userList = userMapper.findAllUserAndRole();
    for(User user : userList){
        System.out.println(user)
    }
    
    sqlSession.close();

}

3.Mybatis注解开发

3.1.常用注解

  • @Insert 新增

  • @Update 更新

  • @Delete 删除

  • @Select 查询

  • @Result 实现结果集封装

    代替了xml中的和标签

    有以下属性:

    • column 数据库的字段名
    • property 对应对象的属性名
    • one 需要使用的@One注解 :@Result( one=@One )
    • many 需要使用@Many注解:@Result( many=@Many )
  • @Results 可以与@Result一起使用,封装多个结果集

    代替了xml中的标签,也可以使用@Result集合,使用格式:

    @Results({
    
    	@Result(),@Result()
    
    })
    
  • @One 一对一结果集封装

    代替了标签,是多表查询的关键,在注解中用来指定子查询返回单一对象

    有以下属性:

    • select 指定用来多表查询的 sqlmapper

      @Result(column="",property="",one=@One(select=""))
      
  • @Many 一对多结果集封装

    代替了标签,是多表查询的关键,再注解中用来指定子查询返回的对象集合

    • 有以下属性:

      • select 指定用来多表查询的 sqlmapper

        @Result(column="",property="",one=@Many(select=""))
        

3.2.使用注解开发

3.2.1.一对一查询

public class OrderMapper{

    //查询一对一
    @Select("select * from order")
    @Results({
        @Result(id=true,property="id",column="id"),
        @Result(property="userId",column="userId"),
        @Result(property="orderTime",column="orderTime")
        @Result(property="user",column="userId",
                javaType=User.class,
                one=@One(select="com.xxx.mapper.UserMapper.findById")
               )
    })
    List<Order> finUserAndOrder();
    
    ...
}
public class UserMapper{
    
    @Select("select * from user where id=#{userId}")
    User findById(Integer userId);
    
    ...
}

3.2.2.一对多查询

public class OrderMapper{    
	
    @Select("select * from order where userId=#{userId}")
   	List<Order> findByUserId(Integer userId);

    ...
}
public class UserMapper{
    
    //查询一对多
    @Select("select * from user")
    @Results({
        @Result(id=true,property="id",column="id"),
        @Result(id=true,property="username",column="username"),
        @Result(property="orderList",column="id",
	        	javaType=List.class,
        		many=@Many(select="com.xxx.mapper.OrderMapper.findByUserId")
    })
    List<User> findAllUserAndOrder();
	
    ...
}

3.2.3.多堆多查询

public class UserMapper{
    
    //查询多对对
    @Select("select * from user")
	@Results({
        @Result(id=true,property="id",column="id"),
        @Result(property="username",column="username"),
        @Result(property="roleList",column="id",
               javaType=List.class,
               many=@Many(select="com.xxx.mapper.RoleMapper.findByUserId"))
    })
    List<User> findAllUserAndRole();
}
public class RoleMapper{
    //查询多对对
    @Select("select * from role r,user_role ur where r.id=ur.roleId and ur.userId = #{userId}")
    List<Role> findByUserId(Integer userId);
}

4.一级缓存

  • 什么是一级缓存?

  • 为什么使用一级缓存?

Mybatis会在会话对象SqlSession对象中建议一个简单的缓存,将查询到的结果缓存起来,当下次查询的时候,会判断有没有一个完全一样的查询,如果有直接从缓存中返回。否则查询数据库并存入缓存,最后返回给用户。

这样做,是避免我们在短时间内,反复的执行相同的查询语句,而得到的结果也是相同的。这样会造成很大的资源浪费。

4.1.一级缓存的原理及源码分析

  • 底层是使用HashMap的数据结构存储

  • 缓存是在执行器Executor中创建,添加和删除缓存都在这里执行

  • 默认的缓存Key有五个部分组成(具体可以查看类:BaseExecutor):

    1. statementId:MapStatement的id

    2. offset:分页设置,默认为0,从RowBound分页对象中获取

    3. limit:分页最大页数,从RowBound分页对象中获取

    4. boundSql:查询的sql语句,从BoundSql对象中获取

    5. value:sql的参数,parameter中获取

    6. env: 当前的环境,如果sqlMapConfig.xml有配置<environment id="development"> , 也会设置到key中

    相关源码:

    CacheKey cacheKey = new CacheKey(); 
    //MappedStatement 的 id 
    // id就是Sql语句的所在位置包名+类名+ SQL名称 
    cacheKey.update(ms.getId()); 
    // offset 就是 0 
    cacheKey.update(rowBounds.getOffset());
    // limit 就是 Integer.MAXVALUE 
    cacheKey.update(rowBounds.getLimit()); 
    //具体的SQL语句
    cacheKey.update(boundSql.getSql()); 
    //后面是update 了 sql中带的参数 
    cacheKey.update(value);
    
    ...
        
    if (configuration.getEnvironment() != null) {
        // issue #176 
        cacheKey.update(configuration.getEnvironment().getId());
    }
    
    
  • BaseExecutor部分query源码:

    @Override
    public <E> List<E> query(MappedStatement ms, Object parameter,RowBounds rowBounds,ResultHandler resultHandler) throws SQLException {
        //如果查不到的话,就从数据库查,在queryFromDatabase中,会对localcache进行写入。 localcache对象的put方法最终交给Map进行存放
        BoundSql boundSql = ms.getBoundSql(parameter); 
        //创建缓存
        CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql); 
        return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
    }
    
    @SuppressWarnings("unchecked") 
    @Override
    public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException{
        
        ... 
            
        list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
        if (list != null) { 
            //这个主要是处理存储过程用的。
            handleLocallyCachedOutputParameters(ms, key, parameter, boundSql); 
        } else {
            //从数据库中查询
            list = queryFromDatabase(ms, parameter,rowBounds,resultHandler,key,boundSql);
        }
        
        ...
            
    }
    /**
     * queryFromDatabase 方法 
     */
    private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
        List<E> list;
        localCache.putObject(key, EXECUTION_PLACEHOLDER);
        try {
            //执行查询
            list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
        } finally {
            //清除缓存中的数据
            localCache.removeObject(key);
        }
        //把查询到的数据,重新放入一级缓存中
        localCache.putObject(key, list);
        if (ms.getStatementType() == StatementType.CALLABLE) {
            localOutputParameterCache.putObject(key, parameter);
        }
        return list;
    }
    

4.2.二级缓存

  • 什么是二级缓存?

  • 二级缓存的作用?

  • 二级缓存是怎么实现的?

  1. 二级缓存的原理和一级缓存原理一样,第一次查询,会将数据放入缓存中,然后第二次查询则会直接去

缓存中取

  1. 二级缓存需要手工开启,不像一级缓存一样默认就开启了。

  2. 二级缓存存储的不是对象,而是对象的数据;

  3. 对象需要实现Serializable接口,因为二级缓存的存放介质很多,可以是内存,磁盘等。所以在取数据的时候,就需要反序列化操作。

  4. 二级缓存是基于mapper文件的namespace。多个sqlSession可以共享一个mapper中的二级缓存区域

4.2.1.开启二级缓存
  • xml方式开启

    1. 在全局配置文件sqlMapConfig.xml添加以下配置:

      <!--开启二级缓存-->
      <settings>
          <setting name="cacheEnabled" value="true"/>
      </settings>
      
    2. 在Mapper.xml中添加配置:

      <!--开启二级缓存-->
      <cache></cache>
      
  • 注解开启

    1. 在Mapper类中添加注解:
    //使用二级缓存,等用于<cache>属性
    @CacheNamespace
    public interface UserMapper{
        ...
    }
    
4.2.2.用Redis实现二级缓存
  • 使用默认的二级缓存实现有哪些弊端?
  • 使用缓存中间件实现二级缓存解决了哪些问题?

使用默认的二级缓存实现,只能在单机系统中使用,不能实现分布式缓存。

引入缓存中间件redis作为二级缓存的存储介质,可以实现多机分布式缓存数据存储,对缓存数据进行集中管理

  1. 添加依赖。在pom.xml中添加以下依赖:

    <dependency>
        <groupId>org.mybatis.caches</groupId>
        <artifactId>mybatis-redis</artifactId>
        <version>1.0.0-beta2</version>
    </dependency>
    
  2. 在resources目录中添加配置 redis.properties

    redis.host=localhost
    redis.port=6379
    redis.connectionTimeout=5000
    redis.password=
    redis.database=0
    

    默认情况下,如果是本地的redis,且没有其他特殊配置,mybatis-redis已经有默认的配置,不配置redis.properties也是可以的

  3. 指定使用RedisCache作为二级缓存

    • xml方式。在mapper.xml中,添加以下配置:

      <cache type="org.mybatis.caches.redis.RedisCache" />
      
    • 注解方式。使用implementation指定使用RedisCache

      @CacheNamespace(implementation=RedisCache.class)
      public interface UserMapper{
          ...
      }
      
4.2.3.RedisCache源码分析
  • 如何实现的?

  • 如何进行缓存的存取的?

  • 用到的是什么数据结构?

  1. 实现了Mybatis的Cache接口
  2. 内部使用jdis封装了对redis的CURD操作
  3. 使用了redis的Hash数据结构,系列化后存储数据
  4. RedisCache在mybatis启动的时候,由MyBatis的CacheBuilder创建

RedisCache部分源码:

public final class RedisCache implements Cache {
    public RedisCache(final String id) {
        if (id == null) {
            throw new IllegalArgumentException("Cache instances require anID");
        }
        this.id = id;
        //调用RedisConfigurationBuilder创建RedisConfig
        RedisConfig redisConfig = RedisConfigurationBuilder.getInstance()
            //RedisConfig核心方法,这个是加载解析redis配置文件的
            .parseConfiguration();
		pool = new JedisPool(redisConfig,redisConfig.getHost(),redisConfig.getPort(),redisConfig.getConnectionTimeout(),redisConfig.getSoTimeout(), redisConfig.getPassword(), redisConfig.getDatabase(), redisConfig.getClientName());
    }
    ...
}

/**
 * 通过类加载器加载redis配置资源,
 */
public RedisConfig parseConfiguration(ClassLoader classLoader) {
    Properties config = new Properties();
    InputStream input = classLoader.getResourceAsStream(redisPropertiesFilename);
    if (input != null) {
        try {
            //加载配置信息到Properties容器中
            config.load(input);
		}catch (IOException e) {
            throw new RuntimeException( "An error occurred while reading classpath property '" + redisPropertiesFilename + "', see nested exceptions", e);
        } finally {
            try {
                input.close();
            } catch (IOException e) {
                // close quietly 
            } 
        }
    }
    //redisConfig继承了 JedisPoolConfig
    RedisConfig jedisConfig = new RedisConfig();
    //开始设置redis配置
    setConfigProperties(config, jedisConfig);
    return jedisConfig;
}

##5.Mybatis插件机制

  • 使用插件有哪些好处?

  • Mybatis有哪4大组件?

  • Mybatis的四大组件有提供给了哪些拦截方法?

使用插件的好处:

  1. 是增加了框架的灵活性

  2. 开发者可以结合实际需求,对框架进行拓展,使其能够更好的工作

5.1. Mybatis的四大组件

  • 实际就是拦截器

  • 使用了动态代理的方式实现,4大组件都是代理对象

5.1.1.Executor组件

​ 执行器Executor。提供了以下主要函数:

  • update

    • query
    • commit
    • rollback

5.1.2.StatementHandler组件

SQL语法构建器StatementHandler。提供了以下主要函数:

  • parameterize
  • batch
  • updates
  • query

5.1.3.ParameterHandler组件

参数处理器ParameterHandler 。提供了以下主要函数:

  • getParameterObject
  • setParameters

5.1.4.ResultSeetHandler组件

结果集处理器ResultSetHandler 。提供了一下主要函数:

  • handleResultSets
  • handleOutputParameters

5.2.Mybatis的插件原理

  • 通过动态代理

  • 每次四大对象创建的时候,会通过InterceptorChain.pluginAll函数返回一个代理

  • ParameterHandler的源码示例:

public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object object, BoundSql sql, InterceptorChain interceptorChain){
    ParameterHandler parameterHandler =
        mappedStatement.getLang().createParameterHandler(mappedStatement,object,sql);
    //为目标对象生成代理,之后目标对象调用方法的时候走的不是原方法而是代理方法
    parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
    return parameterHandler; 
}

public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors){
        target = interceptor.plugin(target);
    }
    return target;
}

interceptorChain保存了所有的拦截器(interceptors),是mybatis初始化的时候创建的。

调用拦截器链中的拦截器依次的对目标进行拦截或增强。

interceptor.plugin(target)中的target就可以理解为mybatis中的四大对象。返回的target是被重代理后的对象

5.3.Mybatis自定义插件的基础实现

MyBatis在启动时可以加载插件,并保存插件实例到相关对象(InterceptorChain,拦截器链) 中。

待准备工作做完后,MyBatis处于就绪状态。我们在执行SQL时,需要先通过DefaultSqlSessionFactory 创建 SqlSession。Executor 实例会在创建 SqlSession 的过程中被创建, Executor实例创建完毕后,

MyBatis会通过JDK动态代理为实例生成代理类。这样,插件逻辑即可在 Executor相关方法被调用前执行。

5.3.1.插件接口

Interceptor 接口,有以下方法:

  • intercept ,插件的核心方法
  • plugin,生成target的代理对象
  • setProperties,传递插件所需的参数

5.3.2.开始实现自定义插件

  • 代码实现

    /**
     * 实现自定义插件,需要实现Interceptor接口。
     * <p>
     * 使用@Intercepts注解,标注拦截器,并注定拦截的类和方法
     */
    
    @Intercepts(
            @Signature(
                    //指定拦截sql语法构造器StatementHandler
                    type = StatementHandler.class,
    				//拦截的方法为prepare
                	//只要执行到prepare预编译方法,都会先执行这个自定义插件的intercept方法
                    method = "prepare",
                    //参数
                    args = {Connection.class, Integer.class})
    )
    public class Myplugin implements Interceptor {
    
        /**
         * 核心拦截方法,只要被拦截的目标方法被执行时,每次都会执行intercept方法
         *
         * @param invocation
         * @return
         * @throws Throwable
         */
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
            //TODO 业务逻辑
    
            //invocation.proceed是交还给原方法执行
            return invocation.proceed();
        }
    
        /**
         * 主要是把当前的拦截器生成代理类,添加到拦截器链中
         *
         * @param target
         * @return
         */
        @Override
        public Object plugin(Object target) {
            //this表示当前拦截器,target表示目标对象,wrap方法利用mybatis封装的方法为目标对象创建代理对象(没有拦截的对象会直接返回,不会创建代理对象)
            Object wrap = Plugin.wrap(target, this);
            return wrap;
        }
    
        /**
         * 获取配置的参数
         *
         * @param properties
         */
        @Override
        public void setProperties(Properties properties) {
            //properties对应的数据就是xml配置的property
        }
    }
    
  • 在sqlMapConfig.xml中配置:

    <!--配置插件-->
    <plugins>
        <plugin interceptor="com.otoomo.plugin.Myplugin">
            <!--配置插件的参数,在插件的setProperties的方法中可以获取到-->
            <property name="name" value="tom"/>
        </plugin>
    </plugins>
    
    

    配置文件的位置需要注意,不要放错,mybatis有严格的顺序校验,放错位置

5.3.3.插件的源码分析

5.3.4.使用插件编写通用的Mapper

  • 基于Mybatis的插件机制
  • 为了解决通用的单表CURD操作,不需要编写sql
  1. 在pom.xml中引入依赖

    <dependency>
        <groupId>tk.mybatis</groupId>
        <artifactId>mapper</artifactId>
        <version>3.1.2</version>
    </dependency>
    
  2. 在sqlMapConfig.xml添加plugin配置

    <plugins>
        <!--通用Mapper插件配置-->
        <plugin interceptor="tk.mybatis.mapper.mapperhelper.MapperInterceptor">
    		<!--
    			通用Mapper接口,指定通用mapper接口是哪个
    			多个通用接口用逗号隔开
    		-->
            <property name="mappers" value="tk.mybatis.mapper.common.Mapper"/>
        </plugin>
    </plugins>
    
  3. 编写实体类

    @Table("user")
    public class User{
        //指定主键
        @Id
        //指定ID生成策略
        @GeneratedValue(strategy=GenerationType.IDENTITY)
        private Integer id;
        private String username;
    }
    
  4. 创建Mapper

    /**
     * 继承Mapper即可实现通用Mapper
     */
    public interface UserMapper extends Mapper<User>{
        
    }
    
  5. 代码演示

    @org.junit.Test
    public void testCommonMapper() {
        //插入
        Order addOrder = new Order();
        addOrder.setUserId(1);
        addOrder.setOrderTime(new Date());
        orderMapper.insert(addOrder);
        System.out.println(addOrder);
    
        //查询
        Order query = new Order();
        query.setId(1);
        Order order = orderMapper.selectOne(query);
        System.out.println(order);
    
        //自定义查询
        Example example = new Example(Order.class);
        //创建一个查询条件e
        //还有很多方式,如 andLike,andBetween ...
        example.createCriteria().andEqualTo("userId",1);
    
        List<Order> orders = orderMapper.selectByExample(example);
        for (Order order1 : orders) {
            System.out.println(order1);
        }
    }