乐趣区

关于spring:Mybatis应用分析和最佳实践2

Mybatis 利用剖析和最佳实际

​ 以下是一些 MyBatis 的高级用法或者扩大形式,帮忙咱们更好地应用 MyBatis。

为什么要动静 SQL

​ 防止因为前端传入的查问参数不同,所以导致写很多的 if else, 还须要十分留神 SQL 语句中的 and,空格,逗号和本义的单引号,拼接和调试 sql 十分耗时。

​ Mybatis 的动静 SQL 就解决了这个问题,其是基于 OGNL 表达式的。

动静标签

if

<select id="findActiveBlogWithTitleLike"
        resultType="Blog">
  SELECT * FROM BLOG
  WHERE state =‘ACTIVE’<if test="title != null">
    AND title like #{title}
  </if>
</select>

choose(when,otherwise)

<select id="findActiveBlogLike"
        resultType="Blog">
  SELECT * FROM BLOG WHERE state =‘ACTIVE’<choose>
    <when test="title != null">
      AND title like #{title}
    </when>
    <when test="author != null and author.name != null">
      AND author_name like #{author.name}
    </when>
    <otherwise>
      AND featured = 1
    </otherwise>
  </choose>
</select>

trim(where,set)

个别用来去掉前缀后者或追

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

foreach

须要遍历汇合的时候动静生成语句

<select id="selectPostIn" resultType="domain.blog.Post">
  SELECT *
  FROM POST P
  WHERE ID in
  <foreach item="item" index="index" collection="list"
      open="(" separator="," close=")">
        #{item}
  </foreach>
</select>

批量操作

​ 咱们在生产的我的项目中会有一些批量操作的场景,比方导入文件批量解决数据的状况(批量新增商户、批量批改商户信息),当数据量十分大,比方超过几万条的时候,在 Java 代码中循环发送 SQL 到数据库执行必定是不事实的,因为这个意味着要跟数据库创立几万次会话,即便咱们应用了数据库连接池技术,对于数据库服务器来说也是不堪重负的。

​ 在 MyBatis 外面是反对批量的操作的,包含批量的插入、更新、删除。咱们能够直 接传入一个 List、Set、Map 或者数组,配合动静 SQL 的标签,MyBatis 会主动帮咱们 生成语法正确的 SQL 语句。

​ 比方咱们来看两个例子,批量插入和批量更新。

批量插入

批量插入的语法是这样的,只有在 values 前面减少插入的值就能够了。

insert into tbl_emp (emp_id, emp_name, gender,email, d_id) values (?,?,?,?,?) , (?,?,?,?,?) , (?,?,?,?,?) , (?,?,?,?,?) , (?,?,?,?,?) , (?,?,?,?,?) , (?,?,?,?,?) , (?,?,?,?,?) , (?,?,?,?,?) , (?,?,?,?,?)

在 Mapper 文件外面,咱们应用 foreach 标签拼接 values 局部的语句:

<insert id="batchInsert" parameterType="java.util.List" useGeneratedKeys="true">
  <selectKey resultType="long" keyProperty="id" order="AFTER">
    SELECT LAST_INSERT_ID()
  </selectKey>
  insert into tbl_emp (emp_id, emp_name, gender,email, d_id)
  values
  <foreach collection="list" item="emps" index="index" separator=",">
    (#{emps.empId},#{emps.empName},#{emps.gender},#{emps.email},#{emps.dId} )
  </foreach>
</insert>

Java 代码外面,间接传入一个 List 类型的参数。

咱们来测试一下。效率要比循环发送 SQL 执行要高得多。最要害的中央就在于缩小了跟数据库交互的次数,并且防止了开启和完结事务的工夫耗费。

@Test
public void testBatchInsert() {List<Employee> list = new ArrayList<Employee>();
  long start = System.currentTimeMillis();
  int count = 100000;
  // max_allowed_packet 默认 4M,所以超过长度会报错
  for (int i = 0; i < count; i++) {
    String gender = i % 2 == 0 ? "M" : "F";
    Integer did = i % 2 == 0 ? 1 : 2;
    Employee emp = new Employee(null, "TestName" + i, gender, "pony@baidu.com", did);
    list.add(emp);
  }

  employeeMapper.batchInsert(list);
  long end = System.currentTimeMillis();
  System.out.println("批量插入" + count + "条,耗时:" + (end - start) + "毫秒");
}

批量更新

<!-- 批量更新 -->
<!-- 留神 separator 和 open -->
<update id="updateBatch">
    update tbl_emp set
    emp_name =
    <foreach collection="list" item="emps" index="index" separator="" open="case emp_id"close="end">
        when #{emps.empId} then #{emps.empName}
    </foreach>
    ,gender =
    <foreach collection="list" item="emps" index="index" separator="" open="case emp_id"close="end">
        when #{emps.empId} then #{emps.gender}
    </foreach>
    ,email =
    <foreach collection="list" item="emps" index="index" separator="" open="case emp_id"close="end">
        when #{emps.empId} then #{emps.email}
    </foreach>
    where emp_id in
    <foreach collection="list" item="emps" index="index" separator="," open="(" close=")">
        #{emps.empId}
    </foreach>
</update>

批量删除也是相似的

Batch Executor

​ 当然 MyBatis 的动静标签的批量操作也是存在肯定的毛病的,比方数据量特地大的 时候,拼接进去的 SQL 语句过大。

​ MySQL 的服务端对于接管的数据包有大小限度,max_allowed_packet 默认是 4M,须要批改默认配置才能够解决这个问题。

Caused by: com.mysql.jdbc.PacketTooBigException: Packet for query is too large (7188967 > 4194304). You can change this value on the server by setting the max_allowed_packet' variable.

在咱们的全局配置文件中,能够配置默认的 Executor 的类型。其中有一种 BatchExecutor。

<setting name="defaultExecutorType" value="BATCH"

也能够在创立会话的时候指定执行器类型

SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);

BatchExecutor 底层是对 JDBC ps.addBatch()的封装,原理是攒一批 SQL 当前再发

还有一个可能对你来说是新见到的参数,就是 ExecutorType。这个枚举类型定义了三个值:

  • ExecutorType.SIMPLE:这个执行器类型不做非凡的事件。它为每个语句的执行创立一个新的预处理语句。
  • ExecutorType.REUSE:这个执行器类型会复用预处理语句。
  • ExecutorType.BATCH:这个执行器会批量执行所有更新语句,如果 SELECT 在它们两头执行,必要时请把它们辨别开来以保障行为的易读性。

JDBC BatchExecutor 应用

public void testJdbcBatch() throws IOException {
  Connection conn = null;
  PreparedStatement ps = null;

  try {Long start = System.currentTimeMillis();
    // 关上连贯
    conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&rewriteBatchedStatements=true", "root", "123456");
    ps = conn.prepareStatement("INSERT into blog values (?, ?, ?)");

    for (int i = 1000; i < 101000; i++) {Blog blog = new Blog();
      ps.setInt(1, i);
      ps.setString(2, String.valueOf(i)+"");
      ps.setInt(3, 1001);
      //ExecuteType=BATCH 就是对于这个 ps 的封装,批量插入 500w 的数据,用这个性能会失去很大改善 <br>
      ps.addBatch();}

    ps.executeBatch();
    // conn.commit();
    ps.close();
    conn.close();
    Long end = System.currentTimeMillis();
    System.out.println("cost:"+(end -start) +"ms");
  } catch (SQLException se) {se.printStackTrace();
  } catch (Exception e) {e.printStackTrace();
  } finally {
    try {if (ps != null) ps.close();} catch (SQLException se2) { }
    try {if (conn != null) conn.close();} catch (SQLException se) {se.printStackTrace();
    }
  }
}
}

三种 Executor 的区别

SimpleExecutor

每执行一次 update 或 select, 就开启一个 Statement 对象, 用完立即敞开 Statement 对象。

ReuseExecutor

  • 执行 update 或 select, 以 sql 作为 key 查找 Statement 对象, 存在就应用, 不存在就创立
  • 用完后, 不敞开 Statement 对象, 而是搁置于 Map 内, 供下一次应用。
  • 简言之, 就是重复使用 Statement 对象

Batch Executor

  • 执行 update(没有 select,JDBC 批处理不反对 select)

    • 将所有 SQL 都增加到批处理中(add Batch())
    • 期待对立执行 (executebatch()), 它缓存了多个 Statement 对象, 每个 Statement 对象都是 add Batch() 结束后, 期待逐个执行 execute Batch0 批处理。与 DBC 批处理雷同。
  • executeupdate()

    • 是一个语句拜访一次数据库
  • executebatch()

    • 是一批语句访词一次数据库(具体一批发送多少条 SQL 跟服务端的 max allowed packet 无关)。
  • Batchexecutor 底层是对 JDBC

    • ps. add Batch()
    • ps. execute Batch()的封装。

嵌套(关联查问 /N+1/ 提早加载)

https://mybatis.org/mybatis-3…

​ 咱们在查问业务数据的时候常常会遇到跨表关联查问的状况,比方查问员工就会关联部门(一对一),查问问题就会关联课程(一对一),查问订单就会关联商品(一对多),等等。

咱们映射后果有两个标签,一个是 resultType,一个是 resultMap。

​ resultType 是 select 标签的一个属性,实用于返回 JDK 类型 (比方 Integer、String 等等) 和实体类。这种状况下后果集的列和实体类的属性能够间接映射。如果返回的字

段无奈间接映射,就要用 resultMap 来建设映射关系。对于关联查问的这种状况,通常不能用 resultType 来映射。用 resultMap 映射,要么就是批改 dto(Data Transfer Object),在外面减少字段,这个会导致减少很多无关的字段。要么就是援用关联的对象,比方 Blog 外面蕴含了一个 Author 对象,这种状况 下就要用到关联查问(association,或者嵌套查问),MyBatis 能够帮咱们主动做后果 的映射。

一对一的关联查问有两种配置形式:

嵌套后果

<!-- 依据文章查问作者,一对一查问的后果,嵌套后果 -->
<resultMap id="BlogWithAuthorResultMap" type="com.zzjson.domain.associate.BlogAndAuthor">
  <id column="bid" property="bid" jdbcType="INTEGER"/>
  <result column="name" property="name" jdbcType="VARCHAR"/>
  <!-- 联结查问,将 author 的属性映射到 ResultMap -->
  <association property="author" javaType="com.zzjson.domain.Author">
    <id column="author_id" property="authorId"/>
    <result column="author_name" property="authorName"/>
  </association>
</resultMap>

<!-- 依据文章查问作者,一对一,嵌套后果,无 N + 1 问题 -->
<select id="selectBlogWithAuthorResult" resultMap="BlogWithAuthorResultMap">
  select b.bid, b.name, b.author_id, a.author_id, a.author_name
  from blog b
  left join author a
  on b.author_id = a.author_id
  where b.bid = #{bid, jdbcType=INTEGER}
</select>

嵌套查问 N+ 1 问题

<!-- 另一种联结查问 (一对一) 的实现,然而这种形式有“N+1”的问题 -->
<resultMap id="BlogWithAuthorQueryMap" type="com.zzjson.domain.associate.BlogAndAuthor">
  <id column="bid" property="bid" jdbcType="INTEGER"/>
  <result column="name" property="name" jdbcType="VARCHAR"/>
  <association property="author" javaType="com.zzjson.domain.Author"
               column="author_id" select="selectAuthor"/> <!-- selectAuthor 定义在上面 -->
</resultMap>

<!-- 嵌套查问 -->
<select id="selectAuthor" parameterType="int" resultType="com.zzjson.domain.Author">
  select author_id authorId, author_name authorName
  from author
  where author_id = #{authorId}
</select>

​ 是分两次查问,当咱们查问了员工信息之后,会再次发送一条 SQL 到数据库查问部门信息。

​ 咱们只执行了一次查问员工信息的 SQL(所谓的 1),如果返回了 N 条记录,就会再发送 N 条到数据库查问部门信息(所谓的 N), 这就是咱们说的 N + 1 的问题,这样会白白的节约咱们的利用和数据库的性能。

懒加载

​ 如果咱们应用了嵌套查问的形式,怎么解决这个问题?

​ 能不能等到应用部门信息的时候再去查问?这就是咱们所说的提早加载,或者叫懒加载

​ 在 Mybatis 外面能够通过开启提早加载的开关来解决这个问题。

setting 配置 + 代理

​ 在 setting 标签外面能够配置

<!-- 提早加载的全局开关。当开启时,所有关联对象都会提早加载。默认 false -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 当开启时,任何办法的调用都会加载该对象的所有属性。默认 false,可通过 select 标签的 fetchType 来笼罩 -->
<setting name="aggressiveLazyLoading" value="false"/>
<!-- Mybatis 创立具备提早加载能力的对象所用到的代理工具,默认 JAVASSIST-->
<setting name ="proxyFactory" value="CGLIB"/>

lazyLoadingEnabled 决定了是否提早加载。

aggressiveLazyLoading 决定了是不是对象的所有办法都会触发查问。

先来测试一下(也能够改成查问列表):

1、没有开启提早加载的开关,会间断发送两次查问;

2、开启了提早加载的开关,调用 blog.getAuthor()以及 默认的 (equals,clone,hashCode,toString) 时才会发动第二次查问,其余办法并不会触发查问,比方 blog.getName();

3、如果开启了 aggressiveLazyLoading=true,其余办法也会触发查问,比方blog.getName()

问题: 为什么能够做到提早加载?

blog.getAuthor(),只是一个获取属性的办法,外面并没有连贯数据库的代码,为什么会触发对数据库的查问呢?

是因为咱们这个类被代理了

System.out.println(blog.getClass());

打印进去果然不对

class com.zzjson.domain.associate.BlogAndAuthor_$$_jvst70_0

这个类的名字前面有 jvst,是 JAVASSIST 的缩写

​ 当开启了提早加载的开关,对象是怎么变成代理对象的?

DefaultResultSetHandler.createResultObject()

​ 既然是代理对象,那么必须要有一种创立代理对象的办法。咱们有哪些实现动静代 理的形式?

​ 这个就是为什么 settings 外面提供了一个 ProxyFactory 属性。MyBatis 默认应用 JAVASSIST 创立代理对象。也能够改为 CGLIB,这时须要引入 CGLIB 的包。

CGLIB 和 JAVASSIST 区别是什么?

测试一下,咱们把默认的 JAVASSIST 批改为 CGLIB,再打印这个对象。

分页

RowBounds

public void testSelectByRowBounds() throws IOException {SqlSession session = sqlSessionFactory.openSession();
  try {BlogMapper mapper = session.getMapper(BlogMapper.class);
    int start = 0; // offset
    int pageSize = 5; // limit
    RowBounds rb = new RowBounds(start, pageSize);
    List<Blog> list = mapper.selectBlogList(rb); // 应用逻辑分页
    for(Blog b :list){System.out.println(b);
    }
  } finally {session.close();
  }
}

参数传入 RowBounds

  • 是一个伪的分页,实际上会先查问所有,而后获取多少条

org.apache.ibatis.executor.resultset.DefaultResultSetHandler#handleRowValuesForSimpleResultMap

手动 limit

<select id="selectBlogPage" parameterType="map" resultMap="BaseResultMap">
  select * from blog limit #{curIndex} , #{pageSize}
</select>

须要在 java 代码计算序号

PageHelper

https://github.com/pagehelper…

  • 利用插件

    • ThreadLocal 来设置

依赖

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>x.x.x</version>
</dependency>

插件配置

<plugins>
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
        <!-- config params as the following -->
        <property name="param1" value="value1"/>
    </plugin>
</plugins>

应用

静态方法调用

// 获取第 1 页,10 条内容,默认查问总数 count
PageHelper.startPage(1, 10);

PageInfo

// 获取第 1 页,10 条内容,默认查问总数 count
PageHelper.startPage(1, 10);
List<User> list = userMapper.selectAll();
// 用 PageInfo 对后果进行包装
PageInfo page = new PageInfo(list);

参数形式

<plugins>
    <!-- com.github.pagehelper 为 PageHelper 类所在包名 -->
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
        <!-- 应用上面的形式配置参数,前面会有所有的参数介绍 -->
        <property name="supportMethodsArguments" value="true"/>
        <property name="params" value="pageNum=pageNumKey;pageSize=pageSizeKey;"/>
    </plugin>
</plugins>
List<User> selectByPageNumSize(@Param("user") User user,
        @Param("pageNumKey") int pageNum, 
        @Param("pageSizeKey") int pageSize);

MybatisGenerator

https://github.com/mybatis/ge…

​ 咱们在我的项目中应用 MyBaits 的时候,针对须要操作的一张表,须要 创立实体类、Mapper 映射器、Mapper 接口 ,外面又有很多的字段和办法的配置,这部分的工作是 十分繁琐的。而大部分时候咱们对于表的操作是雷同的,比方依据主键查问、依据 Map 查问、单条插入、批量插入、依据主键删除等等等等。当咱们的表很多的时候,意味着 有大量的反复工作。所以有没有一种方法,能够依据咱们的表,主动生成 实体类、Mapper 映射器、Mapper 接口,外面蕴含了咱们须要用到的这些根本办法和 SQL 呢?

​ MyBatis 也提供了一个这样的货色,叫做 MyBatis Generator,简称 MBG。咱们只须要批改一个配置文件,应用相干的 jar 包命令或者 Java 代码就能够帮忙咱们生成实体类、映射器和接口文件。不晓得用 MyBatis 的同学有没有跟当年的我一样,还是实体类的一个一个字段,接口的一个一个办法,映射器的一条一条 SQL 去写的。

​ MBG 的配置文件外面有一个 Example 的开关,这个货色用来结构简单的筛选条件的,换句话说就是依据咱们的代码去生成 where 条件

​ 原理: 在实体类中蕴含了两个有继承关系的 Criteria,用其中主动生成的办法来构建查问条件。把这个蕴含了 Criteria 的实体类作为参数传到查问参数中,在解析 Mapper 映射器的时候会转换成 SQL 条件。

(mybatis-standalone 工程:

com.zzjson.domain.BlogExample

com.zzjson.BlogExampleTest)

BlogExample 外面蕴含了一个两个 Criteria:

实例: 查问 bid=1 的 Blog,通过创立一个 Criteria 去构建查问条件:

BlogMapper mapper = session.getMapper(BlogMapper.class);
BlogExample example = new BlogExample();
BlogExample.Criteria criteria = example.createCriteria();
criteria.andBidEqualTo(1);
List<Blog> list = mapper.selectByExample(example);

生成的语句

select 'true' as QUERYID, bid, name, author_id from blog WHERE (bid = ?)

翻页

​ 在写存储过程的年代,翻页也是一件很难调试的事件,咱们要实现数据不多不少精确地返回,须要大量的调试和批改。然而如果本人手写过分页,就能分明分页的原理。

​ 在咱们查询数据库的操作中,有两种翻页形式,一种是逻辑翻页(假分页),一种是物理翻页(真分页)。逻辑翻页的原理是把所有数据查出来,在内存中删选数据。物理翻页是真正的翻页,比方 MySQL 应用 limit 语句,Oracle 应用 rownum 语句,SQLServer 应用 top 语句。

逻辑翻页

MyBatis 外面有一个逻辑分页对象 RowBounds,外面次要有两个属性,offset 和 limit(从第几条开始,查问多少条)。

咱们能够在 Mapper 接口的办法上加上这个参数,不须要批改 xml 外面的 SQL 语句。

public List<Blog> selectBlogList(RowBounds rowBounds);

应用:mybatis-standalone- MyBatisTest-testSelectByRowBounds()

int start = 10; // offset,从第几行开始查问 
int pageSize = 5; // limit,查问多少条 
RowBounds rb = new RowBounds(start, pageSize); 
List<Blog> list = mapper.selectBlogList(rb); for(Blog b :list){System.out.println(b);
}

​ 它的底层其实是对 ResultSet 的解决。它会舍弃掉后面 offset 条数据,而后再取剩下的数据的 limit 条。

// DefaultResultSetHandler.java
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {DefaultResultContext<Object> resultContext = new DefaultResultContext();
  ResultSet resultSet = rsw.getResultSet();
  this.skipRows(resultSet, rowBounds);
  while(this.shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
    ResultMap discriminatedResultMap = this.resolveDiscriminatedResultMap(resultSet,
                                                                          resultMap, (String)null);
    Object rowValue = this.getRowValue(rsw, discriminatedResultMap, (String)null);
    this.storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet); }
}

​ 很显著,如果数据量大的话,这种翻页形式效率会很低 (跟查问到内存中再应用 subList(start,end) 没什么区别)。所以咱们要用到物理翻页。

物理翻页

物理翻页是真正的翻页,它是通过数据库反对的语句来翻页

第一种简略的方法就是传入参数(或者包装一个 page 对象),在 SQL 语句中翻页。

<select id="selectBlogPage" parameterType="map" resultMap="BaseResultMap">
  select * from blog limit #{curIndex} , #{pageSize}
</select>

​ 第一个问题是咱们要在 Java 代码外面去计算起止序号; 第二个问题是: 每个须要翻页的 Statement 都要编写 limit 语句,会造成 Mapper 映射器外面很多代码冗余。

那咱们就须要一种通用的形式,不须要去批改配置的任何一条 SQL 语句,只有在我 们须要翻页的中央封装一下翻页对象就能够了。

​ 咱们最罕用的做法就是应用翻页的插件,这个是基于 MyBatis 的拦截器实现的,比方 PageHelper。

// pageSize 每一页几条
PageHelper.startPage(pn, 10);
List<Employee> emps = employeeService.getAll(); // navigatePages 导航页码数
PageInfo page = new PageInfo(emps, 10);
return Msg.success().add("pageInfo", page);

​ PageHelper 是通过 MyBatis 的拦截器实现的,插件的具体原理咱们前面再剖析。简略地来说,它会依据 PageHelper 的参数,改写咱们的 SQL 语句。比方 MySQL 会生成 limit 语句,Oracle 会生成 rownum 语句,SQL Server 会生成 top 语句。

通用 Mapper

​ 问题: 当咱们的表字段发生变化的时候,咱们须要批改实体类和 Mapper 文件定义的字段和办法。如果是增量保护,那么一个个文件去批改。如果是全量替换,咱们还要去比照用 MBG 生成的文件。字段变动一次就要批改一次,保护起来十分麻烦。

​ 解决这个问题,咱们有两种思路。

​ 第一个,因为 MyBatis 的 Mapper 是反对继承的(见 https://github.com/mybatis/my…)。所 以 我 们 可 以 把 我 们 的 Mapper.xml 和 Mapper 接口都分成两个文件。一个是 MBG 生成的,这部分是固定不变的。而后创立 DAO 类继承生成的接口,变动的局部就在 DAO 外面保护。

mybatis-standalone 工程:

public interface BlogMapperExt extends BlogMapper {public Blog selectBlogByName(String name);
}
<?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="com.zzjson.mapper.BlogMapperExt">
  <!-- 只能继承 statement,不能继承 sql、resultMap 等标签 -->
  <resultMap id="BaseResultMap" type="blog">
    <id column="bid" property="bid" jdbcType="INTEGER"/>
    <result column="name" property="name" jdbcType="VARCHAR"/>
    <result column="author_id" property="authorId" jdbcType="INTEGER"/>
  </resultMap>

  <!-- 在 parent xml 和 child xml 的 statement id 雷同的状况下,会应用 child xml 的 statement id -->
  <select id="selectBlogByName" resultMap="BaseResultMap" statementType="PREPARED">
    select *
    from blog
    where name = #{name}
  </select>
</mapper>

所以当前只有批改 Ext 的文件就能够了。

这么做有一个毛病,就是文件会增多。

​ 既然针对每张表生成的根本办法都是一样的,也就是公共的办法局部代码都是一样的,咱们能不能把这部分合并成一个文件,让它反对泛型呢? 编写一个反对泛型的通用接口,比方叫 GPBaseMapper<T>,把实体类作为参数传 入。这个接口外面定义了大量的增删改查的根底办法,这些办法都是反对泛型的。自定义的 Mapper 接口继承该通用接口,例如 BlogMapper extends GPBaseMapper<Blog>,主动取得对实体类的操作方法。遇到没有的办法,咱们仍然 能够在咱们本人的 Mapper 外面编写。咱们能想到的解决方案,早就有人做了这个事了,这个货色就叫做

通用 Mapper。https://github.com/abel533/Ma…

​ 用处: 次要解决单表的增删改查问题,并不适用于多表关联查问的场景。

除了配置文件变动的问题之外,通用 Mapper 还能够解决:

  1. 每个 Mapper 接口中大量的反复办法的定义;
  2. 屏蔽数据库的差别;
  3. 提供批量操作的办法;
  4. 实现分页。

通用 Mapper 和 PageHelper 作者是同一个人(刘增辉)。

应用形式: 在 Spring 中应用时,引入 jar 包,替换 applicationContext.xml 中的
sqlSessionFactory 和 configure。

<bean class="tk.mybatis.spring.mapper.MapperScannerConfigurer">
  <property name="backPackage" value="com.zzjson.crud.dao"/>
</bean

Mybatis-Plus

https://mybatis.plus/guide

MyBatis-Plus 是原生 MyBatis 的一个加强工具,能够在应用原生 MyBatis 的所有 性能的根底上,应用 plus 特有的性能。

MyBatis-Plus 的外围性能:

通用 CRUD:

​ 定义好 Mapper 接口后,只须要继承 BaseMapper<T> 接口即可取得通用的增删改查性能,无需编写任何接口办法与配置文件。条件结构器: 通过 EntityWrapper<T>(实体包装类),能够用于拼接 SQL 语句,并且反对排序、分组查问等简单的 SQL。代码生成器: 反对一系列的策略配置与全局配置,比 MyBatis 的代码生成更好用。
另外 MyBatis-Plus 也有分页的性能。

我的笔记仓库地址 gitee 快来给我点个 Star 吧

退出移动版