Mybatis 从入门到入土

一、配置 MyBatis(XML 形式)

src/main/resources/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>
    <!--加载外部的properties文件,使用 ${jdbc.driver} 等形式注入-->
    <properties resource="jdbc.properties"></properties>
    
    <settings>
        <setting name="logImpl" value="LOG4J" />
    </settings>
    
    <typeAliases>
        <!--给单独的实体起别名-->
        <!--  <typeAlias type="com.lagou.pojo.User" alias="user"></typeAlias>-->
        <!--批量起别名:该包下所有的类的本身的类名:别名还不区分大小写-->
        <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}"/>
                <!--<property name="driver" value="com.mysql.jdbc.Driver"/>-->
                <!--<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>-->
                <!--<property name="username" value="root"/>-->
                <!--<property name="password" value=""/>-->
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!-- <package name="tk.mybatis.simple.mapper"/> -->
        <mapper resource="tk/mybatis/simple/mapper/CountryMapper.xml"/>
    </mappers>
</configuration>

二、具体语句

1. Insert

insert 返回主键

1.1. 只适用于支持主键自增的数据库

<insert id="insert2" useGeneratedKeys="true" keyProperty="id">
    insert into sys_user(
        user_name, user_password)
    values(
        #{userName}, #{userPassword})
</insert>

1.2. 使用 selectKey 返回主键的值

<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:

<selectKey keyColumn="id" result="long" keyProperty="id" order="BEFORE">
    SELECT SEQ_ID.nextval from dual
</selectKey>

2. select

<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. 通过别名自动映射

@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 方式

// 定义 id,实现共用
@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. @insert 注解

2.1. 返回自增主键

@Insert({"insert into sys_role(role_name)",
         "values(#{roleName})"})
@Options(useGeneratedKeys = true, keyProperty = "id")
int insert2(SysRole sysRole); 

2.2. 返回非自增主键

@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 注解

@SelectProvider(type = PrivilegeProvider.class, metod = "selectById")
SysPrivilege selectById(Long id);

PrivilegeProvider:

public class PrivilegeProvider {
    // 直接返回字符串 sql 也行
    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 用法

<if test="userName != null and userName != ''">
    and user_name like concat('%', #{userName}, '%')
</if>

2. choose 用法

<choose>
    <when test="userName != null">
    ...
    </when>
    <otherwhise></otherwhise>
</choose>

3. where set trim 用法

3.1. where 用法

<where>
    <if test="userName != null and userName != ''">
        and user_name like concat('%', #{userName}, '%')
    </if>
</where>

3.2. set 用法

<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 的实现:

<trim prefix="WHERE" prefixOverrides="AND |OR">
    ...
</trim>

set 标签对应的 trim 实现:

<trim prefix="SET" suffixOverrides=",">
    ...
</trim>
  • prefix: 给内容增加 prefix 指定的前缀
  • prefixOverrides: 把内容中匹配的前缀字符串去掉
  • suffix: 给内容增加 suffix 指定的后缀
  • suffixOverrides: 把内容中匹配的后缀字符串去掉

4. foreach 用法

where id in
<foreach collection="list" open="(" separator="," close=")" item="id" index="i">
    #{id}
</foreach>

5. bind 用法

<if test="userName != null and userName != ''">
    <bind name="userNameLike" value="'%' + userName + '%'"/>
    and user_name like #{userNameLike}
</if>

五、高级结果映射

1. 一对一映射

1.1. 使用自动映射

<select id="selectUserAndRoleById" resultType="tk.mybatis.simple.model.SysUser">
    select u.id, u.user_name userName, u.user_password userPassword, 
           r.id "role.id", r.role_name "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 配置一对一映射

<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 标签配置一对一映射

<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. 一对多映射

<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. 鉴别器映射

类似于 switch

<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>

4. 枚举处理器

在 mybatis-config.xml 中添加配置:使用枚举的索引进行处理

<typeHandlers>
    <typeHandler javaType="tk.mybatis.simple.type.Enabled" 
                 handler="org.apache.ibatis.type.EnumOrdinalTypeHandler"/>
</typeHandlers>

5. 自定义处理器

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 中配置:

<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”: 查询数据前清空当前的一级缓存

<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:

<settings>
    <!-- 其他配置 -->
    <setting name="cacheEnabled" value="true"/>
</settings>

开启二级缓存:

<mapper namespace="tk.mybatis.simple.mapper.RoleMapper">
    <cache/>
</mapper>

默认的二级缓存会有如下效果

  • 所有的 select 语句将会被缓存
  • 所有的 insert、update、delete 语句将会刷新缓存
  • 缓存会使用 LRU 算法来回收
  • 根据时间表(刷新间隔),缓存不会以任何时间顺序刷新
  • 缓存会存储集合或对象 1024 个引用
  • 缓存会被视为 可读可写 的,所以返回的对象不是共享的,可以安全的修改

cache 可配置的属性:

<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
  • eviction:收回策略
  • LRU(最近最少使用):移除最长时间不被使用的对象,默认
  • FIFO(先进先出):按对象进入缓存的顺序来移除它们
  • SOFT(软引用):移除基于垃圾回收器状态和弱引用规则的对象
  • WEAK(弱引用):更积极地移除基于垃圾收集器状态和弱引用规则的对象
  • flushInterval:刷新间隔。毫秒,默认不设置(没有刷新间隔,缓存仅在调用 sql 时刷新)。
  • size:引用数目。默认 1024
  • readOnly:只读。只读的缓存会给所有调用者返回缓存对象的相同实例,不能修改。默认 false

2.2. 在 Mapper 接口中配置二级缓存

当只使用注解方式配置二级缓存时:

@CacheNamespace
public interface RoleMapper {
    ...
}

配置属性:

@CacheNamespace(
    eviction = FifoCache.class,
    flushInterval = 60000,
    size = 512,
    readWrite = true
)

当同时使用注解方式和 XML 映射文件时: 使用参照缓存
这样就会使用命名空间为 tk.mybatis.simple.mapperRoleMapper 的缓存配置,即 RoleMapper.xml 中配置的缓存

引用 Mapp 接口中配置的二级缓存

<cache-ref namespace="tk.mybatis.simple.mapper.RoleMapper"/>

2.3. 使用二级缓存

使用可读写缓存,通过序列化和反序列化来保证通过缓存获取数据时,得到的是一个 新的实例

sqlSession = getSqlSession();
try {
    // 获取 RoleMapper 接口
    RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
    // 调用 selectById 方法,查询 id = 1 的用户
    SysRole role2 = roleMapper.selectById(1L);
    // 第二个 session 获取的用户名是 New Nam
    Assert.assertEquals("New Name", role2.getRoleName());
    // 这里的 role2 和前一个 session 查询的结果是两个不同的实例
    Assert.assertNotEquals(role1, role2);
    // 获取 role3
    SysRole role3 = roleMapper.selectById(1L);
    // 这里的 role2 和 role3 是两个不同的实例
    Assert.assertNotEquals(role2, role3);
} finally {
    // 关闭 sqlSession
    sqlSession.close();
}

2.4. 二级缓存适用场景

  • 以查询为主的应用中,只有尽可能少的 curd
  • 绝大多数以单表操作存在时,由于很少存在互相关联的情况,因此不会出现脏数据
  • 分布式场景不能使用,需要借助第三方缓存中间件

2.5. 使用 redis 实现二级缓存

  1. 加入依赖

     <dependency>
         <groupId>org.mybatis.caches</groupId>
         <artifactId>mybatis-redis</artifactId>
         <version>1.0.0-beta2</version>
     </dependency>
    
  2. 使用缓存

    @CacheNamespace(implementation = RedisCache.class)
    public interface IUserMapper {
        ...
    }
    

七、插件

1. 概述

对 mybatis 的扩展,其原理是拦截器,在四大组件(ExecutorStatementHandlerParamterHandlerResultSetHandler)使用时拦截处理

clipboard.png

允许拦截的方法:

  • Executor(执行器):update、query、commit、rollback
  • StatementHandler(SQL 语句构建器):prepare、parameterize、batch、update、query
  • ParameterHandler(参数处理器):getParameterObject、setParameters
  • ResultSetHandler(结果集处理器):handleResultSets、handleOutputParameters

原理:
在四大组件对象创建时遍历所有的拦截器,每个拦截器都使用 interceptor.plugin(target) 方法对目标类进行增强

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. 自定义插件

  1. 实现 Interceptor 并添加注解
    @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);
        }
        
        // 插件初始化时调用一次,获取配置的属性,如下:<name, Bob>
        @Override
        public void setProperties(Properties properties) { 
            System.out.println("初始化参数" + properties);
        }
    }
    
  2. 配置到配置文件中
    <plugins> 
        <plugin interceptor="com.xxx.plugin.ExamplePlugin">
            <property name="name" value="Bob"/>
        </plugin>
    </plugins>
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值