关于mybatis:Mybatis新手进阶知识点老鸟请走开

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

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

  1. 加载数据库驱动
  2. 创立连贯
  3. 创立一个Statement
  4. 执行SQL
  5. 处理结果集
  6. 敞开连贯

原生的形式步骤繁琐,开发效率低,市面上有很多的优良的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>

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理