程序中框架【代码半成品】
Mybatis是一个半自动化持久化层ORM框架
ORM:Object Relational Mapping【对象 关系 映射】
Mybatis与Hibernate对比
Mybatis与JDBC对比
Java POJO(Plain Old Java Objects,普通老式 Java 对象)
导入jar包
编写配置文件
使用核心类库
导入jar包
<!--导入MySQL的驱动包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.37</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.26</version>
</dependency>
<!--导入MyBatis的jar包-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
编写核心配置文件【mybatis-config.xml】
位置: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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!-- mysql8版本-->
<!-- <property name="driver" value="com.mysql.cj.jdbc.Driver"/>-->
<!-- <property name="url" value="jdbc:mysql://localhost:3306/db220106?serverTimezone=UTC"/>-->
<!-- mysql5版本-->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/db220106"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<!-- 设置映射文件路径-->
<mappers>
<mapper resource="mapper/EmployeeMapper.xml"/>
</mappers>
</configuration>
书写相关接口及映射文件
映射文件位置:resources/mapper
映射文件名称:XXXMapper.xml
映射文件作用:主要作用为Mapper接口书写Sql语句
示例代码
<?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.atguigu.mybatis.mapper.EmployeeMapper">
<select id="selectEmpById" resultType="com.atguigu.mybatis.pojo.Employee">
SELECT
id,
last_name,
email,
salary
FROM
tbl_employee
WHERE
id=#{empId}
</select>
</mapper>
测试【SqlSession】
导入jar包
<!-- log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
编写配置文件
配置文件名称:log4j.xml
配置文件位置:resources
示例代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
<param name="Encoding" value="UTF-8" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m (%F:%L) \n" />
</layout>
</appender>
<logger name="java.sql">
<level value="debug" />
</logger>
<logger name="org.apache.ibatis">
<level value="info" />
</logger>
<root>
<level value="debug" />
<appender-ref ref="STDOUT" />
</root>
</log4j:configuration>
properties子标签
作用:定义或引入外部属性文件
示例代码
#key=value
db.driver=com.mysql.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/db220106
db.username=root
db.password=root
<properties resource="db.properties"></properties>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!-- mysql8版本-->
<!-- <property name="driver" value="com.mysql.cj.jdbc.Driver"/>-->
<!-- <property name="url" value="jdbc:mysql://localhost:3306/db220106?serverTimezone=UTC"/>-->
<!-- mysql5版本-->
<property name="driver" value="${db.driver}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
</dataSource>
</environment>
</environments>
settings子标签
作用:这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。
mapUnderscoreToCamelCase属性:是否开启驼峰命名自动映射,默认值false,如设置true会自动将
字段a_col与aCol属性自动映射
类型别名(typeAliases)
作用:类型别名可为 Java 类型设置一个缩写名字。
语法及特点
<typeAliases>
<!-- 为指定类型定义别名-->
<!-- <typeAlias type="com.atguigu.mybatis.pojo.Employee" alias="employee"></typeAlias>-->
<!-- 为指定包下所有的类定义别名
默认将类名作为别名,不区分大小写【推荐使用小写字母】
-->
<package name="com.atguigu.mybatis.pojo"/>
</typeAliases>
Mybatis自定义别名
| 别名 | 类型 |
|---|---|
| _int | int |
| integer或int | Integer |
| string | String |
| list或arraylist | ArrayList |
| map或hashmap | HashMap |
环境配置(environments)
作用:设置数据库连接环境
示例代码
<!-- 设置数据库连接环境-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!-- mysql8版本-->
<!-- <property name="driver" value="com.mysql.cj.jdbc.Driver"/>-->
<!-- <property name="url" value="jdbc:mysql://localhost:3306/db220106?serverTimezone=UTC"/>-->
<!-- mysql5版本-->
<property name="driver" value="${db.driver}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
</dataSource>
</environment>
</environments>
mappers子标签
作用:设置映射文件路径
示例代码
<!-- 设置映射文件路径-->
<mappers>
<mapper resource="mapper/EmployeeMapper.xml"/>
<!-- 要求:接口的包名与映射文件的包名需要一致-->
<!-- <package name="com.atguigu.mybatis.mapper"/>-->
</mappers>
注意:核心配置中的子标签,是有顺序要求的。

子标签共有9个,注意学习其中8大子标签
语法:
位置:参数前面
注意:
示例代码
/**
* 通过员工姓名及薪资查询员工信息【命名参数】
* @return
*/
public List<Employee> selectEmpByNamed(@Param("lName")String lastName,
@Param("salary") double salary);
<select id="selectEmpByNamed" resultType="employee">
SELECT
id,
last_name,
email,
salary
FROM
tbl_employee
WHERE
last_name=#{param1}
AND
salary=#{param2}
</select>
源码分析
MapperMethod对象:142行代码【命名参数底层代码入口】
命名参数底层封装map为ParamMap,ParamMap继承HashMap
ParamNameResolver对象:130行代码,命名参数底层实现逻辑
//130行
final Map<String, Object> param = new ParamMap<>();
int i = 0;
for (Map.Entry<Integer, String> entry : names.entrySet()) {
param.put(entry.getValue(), args[entry.getKey()]);
// add generic param names (param1, param2, ...)
final String genericParamName = GENERIC_NAME_PREFIX + (i + 1);
// ensure not to overwrite parameter named with @Param
if (!names.containsValue(genericParamName)) {
param.put(genericParamName, args[entry.getKey()]);
}
i++;
}
return param;
查询SQL:select col,col2 from table1 where col=? and col2=? group by ?, order by ? limit ?,?
$使用场景,#解决不了的参数传递问题,均可以交给$处理【如:form 动态化表名】
/**
* 测试$使用场景
*/
public List<Employee> selectEmpByDynamitTable(@Param("tblName") String tblName);
<select id="selectEmpByDynamitTable" resultType="employee">
SELECT
id,
last_name,
email,
salary
FROM
${tblName}
</select>
/**
* 通过id获取员工信息
*/
public Employee selectEmpById(int empId);
<select id="selectEmpById" resultType="employee">
SELECT
id,
last_name,
email,
salary
FROM
tbl_employee
WHERE
id=#{empId}
</select>
/**
* 查询所有员工信息
*/
public List<Employee> selectAllEmps();
<select id="selectAllEmps" resultType="employee">
SELECT
id,
last_name,
email,
salary
FROM
tbl_employee
</select>
Map<String key,Object value>
示例代码
/**
* 查询单行数据返回Map集合
* @return
*/
public Map<String,Object> selectEmpReturnMap(int empId);
<!-- 查询单行数据返回Map集合-->
<select id="selectEmpReturnMap" resultType="map">
SELECT
id,
last_name,
email,
salary
FROM
tbl_employee
WHERE
id=#{empId}
</select>
Map<Integer key,Employee value>
示例代码
/**
* 查询多行数据返回Map
* Map<Integer,Object>
* Map<Integer,Employee>
* 对象Id作为:key
* 对象作为:value
* @return
*/
@MapKey("id")
public Map<Integer,Employee> selectEmpsReturnMap();
<select id="selectEmpsReturnMap" resultType="map">
SELECT
id,
last_name,
email,
salary
FROM
tbl_employee
</select>
自动映射【resultType】
自定义映射【resultMap】
<!-- 自定义映射 【员工与部门关系】-->
<resultMap id="empAndDeptResultMap" type="employee">
<!-- 定义主键字段与属性关联关系 -->
<id column="id" property="id"></id>
<!-- 定义非主键字段与属性关联关系-->
<result column="last_name" property="lastName"></result>
<result column="email" property="email"></result>
<result column="salary" property="salary"></result>
<!-- 为员工中所属部门,自定义关联关系-->
<result column="dept_id" property="dept.deptId"></result>
<result column="dept_name" property="dept.deptName"></result>
</resultMap>
<select id="selectEmpAndDeptByEmpId" resultMap="empAndDeptResultMap">
SELECT
e.`id`,
e.`email`,
e.`last_name`,
e.`salary`,
d.`dept_id`,
d.`dept_name`
FROM
tbl_employee e,
tbl_dept d
WHERE
e.`dept_id` = d.`dept_id`
AND
e.`id` = #{empId}
</select>
特点:解决一对一映射关系【多对一】
示例代码
<!-- 自定义映射 【员工与部门关系】-->
<resultMap id="empAndDeptResultMapAssociation" type="employee">
<!-- 定义主键字段与属性关联关系 -->
<id column="id" property="id"></id>
<!-- 定义非主键字段与属性关联关系-->
<result column="last_name" property="lastName"></result>
<result column="email" property="email"></result>
<result column="salary" property="salary"></result>
<!-- 为员工中所属部门,自定义关联关系-->
<association property="dept"
javaType="com.atguigu.mybatis.pojo.Dept">
<id column="dept_id" property="deptId"></id>
<result column="dept_name" property="deptName"></result>
</association>
</resultMap>
示例代码
/**
* 通过部门id获取部门信息,及部门所属员工信息
*/
public Dept selectDeptAndEmpByDeptId(int deptId);
<resultMap id="deptAndempResultMap" type="dept">
<id property="deptId" column="dept_id"></id>
<result property="deptName" column="dept_name"></result>
<collection property="empList"
ofType="com.atguigu.mybatis.pojo.Employee">
<id column="id" property="id"></id>
<result column="last_name" property="lastName"></result>
<result column="email" property="email"></result>
<result column="salary" property="salary"></result>
</collection>
</resultMap>
<select id="selectDeptAndEmpByDeptId" resultMap="deptAndempResultMap">
SELECT
e.`id`,
e.`email`,
e.`last_name`,
e.`salary`,
d.`dept_id`,
d.`dept_name`
FROM
tbl_employee e,
tbl_dept d
WHERE
e.`dept_id` = d.`dept_id`
AND
d.dept_id = #{deptId}
</select>
resultMap标签:自定义映射标签
resultMap子标签
为什么使用分步查询【分步查询优势】?
示例代码
一对一
/**
* 通过员工id获取员工信息及员工所属的部门信息【分步查询】
1. 先通过员工id获取员工信息【id、last_name、email、salary、dept_id】
2. 再通过部门id获取部门信息【dept_id、dept_name】
*/
public Employee selectEmpAndDeptByEmpIdAssociationStep(int empId);
<select id="selectEmpAndDeptByEmpIdAssociationStep" resultMap="empAndDeptResultMapAssocationStep">
select
id,
last_name,
email,
salary,
dept_id
from
tbl_employee
where
id=#{empId}
</select>
/**
* 通过部门id获取部门信息
*/
public Dept selectDeptByDeptId(int deptId);
<select id="selectDeptByDeptId" resultType="dept">
select
dept_id,
dept_name
from
tbl_dept
where
dept_id=#{deptId}
</select>
一对多
/**
* 通过部门id获取部门信息,及部门所属员工信息【分步查询】
1. 通过部门id获取部门信息
2. 通过部门id获取员工信息
*/
public Dept selectDeptAndEmpByDeptIdStep(int deptId);
<!-- 通过部门id获取部门信息,及部门所属员工信息【分步查询】-->
<!-- 1. 通过部门id获取部门信息-->
<!-- 2. 通过部门id获取员工信息-->
<select id="selectDeptAndEmpByDeptIdStep" resultMap="deptAndEmpResultMapStep">
select
dept_id,
dept_name
from
tbl_dept
where
dept_id=#{deptId}
</select>
/**
* 通过部门Id获取员工信息
* @param deptId
* @return
*/
public List<Employee> selectEmpByDeptId(int deptId);
<select id="selectEmpByDeptId" resultType="employee">
select
id,
last_name,
email,
salary,
dept_id
from
tbl_employee
where
dept_id=#{deptId}
</select>
需要时加载,不需要暂时不加载
优势:提升程序运行效率
语法
全局设置
<!-- 开启延迟加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 设置加载的数据是按需加载3.4.2及以后的版本该步骤可省略-->
<setting name="aggressiveLazyLoading" value="false"/>
局部设置
fetchType
示例代码
<association property="dept"
select="com.atguigu.mybatis.mapper.DeptMapper.selectDeptByDeptId"
column="dept_id"
fetchType="eager">
</association>
如果分步查询时,需要传递给调用的查询中多个参数,则需要将多个参数封装成
Map来进行传递,语法如下: {k1=v1, k2=v2....}
SQL中注释
//方式一 -- 1=1 //方式二【推荐使用】 <!-- 1=1 -->
if标签:用于完成简单的判断
where标签:用于解决where关键字及where后第一个and或or的问题
trim标签: 可以在条件判断完的SQL语句前后添加或者去掉指定的字符
prefix: 添加前缀
prefixOverrides: 去掉前缀
suffix: 添加后缀
suffixOverrides: 去掉后缀
set标签:主要用于解决set关键字及多出一个【,】问题
choose标签:类似java中if-else【switch-case】结构
foreach标签:类似java中for循环
sql标签:提取可重用SQL片段
<?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.atguigu.mybatis.mapper.EmployeeMapper">
<sql id="emp_col">
id,
last_name,
email,
salary
</sql>
<sql id="select_employee">
select
id,
last_name,
email,
salary
from
tbl_employee
</sql>
<!-- 按条件查询员工信息【条件不确定】-->
<select id="selectEmpByOpr" resultType="employee">
<include refid="select_employee"></include>
<where>
<if test="id != null">
and id = #{id}
</if>
<if test="lastName != null">
and last_name = #{lastName}
</if>
<if test="email != null">
and email = #{email}
</if>
<if test="salary != null">
and salary = #{salary}
</if>
</where>
</select>
<select id="selectEmpByOprTrim" resultType="employee">
<include refid="select_employee"></include>
<trim prefix="where" suffixOverrides="and">
<if test="id != null">
id = #{id} and
</if>
<if test="lastName != null">
last_name = #{lastName} and
</if>
<if test="email != null">
email = #{email} and
</if>
<if test="salary != null">
salary = #{salary}
</if>
</trim>
</select>
<update id="updateEmpByOpr">
update
tbl_employee
<set>
<if test="lastName != null">
last_name=#{lastName},
</if>
<if test="email != null">
email=#{email},
</if>
<if test="salary != null">
salary=#{salary}
</if>
</set>
where
id = #{id}
</update>
<select id="selectEmpByOneOpr" resultType="employee">
select
<include refid="emp_col"></include>
from
tbl_employee
<where>
<choose>
<when test="id != null">
id = #{id}
</when>
<when test="lastName != null">
last_name = #{lastName}
</when>
<when test="email != null">
email = #{email}
</when>
<when test="salary != null">
salary = #{salary}
</when>
<otherwise>
1=1
</otherwise>
</choose>
</where>
</select>
<select id="selectEmpByIds" resultType="employee">
select
id,
last_name,
email,
salary
from
tbl_employee
<where>
id in(
<foreach collection="ids" item="id" separator=",">
#{id}
</foreach>
)
</where>
</select>
<insert id="batchInsertEmp">
INSERT INTO
tbl_employee(last_name,email,salary)
VALUES
<foreach collection="employees" item="emp" separator=",">
(#{emp.lastName},#{emp.email},#{emp.salary})
</foreach>
</insert>
</mapper>
一级缓存
二级缓存
第三方缓存

概述:一级缓存【本地缓存(Local Cache)或SqlSession级别缓存】
特点
缓存原理
一级缓存五种失效情况
不同的SqlSession对应不同的一级缓存
同一个SqlSession但是查询条件不同
同一个SqlSession两次查询期间执行了任何一次增删改操作
二级缓存【second level cache】概述
二级缓存特点
二级缓存使用的步骤:
① 全局配置文件中开启二级缓存
② 需要使用二级缓存的映射文件处使用cache配置缓存
③ 注意:POJO需要实现Serializable接口

④ 关闭sqlSession或提交sqlSession时,将数据缓存到二级缓存
二级缓存底层原理
二级缓存相关属性
二级缓存的失效情况
第三方缓存:EhCache
EhCache 是一个纯Java的进程内缓存框架
使用步骤
导入jar包
<!-- mybatis-ehcache -->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.0.3</version>
</dependency>
<!-- slf4j-log4j12 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.2</version>
<scope>test</scope>
</dependency>
编写配置文件【ehcache.xml】
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<!-- 磁盘保存路径 -->
<diskStore path="E:\mybatis\ehcache" />
<defaultCache
maxElementsInMemory="512"
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
</ehcache>
加载第三方缓存【映射文件】
开始使用
注意事项
基于Spring注解+MyBatis+Servlet实现数据库交换的小小Demo第一步创建web项目,这一步省略,有不会的可以参考之前发布的文档第二步配置pom.xml文件dependencies>dependency>groupId>org.springframeworkgroupId>artifactId>spring-contextartifactId>version>5.2.9.RELEASEversion>dependency>dependency>groupId>org.springframeworkgroupId>artifactId>spring-aspectsartifact
特别说明:本次项目整合基于idea进行的,如果使用Eclipse可能操作会略有不同,不过总的来说不影响。springboot整合之如何选择版本及项目搭建springboot整合之版本号统一管理 springboot整合mybatis-plus+durid数据库连接池springboot整合swaggerspringboot整合mybatis代码快速生成springboot整合之统一结果返回springboot整合之统一异常处理springboot整合之Validated参数校验 springboot整合之logback日志配置springboot整合pagehelper分页springboot
一、环境搭建1.创建一个springboot项目(勾选web)2.导入依赖org.springframework.bootspring-boot-starter-weborg.springframework.bootspring-boot-starter-testtestorg.springframework.bootspring-boot-starterorg.mybatis.spring.bootmybatis-spring-boot-starter1.3.2mysqlmysql-connector-javaruntimecom.alibabadruid1.1.12org.junit.ju
目录一MyBatis动态拼接SQL1:if标签2、where标签3:foreach标签二 MyBatisPlus条件构造器2.1使用QueryWrapper进行各种条件构造2.1.1案例一组装条件查询2.1.2、例2:组装排序条件2.1.3、例3:组装删除条件2.1.4、例4:条件的优先级2.1.5实现子查询2.2常用的QueryWrapper查询语法总结一MyBatis动态拼接SQL1:if标签andmoney>10002、where标签andmoney>1000where标签的作用是可以自动处理掉第一个and3:foreach标签#{id}二 MyBatisPlus条件构造器2.1使用Qu
我们目前正在重新实现我们的数据库层以使用MyBatis。我们有一个用XML定义的模型“User”的ResultMapper。对于一个相当复杂的搜索查询,我们希望用Java构建查询,但从用XML定义的ResultMapper获得类型化结果。我认为必须有可能获得Java中的ResultMappers,以便将它们与手工制作的Java选择查询相结合。任何人都知道我应该如何或应该尝试另一种方法吗? 最佳答案 这违背了MyBatis的目的。来自MyBatis文档:OneofthenastiestthingsaJavadeveloperwille
我尝试用MyBatis3.2.8映射集合的反向关系。看起来MyBatis会复制对象,即使它们具有相同的id。publicclassObjA{privateStringid;privateArrayListobjBs;//Getters,setters...//Equalsbasedontheidfield}publicclassObjB{privateStringid;privateObjAobjA;//Getters,setters...//Equalsbasedontheidfield}映射xml文件JUnit测试ArrayListresult=service.getAllObjA
是否可以在myBatisresultMap中映射一个嵌套的java.sql.ResultSet?例如。假设我有一个这样定义的过程映射:{callgetCarsByYear(#{year,jdbcType=INTEGER,mode=IN},#{results,jdbcType=CURSOR,mode=OUT,javaType=java.sql.ResultSet,jdbcType=CURSOR,resultMap=cars})}还有我的映射器。它返回一个汽车对象列表,还有一个经销商列表(嵌套的CURSOR):这里的问题是,当我检查生成的java对象时,dealerships是一个空列表。
我正在移植一些旧的iBatis版本2。?代码,想知道如何替换标签?我读过MyBatisuserguide,并且知道你可以做一个但我不知道如何引用参数对象?它有什么特别的名字吗?是否可以使用不同的标签? 最佳答案 原来有一个特殊的变量叫做“_parameter”。它没有出现在用户指南中,但它应该在那里accordingtoJeffButler.然后您可以测试此变量是否为空。因此它似乎是的替代品是. 关于java-MyBatis中的iBatis标签是什么?,我们在StackOverflow上
我找到了几个如何将自定义ResultHandler连接到MyBatis查询的示例:例如https://code.google.com/p/mybatis/wiki/ResultHandlerExample不幸的是,示例中给出的ResultHandler从未被调用。(如上一条评论所述)所以我搜索了一个解决方案并找到了这个:MyBatis-ResultHandlerisnotinvoked但这不太适合我的问题,因为我使用MyBatis的xml样式方式而不是API样式方式。所以就我而言,我没有SqlSessionsession=MyBatisConnectionFactory.getSqlS
我已经在一个mapper.xml文件中为表/对象编写了一个标准的选择和结果映射,我想知道是否有一种方法可以通过关联、集合上的“select”参数在另一个mapper.xml文件中使用这个选择等 最佳答案 其他映射器文件中定义的元素可以由包含映射器命名空间的完全限定标识符使用。例如,您在mapper1.xml中选择:select*formentity1可以在mapper2.xml中使用: 关于xml-Mybatis从另一个mapper.xml文件中引用sql,我们在StackOverflo