一、配置 MyBatis(XML 形式) src/main/resources/mybatis-config.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 <?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 > <properties resource ="jdbc.properties" > </properties > <settings > <setting name ="logImpl" value ="LOG4J" /> </settings > <typeAliases > <package name ="tk.mybatis.simple.model" /> </typeAliases > <environments default ="development" > <environment id ="development" > <transactionManager type ="JDBC" > <property name ="" value ="" /> </transactionManager > <dataSource type ="UNPOOLED" > <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 ="tk/mybatis/simple/mapper/CountryMapper.xml" /> </mappers > </configuration >
二、具体语句 1. Insert insert 返回主键
1.1. 只适用于支持主键自增的数据库 1 2 3 4 5 6 <insert id ="insert2" useGeneratedKeys ="true" keyProperty ="id" > insert into sys_user( user_name, user_password) values( #{userName}, #{userPassword}) </insert >
1.2. 使用 selectKey 返回主键的值 1 2 3 4 5 6 7 8 9 <insert id ="insert2" useGeneratedKeys ="true" keyProperty ="id" > insert into sys_user( user_name, user_password) values( #{userName}, #{userPassword}) <selectKey keyColumn ="id" resultType ="long" keyProperty ="id" order ="AFTER" > SELECT LAST_INSERT_ID() </selectKey > </insert >
Oracle:
1 2 3 <selectKey keyColumn ="id" result ="long" keyProperty ="id" order ="BEFORE" > SELECT SEQ_ID.nextval from dual </selectKey >
2. select 1 2 3 4 5 6 <select id ="selectAll" resultType ="tk.mybatis.simple.model.SysUser" > select id, user_name userName, user_password userPassword from sys_user </select >
使用 resultType 需要设置别名来实现自动映射
三、注解方式 1. @Select 注解 1.1. 通过别名自动映射 1 2 3 4 5 6 7 8 9 @Select({"select id, role_name roleName ", "from sys_role ", "where id = #{id}"}) SysRole selectById (Long id) ; @Select({"select id, role_name roleName from sys_role where id = #{id}"}) SysRole selectById (Long id) ;
1.2. 使用 resultMap 方式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Results(id = "roleResultMap", value = { @Result(property = "id", column = "id", id = true), @Result(property = "roleName", column = "role_name") }) @Results({ @Result(property = "id", column = "id", id = true), @Result(property = "roleName", column = "role_name") }) @Select("select id, role_name from sys_role where id = #{id}") SysRole selectById2 (Long id) ; @ResultMap("roleResultMap") @Select("select * from sys_role") List<SysRole> selectAll () ;
2.1. 返回自增主键 1 2 3 4 @Insert({"insert into sys_role(role_name)", "values(#{roleName})"}) @Options(useGeneratedKeys = true, keyProperty = "id") int insert2 (SysRole sysRole) ;
2.2. 返回非自增主键 1 2 3 4 5 6 7 @Insert({"insert into sys_role(role_name)", "values(#{roleName})"}) @SelectKey(statement = "SELECT LAST_INSERT_ID()", keyProperty = "id", resultType = Long.class, before = false) int insert3 (SysRole sysRole) ;
3. Provider 注解 1 2 @SelectProvider(type = PrivilegeProvider.class, metod = "selectById") SysPrivilege selectById (Long id) ;
PrivilegeProvider:
1 2 3 4 5 6 7 8 9 10 11 12 public class PrivilegeProvider { public String selectById (final Long id) { result new SQL () { { SELECT("id, privilege_name, privilege_url" ); FROM("sys_privilege" ); WHERE("id = #{id}" ); } }.toString(); } }
四、动态 SQL 1. if 用法 1 2 3 <if test ="userName != null and userName != ''" > and user_name like concat('%', #{userName}, '%') </if >
2. choose 用法 1 2 3 4 5 6 <choose > <when test ="userName != null" > ... </when > <otherwhise > </otherwhise > </choose >
3. where set trim 用法 3.1. where 用法 1 2 3 4 5 <where > <if test ="userName != null and userName != ''" > and user_name like concat('%', #{userName}, '%') </if > </where >
3.2. set 用法 1 2 3 4 5 6 7 8 9 10 11 12 <update id ="updateByIdSelective" > update sys_user <set > <if test ="userName != null and userName != ''" > user_name = #{userName}, </if > <if test ="userPassword != null and userPassword != ''" > user_password = #{userPassword}, </if > </set > where id = #{id} </update >
3.3. trim 用法 where 标签对应 trim 的实现:
1 2 3 <trim prefix ="WHERE" prefixOverrides ="AND |OR" > ... </trim >
set 标签对应的 trim 实现:
1 2 3 <trim prefix ="SET" suffixOverrides ="," > ... </trim >
prefix: 给内容增加 prefix 指定的前缀
prefixOverrides: 把内容中匹配的前缀字符串去掉
suffix: 给内容增加 suffix 指定的后缀
suffixOverrides: 把内容中匹配的后缀字符串去掉
4. foreach 用法 1 2 3 4 where id in <foreach collection ="list" open ="(" separator ="," close =")" item ="id" index ="i" > #{id} </foreach >
5. bind 用法 1 2 3 4 <if test ="userName != null and userName != ''" > <bind name ="userNameLike" value ="'%' + userName + '%'" /> and user_name like #{userNameLike} </if >
五、高级结果映射 1. 一对一映射 1.1. 使用自动映射 1 2 3 4 5 6 7 8 <select id ="selectUserAndRoleById" resultType ="tk.mybatis.simple.model.SysUser" > select u.id, u.user_name as userName, u.user_password as userPassword, r.id as "role.id", r.role_name as "role.roleName" from sys_user u inner join sys_user_role ur on u.id = ur.user_id inner join sys_role r on ur.role_id = r.id where u.id = #{id} </select >
1.2. 使用 resultMap 配置一对一映射 1 2 3 4 5 6 7 8 <resultMap id ="userRoleMap" type ="tk.mybatis.simple.model.SysUser" > <result property ="id" column ="id" /> <result property ="userName" column ="user_name" /> <result property ="userPassword" column ="user_password" /> <result property ="role.id" column ="role_id" /> <result property ="role.roleName" column ="role_name" /> </resultMap >
1.3. 使用 resultMap 的 association 标签配置一对一映射 1 2 3 4 5 6 <resultMap id ="userRoleMap" type ="tk.mybatis.simple.model.SysUser" > <association property ="role" columnPrefix ="role_" javaType ="tk.mybatis.simple.model.SysRole" > <result property ="id" column ="id" /> <result property ="roleName" column ="role_name" /> </association > </resultMap >
2. 一对多映射 1 2 3 4 5 6 7 8 9 <resultMap id ="userRoleListMap" extends ="userMap" type ="tk.mybatis.simple.model.SysUser" > <id property ="id" column ="id" /> <result property ="userName" column ="user_name" /> <result property ="userPassword" column ="user_password" /> <collection property ="roleList" columnPrefix ="role_" javaType ="tk.mybatis.simple.model.SysRole" > <id property ="id" column ="id" /> <result property ="roleName" column ="role_name" /> </collection > </resultMap >
3. 返回 Map 3.1. map 的 value 为 java 类 mapper.java
1 2 @MapKey("id") Map<Long, UserInfo> getUserInfoMap () ;
mapper.xml
1 2 3 4 5 6 7 8 <resultMap id ="UserResultMap" type ="com.xixicat.domain.UserInfo" > <result property ="id" column ="id" /> <result property ="username" column ="username" /> <result property ="sex" column ="sex" /> </resultMap > <select id ="getUserInfoMap" resultMap ="UserResultMap" > select id,username,sex from user_info </select >
3.2. map 的 value 为 map mapper.java
1 2 @MapKey("id") Map<Long, Map<String,Object>> getUserValueMap () ;
mapper.xml
1 2 3 4 <select id ="getUserValueMap" resultType ="map" > select id,username,sex from user_info from user_info </select >
3.3. 返回 List<Map<key, value>> mapper.java
1 List<Map<String, Object>> countInquireForMobile () ;
mapper.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <select id ="countInquireForMobile" resultType ="map" > select o.status as `status`, count(mc.id) as `count` from mall_contract mc join mall_order o on o.contract_no = mc.contract_no where o.archive = false and o.flag = 0 and o.status in ('SUBMITTED', 'PAID', 'SHIPPED', 'SUCCESS') and o.refund_status = 'CREATE' <if test ="params.opUserType == 'BUYER'" > and o.buyer_id = #{params.operatorId,typeHandler=idHandler} </if > <if test ="params.opUserType == 'SELLER'" > and o.seller_id = #{params.operatorId,typeHandler=idHandler} </if > group by o.status </select >
4. 鉴别器映射 类似于 switch
1 2 3 4 5 6 <resultMap id ="rolePrivilegeListMapChoose" type ="tk.mybatis.simple.model.SysRole" > <discriminator column ="enabled" javaType ="int" > <case value ="1" resultMap ="rolePrivilegeListMapSelect" /> <case value ="0" resultMap ="roleMap" /> </discriminator > </resultMap >
5. 枚举处理器 在 mybatis-config.xml 中添加配置:使用枚举的索引进行处理
1 2 3 4 <typeHandlers > <typeHandler javaType ="tk.mybatis.simple.type.Enabled" handler ="org.apache.ibatis.type.EnumOrdinalTypeHandler" /> </typeHandlers >
6. 自定义处理器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public class EnabledTypeHandler implements TypeHanlder <Enabled> { private final Map<Integer, Enabled> enabledMap = new HashMap <Integer, Enabled>(); public EnabledTypeHandler () { for (Enabled enabled : Enabled.values()) { enabledMap.put(enabled.getValue(), enabled); } } @Override public void setParameter (PreparedStatement ps, int i, Enabled parameter, JdbcType jdbcType) throws SQLException { ps.setInt(i, parameter.getValue()); } @Override public Enabled getResult (ResultSet rs, int columnIndex) throws SQLException { Integer value = rs.getInt(columnIndex); return enabledMap.get(value); } @Override public Enabled getResult (CallableStatement cs, int columnIndex) throws SQLException { Integer value = cs.getInt(columnIndex); return enabledMap.get(value); } }
在 mybatis-config.xml 中配置:
1 2 3 4 <typeHandlers > <typeHandler javaType ="tk.mybatis.simple.type.Enabled" handler ="tk.mybatis.simple.type.EnabledTypeHandler" /> </typeHandlers >
六、缓存 1. 一级缓存 范围: SqlSession
概述: MyBatis 的一级缓存存在于 SqlSession 的生命周期中,默认开启 在同一个 SqlSession 中查询时,MyBatis 会把执行的方法和参数通过算法生成缓存的键值,将键值和查询结果存入一个 Map 对象中
清空缓存: 更新或删除操作会清空缓存,或使用 SqlSession#clearCache () 方法进行手动刷新
flushCache=”true”: 查询数据前清空当前的一级缓存
1 2 3 <select id ="selectById" flushCache ="true" resultMap ="userMap" > select * from sys_user where id = #{id} </select >
2. 二级缓存 范围: Mapper(namespace)
概述: 默认不开启 会重新创建一个对象进行 copy 无法实现分布式缓存
2.1. 在 mapper.xml 中配置 二级缓存全局开关: 不必配置,默认已为 true mybatis-config.xml:
1 2 3 4 <settings > <setting name ="cacheEnabled" value ="true" /> </settings >
开启二级缓存:
1 2 3 <mapper namespace ="tk.mybatis.simple.mapper.RoleMapper" > <cache /> </mapper >
默认的二级缓存会有如下效果
所有的 select 语句将会被缓存
所有的 insert、update、delete 语句将会刷新缓存
缓存会使用 LRU 算法来回收
根据时间表(刷新间隔),缓存不会以任何时间顺序刷新
缓存会存储集合或对象 1024 个引用
缓存会被视为 可读可写 的,所以返回的对象不是共享的,可以安全的修改
cache 可配置的属性:
1 <cache eviction ="FIFO" flushInterval ="60000" size ="512" readOnly ="true" />
eviction:收回策略
LRU(最近最少使用):移除最长时间不被使用的对象,默认
FIFO(先进先出):按对象进入缓存的顺序来移除它们
SOFT(软引用):移除基于垃圾回收器状态和弱引用规则的对象
WEAK(弱引用):更积极地移除基于垃圾收集器状态和弱引用规则的对象
flushInterval:刷新间隔。毫秒,默认不设置(没有刷新间隔,缓存仅在调用 sql 时刷新)。
size:引用数目。默认 1024
readOnly:只读。只读的缓存会给所有调用者返回缓存对象的相同实例,不能修改。默认 false
2.2. 在 Mapper 接口中配置二级缓存 当只使用注解方式配置二级缓存时:
1 2 3 4 @CacheNamespace public interface RoleMapper { ... }
配置属性:
1 2 3 4 5 6 @CacheNamespace( eviction = FifoCache.class, flushInterval = 60000, size = 512, readWrite = true )
当同时使用注解方式和 XML 映射文件时: 使用参照缓存 这样就会使用命名空间为 tk.mybatis.simple.mapperRoleMapper 的缓存配置,即 RoleMapper.xml 中配置的缓存
引用 Mapp 接口中配置的二级缓存
1 <cache-ref namespace ="tk.mybatis.simple.mapper.RoleMapper" />
2.3. 使用二级缓存 使用可读写缓存,通过序列化和反序列化来保证通过缓存获取数据时,得到的是一个 新的实例 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 sqlSession = getSqlSession(); try { RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class); SysRole role2 = roleMapper.selectById(1L ); Assert.assertEquals("New Name" , role2.getRoleName()); Assert.assertNotEquals(role1, role2); SysRole role3 = roleMapper.selectById(1L ); Assert.assertNotEquals(role2, role3); } finally { sqlSession.close(); }
2.4. 二级缓存适用场景
以查询为主的应用中,只有尽可能少的 curd
绝大多数以单表操作存在时,由于很少存在互相关联的情况,因此不会出现脏数据
分布式场景不能使用,需要借助第三方缓存中间件
2.5. 使用 redis 实现二级缓存
加入依赖
1 2 3 4 5 <dependency > <groupId > org.mybatis.caches</groupId > <artifactId > mybatis-redis</artifactId > <version > 1.0.0-beta2</version > </dependency >
使用缓存
1 2 3 4 @CacheNamespace(implementation = RedisCache.class) public interface IUserMapper { ... }
七、插件 1. 概述 对 mybatis 的扩展,其原理是拦截器,在四大组件(Executor
、StatementHandler
、ParamterHandler
、ResultSetHandler
)使用时拦截处理
允许拦截的方法:
Executor(执行器):update、query、commit、rollback
StatementHandler(SQL 语句构建器):prepare、parameterize、batch、update、query
ParameterHandler(参数处理器):getParameterObject、setParameters
ResultSetHandler(结果集处理器):handleResultSets、handleOutputParameters
原理: 在四大组件对象创建时遍历所有的拦截器,每个拦截器都使用 interceptor.plugin (target) 方法对目标类进行增强
1 2 3 4 5 6 7 8 9 10 11 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; }
2. 自定义插件
实现 Interceptor 并添加注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 @Intercepts({ @Signature( // 指定组件 type = Executor.class, // 指定方法 method = "query", // 指定参数确定方法 args = {MapperStatement.class, Object.class, RowBounds.class, ResultHandler.class} ) }) public class ExamplePlugin implements Interceptor { @Override public Object intercept (Invocation invocation) throws Throwable { System.out.println("对方法进行了增强...." ); return invocation.proceed(); } @Override public Object plugin (Object target) { System.out.println("将要包装的目标对象" + target); return Plugin.wrap(target, this ); } @Override public void setProperties (Properties properties) { System.out.println("初始化参数" + properties); } }
配置到配置文件中
1 2 3 4 5 <plugins > <plugin interceptor ="com.xxx.plugin.ExamplePlugin" > <property name ="name" value ="Bob" /> </plugin > </plugins >