2

Mybatis新手进阶知识点,老鸟请走开

 2 years ago
source link: https://segmentfault.com/a/1190000040647422
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Mybatis新手进阶知识点,老鸟请走开

ORM全称:object relation mapping,译为:对象关系映射。
ORM框架是将对象和数据库表字段建立映射,并提供CRUD操作的API的框架。

Java原生的与数据库连接的方式是JDBC,每次操作需要以下6个步骤

  1. 加载数据库驱动
  2. 创建一个Statement
  3. 执行SQL
  4. 处理结果集

原生的方式步骤繁琐,开发效率低,市面上有很多的优秀的ORM框架:

  1. Hibernate 全自动ORM框架,弱化sql, 甚至不需要考虑建表,Hibernate会根据对象生成表甚至中间表。CURD一般不需要写sql。用起来方便,用好很难,有些老的项目还在用。
  2. Mybatis半自动ORM框架,本文的主角,被广泛使用,它支持自定义 SQL、存储过程以及高级映射。前身是ibatis, 另外还有一个在此基础上封装的号称为简化开发而生的框架MyBatis-Plus。提供通用的CURD,一般操作不需要写sql。
  3. JPA是大spring旗下ORM框架,特点是根据方法名就能自动实现方法逻辑,你敢信?不信可以看看这篇文章《简单才是美! SpringBoot+JPA

下面将介绍一些mybatis新手进阶知识点,老鸟请走开🤦‍♂️

resultMap中嵌套一个查询。通过标签<association>select属性完成。select的值是另一个<select>查询的id,column属性为关联字段,用来实现关联查询。

  1. 根据user_id查询user

     <select id="getUserWithAddress2" resultMap="BaseResultWithAddress2Map">
          select * from `user` where user_id = #{userId}
     </select>
  2. <association>中嵌套一个id为selectAddressByUserId的查询,查询这个用户的地址。

     <resultMap id="BaseResultWithAddress2Map" type="com.mashu.springmybatis.entity.UserWithAddress">
         <id column="user_id" property="userId" jdbcType="INTEGER"/>
         <result column="name" property="name" jdbcType="VARCHAR"/>
         <result column="email" property="email" jdbcType="VARCHAR"/>
         <association property="address" column="user_id" javaType="com.mashu.springmybatis.entity.Address"
                      select="selectAddressByUserId">
         </association>
     </resultMap>
  3. id为selectAddressByUserId的查询:根据用户id查询地址详情:

     <select id="selectAddressByUserId"
             resultMap="com.mashu.springmybatis.mapper.AddressMapper.BaseResultMap">
             select * from address where user_id = #{userId}
     </select>

上面的查询会有N+1的问题,就是执行两遍查询,可以使用联表查询解决这个问题,结果集同样是使用<resultMap>映射,<association>标签+resultMap属性。具体写法如下:

  1. association标签的resultMap属性指向address的resultMap

    <resultMap id="BaseResultWithAddressMap" type="com.mashu.springmybatis.entity.UserWithAddress">
     <id column="user_id" property="userId" jdbcType="INTEGER"/>
     <result column="name" property="name" jdbcType="VARCHAR"/>
     <result column="email" property="email" jdbcType="VARCHAR"/>
     <association property="address" javaType="com.mashu.springmybatis.entity.Address"
                      resultMap="com.mashu.springmybatis.mapper.AddressMapper.BaseResultMap">
     </association>
    </resultMap>
  2. 联表查询sql

     <select id="getUserWithAddress" resultMap="BaseResultWithAddressMap">
         select * from `user` u join address a on u.user_id and a.user_id where u.user_id = #{userId}
     </select>

还可以一对多的映射,将<association>换成<collection>,实现一个人有多个女朋友的一对多关联查询。
myabtis会自动合并重复的user,girlFriends作为集合映射到user的girlFriends属性。

    <resultMap id="BaseResultWithGirlFriendsMap" type="com.mashu.springmybatis.entity.UserWithGirlFriends">
        <id column="user_id" property="userId" jdbcType="INTEGER"/>
        <result column="name" property="name" jdbcType="VARCHAR"/>
        <result column="email" property="email" jdbcType="VARCHAR"/>
        <collection property="girlFriends" ofType="com.mashu.springmybatis.entity.GirlFriend">
            <id column="girl_friend_id" property="girlFriendId" jdbcType="INTEGER"/>
            <result column="user_id" property="userId" jdbcType="VARCHAR"/>
            <result column="girl_friend_name" property="name" jdbcType="VARCHAR"/>
            <result column="age" property="age" jdbcType="INTEGER"/>
            <result column="weight" property="weight" jdbcType="INTEGER"/>
            <result column="height" property="height" jdbcType="INTEGER"/>
        </collection>
    </resultMap>

除了联表查询解决N+1的问题,mybatis的懒加载似乎更好,拿第一个嵌套查询的栗子来说,如果开启了懒加载,
在不使用address的时候,只会执行查询user的sql,不会执行查询address的sql。
只有在使用中get了address属性才会执行查询address的sql,使用起来也很见简单:

  1. yml配置

    mybatis:
      mapper-locations: classpath:mapper/*Mapper.xml
      configuration:
     ##开启懒加载
     lazy-loading-enabled: true
     ##false:按需加载
     aggressive-lazy-loading: false
     ##触发加载的方法名
     lazy-load-trigger-methods:
  2. <association>加上fetchType="lazy"的属性即可。

     <resultMap id="BaseResultWithAddress2Map" type="com.mashu.springmybatis.entity.UserWithAddress">
         <id column="user_id" property="userId" jdbcType="INTEGER"/>
         <result column="name" property="name" jdbcType="VARCHAR"/>
         <result column="email" property="email" jdbcType="VARCHAR"/>
         <association fetchType="lazy" property="address" column="user_id" javaType="com.mashu.springmybatis.entity.Address"
                      select="selectAddressByUserId">
         </association>
     </resultMap>

但是,实现懒加载的问题比较多😢:

  1. 如果报错No serializer found for class org.apache.ibatis.executor.loader.javassist.JavassistProxyFactory..
    序列化问题需要在实体类上添加注解@JsonIgnoreProperties(value = {"handler"})
  2. 如果懒加载失败:检查是否是lombok中的@Data注解的toString()导致的
  3. 检查全局配置是否正确
  4. 还有在idea失败,在eclipce成功的。。。

一二级缓存

一级缓存,一次请求查询两次数据,第二次从缓存中取,mybatis默认开启
二级缓存,多次请求查询同一个数据,都能从缓存中取,需要手动开启

  1. 开启全局配置:

    mybatis:
      mapper-locations: classpath:mapper/*Mapper.xml
     ##开启二级缓存
     cache-enabled: true
  2. 添加useCache="true"属性。

     <select id="selectByCache" useCache="true" resultMap="BaseResultMap" parameterType="java.lang.Integer">
     select user_id, name, email
     from user
     where user_id = #{userId,jdbcType=INTEGER}
      </select>

类型处理器

有时候我们在入库和出库的时候对字段做一些处理,
比如不支持utf8mb4的数据库存储emoji表情之前需要转义成utf8支持的unicode字符编码,出库后需要转化成emoji表情。
又或者用户的密码不能明文保存到数据库,入库需要进行一些加密操作。
mybatis的类型处理器,就可以在入库和出库前对数据做一些操作。
下面举个栗子将邮箱入库Base64加密,出库Base64解密。

  1. 自定义类型处理器类继承BaseTypeHandler抽象类。

    public class MyHandler extends BaseTypeHandler<String> {
     
     //入库加密
     @Override
     public void setNonNullParameter(PreparedStatement preparedStatement, int i, String s, JdbcType jdbcType) throws SQLException {
         preparedStatement.setString(i,Base64Utils.encodeToString(s.getBytes()));
     }
     //出库解密
     @Override
     public String getNullableResult(ResultSet resultSet, String columnName) throws SQLException {
         String column = resultSet.getString(columnName);
         return new String(Base64Utils.decode(column.getBytes()));
     }
    
     @Override
     public String getNullableResult(ResultSet resultSet, int i) throws SQLException {
         System.out.println(resultSet);
         return null;
     }
    
     @Override
     public String getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
         System.out.println(callableStatement);
         return null;
     }
    }
  2. 字段添加typeHandler属性,并指向自定义类型处理器类的路径

     <resultMap id="BaseResultMap" type="com.mashu.springmybatis.entity.User">
         <id column="user_id" property="userId" jdbcType="INTEGER"/>
         <result column="name" property="name" jdbcType="VARCHAR"/>
         <result column="email" property="email" jdbcType="VARCHAR" typeHandler="com.mashu.springmybatis.config.MyHandler"/>
     </resultMap>

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK