MyBatis最佳实践

4 篇文章 1 订阅
1 篇文章 0 订阅

一、MyBatis最佳实践

1.动态SQL语句

动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦

使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少

  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach

1.1 if

需要判断的时候,条件写在test中

<select id="selectListIf" parameterType="user" resultMap="BaseResultMap" >
select
<include refid="baseSQL"></include>
from t_user
<where>
<if test="id != null">
and id = #{id}
</if>
<if test="userName != null">
and user_name = #{userName}
</if>
</where>
</select>

1.2 choose

需要选择一个条件的时候

<!-- choose 的使用 -->
<select id="selectListChoose" parameterType="user" resultMap="BaseResultMap"
>
select
<include refid="baseSQL"></include>
from t_user
<where>
<choose>
<when test="id != null">
id = #{id}
</when>
<when test="userName != null and userName != ''">
and user_name like CONCAT(CONCAT('%',#
{userName,jdbcType=VARCHAR}),'%')
</when>
<otherwise>
</otherwise>
</choose>
</where>
</select>

1.3 trim

需要去掉where、and、逗号之类的符号的时候

<!--
trim 的使用
替代where标签的使用
-->
<select id="selectListTrim" resultMap="BaseResultMap"
parameterType="user">
select <include refid="baseSQL"></include>
<!-- <where>
<if test="username!=null">
and name = #{username}
</if>
</where> -->
<trim prefix="where" prefixOverrides="AND |OR ">
<if test="userName!=null">
and user_name = #{userName}
</if>
<if test="age != 0">
and age = #{age}
</if>
</trim>
</select>
<!-- 替代set标签的使用 -->
<update id="updateUser" parameterType="User">
update t_user
<trim prefix="set" suffixOverrides=",">
<if test="userName!=null">
user_name = #{userName},
</if>
<if test="age != 0">
age = #{age}
</if>
</trim>
where id=#{id}
</update>

1.4 foreach

需要遍历集合的时候

<delete id="deleteByList" parameterType="java.util.List">
delete from t_user
where id in
<foreach collection="list" item="item" open="(" separator="," close=")">
#{item.id,jdbcType=INTEGER}
</foreach>
</delete>

动态SQL主要是用来解决SQL语句生成的问题。

2.批量操作

我们在项目中会有一些批量操作的场景,比如导入文件批量处理数据的情况(批量新增商户、批量修改商户信息),当数据量非常大,比如超过几万条的时候,在Java代码中循环发送SQL到数据库执行肯定是不现实的,因为这个意味着要跟数据库创建几万次会话。即使在同一个连接中,也有重复编译和执行SQL的开销。

例如循环插入10000条(大约耗时3秒钟):

public class Test03Batch {
public SqlSession session;
@Before
public void init() throws IOException {
// 1.获取配置文件
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
// 2.加载解析配置文件并获取SqlSessionFactory对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
// 3.根据SqlSessionFactory对象获取SqlSession对象
session = factory.openSession();
}
/**
* 循环插入10000
*/
@Test
public void test1(){
long start = System.currentTimeMillis();
UserMapper mapper = session.getMapper(UserMapper.class);
int count = 12000;
for (int i=2000; i< count; i++) {
User user = new User();
user.setUserName("a"+i);
mapper.insertUser(user);
}
session.commit();
session.close();
long end = System.currentTimeMillis();
System.out.println("循环批量插入"+count+"条,耗时:" + (end -start )+"毫
秒");
}
}

在MyBatis里面是支持批量的操作的,包括批量的插入、更新、删除。我们可以直接传入一个List、Set、Map或者数组,配合动态SQL的标签,MyBatis会自动帮我们生成语法正确的SQL语句。

2.1 批量插入

批量插入的语法是这样的,只要在values后面增加插入的值就可以了。

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

在Mapper文件里面,我们使用foreach标签拼接 values部分的语句:

<!-- 批量插入 -->
<insert id="insertUserList" parameterType="java.util.List" >
insert into t_user(user_name,real_name)
values
<foreach collection="list" item="user" separator=",">
(#{user.userName},#{user.realName})
</foreach>
</insert>

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

/**
* 批量插入
*/
@Test
public void test2(){
long start = System.currentTimeMillis();
UserMapper mapper = session.getMapper(UserMapper.class);
int count = 12000;
List<User> list = new ArrayList<>();
for (int i=2000; i< count; i++) {
User user = new User();
user.setUserName("a"+i);
list.add(user);
}
mapper.insertUserList(list);
session.commit();
session.close();
long end = System.currentTimeMillis();
System.out.println("循环批量插入"+count+"条,耗时:" + (end -start )+"毫秒");
}

插入一万条大约耗时1秒钟。

可以看到,动态SQL批量插入效率要比循环发送SQL执行要高得多。最关键的地方就在于减少了跟数据库交互的次数,并且避免了开启和结束事务的时间消耗

2.2 批量更新

批量更新的语法是这样的,通过case when,来匹配id相关的字段值

update t_user set
user_name =
case id
when ? then ?
when ? then ?
when ? then ? end ,
real_name =
case id
when ? then ?
when ? then ?
when ? then ? end
where id in ( ? , ? , ? )

所以在Mapper文件里面最关键的就是case when和where的配置
需要注意一下open属性和separator属性。

<update id="updateUserList">
update t_user set
user_name =
<foreach collection="list" item="user" index="index" separator=" "
open="case id" close="end">
when #{user.id} then #{user.userName}
</foreach>
,real_name =
<foreach collection="list" item="user" index="index" separator=" "
open="case id" close="end">
when #{user.id} then #{user.realName}
</foreach>
where id in
<foreach collection="list" item="item" open="(" separator=","
close=")">
#{item.id,jdbcType=INTEGER}
</foreach>
</update>

java代码实现

/**
* 批量更新
*/
@Test
public void test3(){
long start = System.currentTimeMillis();
UserMapper mapper = session.getMapper(UserMapper.class);
int count = 12000;
List<User> list = new ArrayList<>();
for (int i=2000; i< count; i++) {
User user = new User();
user.setId(i);
user.setUserName("a"+i);
list.add(user);
}
mapper.updateUserList(list);
session.commit();
session.close();
long end = System.currentTimeMillis();
System.out.println("批量更新"+count+"条,耗时:" + (end -start )+"毫秒");
}

2.3 批量删除

批量删除也是类似的。

<delete id="deleteByList" parameterType="java.util.List">
delete from t_user where id in
<foreach collection="list" item="item" open="(" separator="," close=")">
#{item.id,jdbcType=INTEGER}
</foreach>
</delete>

2.4 BatchExecutor

当然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的类型(默认是SIMPLE)。其中有一种BatchExecutor

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

也可以在创建会话的时候指定执行器类型

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

Executor
在这里插入图片描述

  1. SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。
  2. ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map内,供下一次使用。简言之,就是重复使用Statement对象。
  3. BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。与JDBC批处理相同。executeUpdate()是一个语句访问一次数据库,executeBatch()是一批语句访问一次数据库(具体一批发送多少条SQL跟服务端的max_allowed_packet有关)。BatchExecutor底层是对
    JDBC ps.addBatch()和ps. executeBatch()的封装。
@Test
public void testJdbcBatch() throws IOException {
Connection conn = null;
PreparedStatement ps = null;
try {
conn =
DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatisdb?
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);
ps.addBatch();
}
ps.executeBatch();
ps.close();
conn.close();
} 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();
}
}
}

3.关联查询

3.1 嵌套查询

我们在查询业务数据的时候经常会遇到关联查询的情况,比如查询员工就会关联部门(一对一),查询学生成绩就会关联课程(一对一),查询订单就会关联商品(一对多),等等。
用户和部门的对应关系是1对1的关系

<!-- 嵌套查询 1对1 1个用户对应一个部门-->
<resultMap id="nestedMap1" type="user">
<id property="id" column="id" jdbcType="INTEGER"/>
<result property="userName" column="user_name" jdbcType="VARCHAR" />
<result property="realName" column="real_name" jdbcType="VARCHAR" />
<result property="password" column="password" jdbcType="VARCHAR"/>
<result property="age" column="age" jdbcType="INTEGER"/>
<result property="dId" column="d_id" jdbcType="INTEGER"/>
<association property="dept" javaType="dept">
<id column="did" property="dId"/>
<result column="d_name" property="dName"/>
<result column="d_desc" property="dDesc"/>
</association>
</resultMap>
<select id="queryUserNested" resultMap="nestedMap1">
SELECT
t1.`id`
,t1.`user_name`
,t1.`real_name`
,t1.`password`
,t1.`age`
,t2.`did`
,t2.`d_name`
,t2.`d_desc`
FROM t_user t1
LEFT JOIN
t_department t2
ON t1.`d_id` = t2.`did`
</select>

在这里插入图片描述
还有就是1对多的关联关系,嵌套查询

<!-- 嵌套查询 1对多 1个部门有多个用户-->
<resultMap id="nestedMap2" type="dept">
<id column="did" property="dId"/>
<result column="d_name" property="dName"/>
<result column="d_desc" property="dDesc"/>
<collection property="users" ofType="user">
<id property="id" column="id" jdbcType="INTEGER"/>
<result property="userName" column="user_name" jdbcType="VARCHAR" />
<result property="realName" column="real_name" jdbcType="VARCHAR" />
<result property="password" column="password" jdbcType="VARCHAR"/>
<result property="age" column="age" jdbcType="INTEGER"/>
<result property="dId" column="d_id" jdbcType="INTEGER"/>
</collection>
</resultMap>
<select id="queryDeptNested" resultMap="nestedMap2">
SELECT
t1.`id`
,t1.`user_name`
,t1.`real_name`
,t1.`password`
,t1.`age`
,t2.`did`
,t2.`d_name`
,t2.`d_desc`
FROM t_user t1
RIGHT JOIN
t_department t2
ON t1.`d_id` = t2.`did`
</select>

在这里插入图片描述

3.2 延迟加载

在MyBatis里面可以通过开启延迟加载的开关来解决这个问题。
在settings标签里面可以配置:

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

azyLoadingEnabled决定了是否延迟加载(默认false)。
aggressiveLazyLoading决定了是不是对象的所有方法都会触发查询。

1对1的延迟加载配置

<!-- 延迟加载 1对1 -->
<resultMap id="nestedMap1Lazy" type="user">
<id property="id" column="id" jdbcType="INTEGER"/>
<result property="userName" column="user_name" jdbcType="VARCHAR" />
<result property="realName" column="real_name" jdbcType="VARCHAR" />
<result property="password" column="password" jdbcType="VARCHAR"/>
<result property="age" column="age" jdbcType="INTEGER"/>
<result property="dId" column="d_id" jdbcType="INTEGER"/>
<association property="dept" javaType="dept" column="d_id"
select="queryDeptByUserIdLazy">
</association>
</resultMap>
<resultMap id="baseDept" type="dept">
<id column="did" property="dId"/>
<result column="d_name" property="dName"/>
<result column="d_desc" property="dDesc"/>
</resultMap>
<select id="queryUserNestedLazy" resultMap="nestedMap1Lazy">
SELECT
t1.`id`
,t1.`user_name`
,t1.`real_name`
,t1.`password`
,t1.`age`
,t1.d_id
FROM t_user t1
</select>
<select id="queryDeptByUserIdLazy" parameterType="int" resultMap="baseDept">
select * from t_department where did = #{did}
</select>

注意:开启了延迟加载的开关,调用user.getDept()以及默认的(equals,clone,hashCode,toString)时才会发起第二次查询,其他方法并不会触发查询,比如blog.getName();

/**
* 1对1 关联查询 延迟加载
* @throws Exception
*/
@Test
public void test03() throws Exception{
init();
UserMapper mapper = session.getMapper(UserMapper.class);
List<User> users = mapper.queryUserNestedLazy();
for (User user : users) {
System.out.println(user.getUserName() );
//System.out.println(user.getUserName() + "---->"+user.getDept());
}
}

触发延迟加载的方法可以通过配置,默认
equals(),clone(),hashCode(),toString()。

1对多的延迟加载的配置

<!-- 1对多 延迟加载 -->
<resultMap id="nestedMap2Lazy" type="dept">
<id column="did" property="dId"/>
<result column="d_name" property="dName"/>
<result column="d_desc" property="dDesc"/>
<collection property="users" ofType="user" column="did"
select="queryUserByDeptLazy">
</collection>
</resultMap>
<select id="queryDeptNestedLazy" resultMap="nestedMap2">
SELECT
,t2.`did`
,t2.`d_name`
,t2.`d_desc`
FROM
t_department t2
</select>
<select id="queryUserByDeptLazy" resultMap="BaseResultMap" >
select * from t_user where d_id = #{did}
</select>

4.分页操作

4.1 逻辑分页

MyBatis里面有一个逻辑分页对象RowBounds,里面主要有两个属性,offset和limit(从第几条开始,查询多少条)。我们可以在Mapper接口的方法上加上这个参数,不需要修改xml里面的SQL语句。
接口中定义

public List queryUserList(RowBounds rowBounds);

测试类

@Test
public void test01() throws Exception{
init();
UserMapper mapper = session.getMapper(UserMapper.class);
// 设置分页的数据
RowBounds rowBounds = new RowBounds(1,3);
List<User> users = mapper.queryUserList(rowBounds);
for (User user : users) {
System.out.println(user);
}
}

RowBounds的工作原理其实是对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)没什么区别)。所以我们要用到物理翻页

4.2 物理分页

物理翻页是真正的翻页,它是通过数据库支持的语句来翻页。
第一种简单的办法就是传入参数(或者包装一个page对象),在SQL语句中翻页。

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

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

那我们就需要一种通用的方式,不需要去修改配置的任何一条SQL语句,我们只要传入当前是第几页,每页多少条就可以了,自动计算出来起止序号。

我们最常用的做法就是使用翻页的插件,比如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语句

5.MBG与Example

MyBatis generator
我们在项目中使用MyBaits的时候,针对需要操作的一张表,需要创建实体类、Mapper映射器、Mapper接口,里面又有很多的字段和方法的配置,这部分的工作是非常繁琐的。

而大部分时候我们对于表的基本操作是相同的,比如根据主键查询、根据Map查询、单条插入、批量插入、根据主键删除等等。

当我们的表很多的时候,意味着有大量的重复工作。所以有没有一种办法,可以根据我们的表,自动生成实体类、Mapper映射器、Mapper接口,里面包含了我们需要用到的这些基本方法和SQL呢?

MyBatis也提供了一个代码生成器,叫做MyBatis Generator,简称MBG(它是MyBatis的一个插件)。我们只需要修改一个配置文件,使用相关的jar包命令或者Java代码就可以帮助我们生成实体类、映射器和接口文件。

MBG的配置文件里面有一个Example的开关,这个东西用来构造复杂的筛选条件的,换句话说就是根据我们的代码去生成where条件。
原理:在实体类中包含了两个有继承关系的Criteria,用其中自动生成的方法来构建查询条件。把这个包含了Criteria的实体类作为参数传到查询参数中,在解析Mapper映射器的时候会转换成SQL条件。

5.1 添加配置文件

我们添加如下的配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<!-- 数据库的驱动包路径 -->
<classPathEntry location="C:\Users\dpb\.m2\repository\mysql\mysql-connectorjava\8.0.11\mysql-connector-java-8.0.11.jar" />
<context id="DB2Tables" targetRuntime="MyBatis3">
<!-- 去掉生成文件中的注释 -->
<commentGenerator>
<property name="suppressAllComments" value="true" />
</commentGenerator>
<!-- 数据库链接URL、用户名、密码 -->
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/mybatisdb?
characterEncoding=utf-8&amp;serverTimezone=UTC"
userId="root"
password="123456">
<property name="nullCatalogMeansCurrent" value="true" />
</jdbcConnection>
<!-- <jdbcConnection driverClass="oracle.jdbc.driver.OracleDriver"
connectionURL="jdbc:oracle:thin:@localhost:1521:XE"
userId="car"
password="car">
</jdbcConnection> -->
<javaTypeResolver >
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- 生成模型的包名和位置 -->
<javaModelGenerator targetPackage="com.gupaoedu.vip.domain"
targetProject="./src/main/java">
<!-- 是否在当前路径下新加一层schema,eg:fase路径com.oop.eksp.user.model,
true:com.oop.eksp.user.model.[schemaName] -->
<property name="enableSubPackages" value="false" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- 生成的映射文件包名和位置 -->
<sqlMapGenerator targetPackage="com.gupaoedu.vip.mapper"
targetProject="./src/main/java">
<property name="enableSubPackages" value="false" />
</sqlMapGenerator>
<!-- 生成DAO的包名和位置 -->
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.gupaoedu.vip.mapper" targetProject="./src/main/java">
<property name="enableSubPackages" value="false" />
</javaClientGenerator>
<table tableName="t_user" domainObjectName="User" />
</context>
</generatorConfiguration>

5.2 添加插件

我们需要在pom.xml中添加对应的插件

<build>
<plugins>
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.2</version>
<configuration>
<!-- 指定配置文件的位置 -->
<configurationFile>src/main/resources/generatorConfig.xml</configurationFile>
</configuration>
</plugin>
</plugins>
</build>

5.3 生成

然后我们就可以利用插件帮助我们快速生成我们需要的表结构对应的相关文件
在这里插入图片描述

6. 通用Mapper

问题:当我们的表字段发生变化的时候,我们需要修改实体类和Mapper文件定义的字段和方法。如果是增量维护,那么一个个文件去修改。如果是全量替换,我们还要去对比用MBG生成的文件。字段变动一次就要修改一次,维护起来非常麻烦。

6.1 方式一

第一个,因为MyBatis的Mapper是支持继承的(见:MyBatis Mapper继承)。所以我们可以把我们的Mapper.xml和Mapper接口都分成两个文件。一个是MBG生成的,这部分是固定不变的。然后创建DAO类继承生成的接口,变化的部分就在DAO里面维护

public interface UserMapperExt extends UserMapper {
public List<User> selectUserByName(String userName);
}

对应的映射文件

<?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.gupaoedu.vip.mapper.UserMapperExt" >
<resultMap id="BaseResultMapExt" type="com.gupaoedu.vip.domain.User" >
<id column="id" property="id" jdbcType="INTEGER" />
<result column="user_name" property="userName" jdbcType="VARCHAR" />
<result column="real_name" property="realName" jdbcType="VARCHAR" />
<result column="password" property="password" jdbcType="VARCHAR" />
<result column="age" property="age" jdbcType="INTEGER" />
<result column="d_id" property="dId" jdbcType="INTEGER" />
<result column="i_id" property="iId" jdbcType="INTEGER" />
</resultMap>
<select id="selectUserByName" resultMap="BaseResultMapExt" >
select * from t_user where user_name = #{userName}
</select>
</mapper>

在全局配置文件中我们也需要扫描

<mappers>
<mapper resource="mapper/UserMapper.xml"/>
<mapper resource="mapper/UserMapperExt.xml"/>
</mappers>

所以以后只要修改Ext的文件就可以了。这么做有一个缺点,就是文件会增多。
在这里插入图片描述

6.2 方式二

既然针对每张表生成的基本方法都是一样的,也就是公共的方法部分代码都是一样的,我们能不能把这部分合并成一个文件,让它支持泛型呢

当然可以!

编写一个支持泛型的通用接口,比如叫GPBaseMapper,把实体类作为参数传入。这个接口里面定义了大量的增删改查的基础方法,这些方法都是支持泛型的。

自定义的Mapper接口继承该通用接口,例如BlogMapper extends GPBaseMapper,自动获得对实体类的操作方法。遇到没有的方法,我们依然可以在我们自己的Mapper里面编写。

我们能想到的解决方案,早就有人做了这个事了,这个东西就叫做通用Mapper

MyBatis 通用 Mapper4

用途:主要解决单表的增删改查问题,并不适用于多表关联查询的场景。
除了配置文件变动的问题之外,通用Mapper还可以解决:

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

使用方式:在Spring中使用时,引入jar包,替换applicationContext.xml中的sqlSessionFactoryconfigure

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

7.MyBatis-Plus

MuBatis-Plus

MyBatis-Plus是原生MyBatis的一个增强工具,可以在使用原生MyBatis的所有功能的基础上,使用plus特有的功能。

MyBatis-Plus的核心功能:

通用 CRUD:定义好Mapper接口后,只需要继承BaseMapper 接口即可获得通用的增删改查功能,无需编写任何接口方法与配置文件。

条件构造器:通过EntityWrapper(实体包装类),可以用于拼接 SQL 语句,并且支持排序、分组查询等复杂的SQL。

代码生成器:支持一系列的策略配置与全局配置,比MyBatis的代码生成更好用。

另外MyBatis-Plus也有分页的功能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值