# mybatis相关梳理
# 1、简介
MyBatis 是一个开源、轻量级的数据持久化框架,是 JDBC 和 Hibernate 的替代方案。MyBatis 内部封装了 JDBC,简化了加载驱动、创建连接、创建 statement 等繁杂的过程,开发者只需要关注 SQL 语句本身。
MyBatis 支持定制化 SQL、存储过程以及高级映射,可以在实体类和 SQL 语句之间建立映射关系,是一种半自动化的 ORM 实现。其封装性低于 Hibernate,但性能优秀、小巧、简单易学、应用广泛。
解析:
- 数据持久化:将内存中的数据模型转换为存储模型,以及将存储模型转换为内存中数据模型的统称。例如,文件的存储、数据的读取以及对数据表的增删改查等都是数据持久化操作。
- ORM:(Object Relational Mapping,对象关系映射)是一种数据持久化技术,它在对象模型和关系型数据库之间建立起对应关系,并且提供了一种机制,通过 JavaBean 对象去操作数据库表中的数据。
MyBatis三个基本要素:
- 核心接口和类
- MyBatis核心配置文件(mybatis-config.xml)
- sql映射文件(mapper.xml)
# 2、mybatics映射器
映射文件和接口在同一package下:WebsiteMapper.java、WebsiteMapper.xml,映射器mapper.xml主要元素:
- mapper:根节点,只有一个namespace属性,全局唯一绑定Dao接口(WebsiteMapper.java),不用写实现类。
- select、insert、update、delete
- parameterMap:定义参数映射关系
- sql:定义一部分sql,在其他地方引用
- resultMap:描述数据库结果集与对象对应关系
- cache:配置给定命名空间的缓存
- cache-ref:其他命名空间缓存的引用
# 3、select 标签
# 1、标签属性
- id:唯一标识,保证namespace + id全局唯一
- parameterType:传入参数类型的全限定名或别名,可选
- resultType:SQL 语句执行后返回的类型(全限定名或者别名)
- resultMap:映射集的引用,与
<resultMap>
元素一起使用,返回时可以使用resultType或resultMap之一 - fetchSize:获取记录总条数,默认值为数据库厂商提供的 JDBC 驱动所设置的条数
- timeout:超时时间,单位s
- flushCache:用于设置在调用 SQL 语句后是否要求 MyBatis 清空之前查询的本地缓存和二级缓存,默认值为 false,如果设置为 true,则任何时候只要 SQL
- useCache:启动二级缓存的开关,默认值为 true,表示将査询结果存入二级缓存中
- statementType:告诉 MyBatis 使用哪个 JDBC 的 Statement 工作,取值为 STATEMENT(Statement)普通的不带参的查询SQL、 PREPARED(PreparedStatement)支持可变参数的SQL、CALLABLE(CallableStatement)支持调用存储过程,提供了对输出和输入/输出参数(INOUT)的支持。
- resultSetType:这是针对 JDBC 的 ResultSet 接口而言,其值可设置为 FORWARD_ONLY(只允许向前访问)、SCROLL_SENSITIVE(双向滚动,但不及时更新)、SCROLLJNSENSITIVE(双向滚动,及时更新)
# 2、传递参数方法
# 1、使用map传递参数
使用map接口作为参数实现
//WebsiteMapper.xml
<select id="selectWebsiteByMap" resultType="net.biancheng.po.Website" parameterType="map">
SELECT id,NAME,url FROM website
WHERE name LIKE CONCAT ('%',#{name},'%')
AND url LIKE CONCAT ('%',#{url},'%')
</select>
//WebsiteMapper.java
public List<Website> selectWebsiteByMap(Map<String, String> params);
//使用案例:map传入name、url参数
Map<String,String> paramsMap = new HashMap<String,String>();
paramsMap.put("name","编程");
paramsMap.put("url","biancheng");
websiteMapper.selectWebsiteByMap(paramsMap);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 2、使用注解传递参数
使用注解@Param()传递参数
//WebsiteMapper.xml
<select id="selectWebsiteByMap" resultType="net.biancheng.po.Website" parameterType="map">
SELECT id,NAME,url FROM website
WHERE name LIKE CONCAT ('%',#{name},'%')
AND url LIKE CONCAT ('%',#{url},'%')
</select>
//WebsiteMapper.java
public List<Website> selectWebsiteByMap(@Param("name") String name, @Param("url") String url);
2
3
4
5
6
7
8
9
# 3、使用javaBean传递参数
在参数过多的情况下,MyBatis 允许组织一个 JavaBean,通过简单的 setter 和 getter 方法设置参数,提高可读性
//WebsiteMapper.xml
<select id="selectWebsiteByMap" resultType="net.biancheng.po.Website" parameterType="map">
SELECT id,NAME,url FROM website
WHERE name LIKE CONCAT ('%',#{name},'%')
AND url LIKE CONCAT ('%',#{url},'%')
</select>
//WebsiteMapper.java
public List<Website> selectWebsiteByAn(Website website);
2
3
4
5
6
7
8
9
# 4、insert标签
# 1、标签属性
- id、parameterType、flushCache、flushCache:同上
- keyProperty:将插入操作的返回值赋给 PO 类的某个属性,通常为主键对应的属性。如果是联合主键,可以将多个值用逗号隔开。
- keyColumn:该属性用于设置第几列是主键,当主键列不是表中的第 1 列时,就需要设置该属性。如果是联合主键,可以将多个值用逗号隔开。
- useGeneratedKe:用来设置,是否使用 JDBC 提供的 getGenereatedKeys() 方法,获取数据库内部产生的主键并赋值到 keyProperty 属性设置的请求对象的属性中,例如 MySQL、SQL Server 等自动递增的字段,其默认值为 false。该属性值设置为 true 后,会将数据库生成的主键回填到请求对象中,以供其他业务使用。
- databaseId:取值范围 oracle、mysql 等,表示数据库厂家;元素内部可通过
<if test="_databaseId = 'oracle'">
来为特定数据库指定不同的 sql 语句。
# 2、主键处理
# 1、自动递增
MySQL、SQL Server 等数据库表可以采用自动递增的字段作为其主键,当向这样的数据库表插入数据时,即使不指定自增主键的值,数据库也会根据自增规则自动生成主键并插入到表中。一些特殊情况下,我们可能需要将这个刚刚生成的主键回填到请求对象(原本不包含主键信息的请求对象)中,供其他业务使用。此时,我们就可以通过在 insert 标签中添加 keyProperty 和 useGeneratedKeys 属性,来实现该功能。
<!-- WebsiteMapper.xml -->
<insert id="addWebsite" parameterType="net.biancheng.po.Website" keyProperty="id" useGeneratedKeys="true">
insert into Website (name,url) values(#{name},#{url})
</insert>
// 添加一个网站信息
Website addsite = new Website();
//插入的对象中不包含主键 id
addsite.setName("jian");
addsite.setUrl("https://www.baidu.com/");
int num = websiteMapper.addWebsite(addsite);
System.out.println("添加了 " + num + " 条记录");
System.out.println("添加记录的主键是:" + addsite.getId());
2
3
4
5
6
7
8
9
10
11
12
13
# 2、自定义主键
若数据库不支持主键自动递增(例如 Oracle),或者取消了主键自动递增的规则,我们可以使用 MyBatis 的 <selectKey>
标签自定义生成主键。
<selectKey>
标签中属性说明如下:
- keyProperty:用于指定主键值对应的 PO 类的属性。
- order:该属性取值可以为 BEFORE 或 AFTER。BEFORE 表示先执行
<selectKey>
标签内的语句,再执行插入语句;AFTER 表示先执行插入语句再执行<selectKey>
标签内的语句。
<!-- 添加一个网站,#{name}为 net.biancheng.po.Website 的属性值 -->
<insert id="insertWebsite" parameterType="net.biancheng.po.Website">
<!-- 先使用selectKey标签定义主键,然后再定义SQL语句 -->
<selectKey keyProperty="id" resultType="Integer" order="BEFORE">
select if(max(id) is null,1,max(id)+1) as newId from Website
</selectKey>
insert into Website (id,name,url) values(#{id},#{name},#{url})
</insert>
2
3
4
5
6
7
8
# 5、resultMap标签
用于解决实体类属性名与数据库表中字段名不一致的情况,可以将查询结果映射成实体对象
<!-- id为唯一标识,type属性表示需要的POJO -->
<resultMap id="BaseResultMap" type="com.icbc.inforisk.rule.entity.AfwMagParameterArea">
<constructor> <!-- 类在实例化时用来注入结果到构造方法 -->
<idArg/> <!-- ID参数,结果为ID -->
<arg/> <!-- 注入到构造方法的一个普通结果 -->
</constructor>
<!-- 用于表示哪个列是主键 -->
<id column="TABLE_NAME" jdbcType="VARCHAR" property="tableName"/>
<!-- 用于表示POJO和SQL列名的映射关系 -->
<result column="PARA_VALUE_1" jdbcType="VARCHAR" property="paraValue1"/>
<association property=""/> <!-- 用于一对一关联 -->
<collection property=""/> <!-- 用于一对多、多对多关联 -->
<discriminator javaType=""> <!-- 使用结果值来决定使用哪个结果映射 -->
<case value=""/> <!-- 基于某些值的结果映射 -->
</discriminator>
</resultMap>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
id和result常用,有如下属性:
- property:映射到列结果的字段或属性
- column:对应sql列
- javaType:配置java类型,可以是特定的类完全限定名或别名
- jdbcType:配置数据库类型
- typeHandler:类型处理器,需要指定jdbcType和javaType相互转化的规则
# 6、注解
可在mybatis-x.x.x.jar/org.apache.ibatis.annotations中查看
- sql语句映射:@Insert、@Select、@Delete、@Param等
- 结果集映射:@Result、@Results、@ResultMap
- 关系映射:@one、@many
# 7、动态SQL
在映射xml文件里配置动态sql,有几种元素
- if:判断语句
- choose(when、otherwise):多条件判断,类似switch和case语句
- trim、where:辅助元素,处理sql拼接问题
- foreach:循环语句,在in语句等列举条件常用
- bind:辅助元素,拼接参数
# 1、if标签
<select id="selectAllWebsite" resultMap="myResult">
select id,name,url from website
<if test="name != null">
where name like #{name}
</if>
</select>
2
3
4
5
6
# 2、choose标签
<mapper namespace="net.biancheng.mapper.WebsiteMapper">
<select id="selectWebsite"
parameterType="net.biancheng.po.Website"
resultType="net.biancheng.po.Website">
SELECT id,name,url,age,country
FROM website WHERE 1=1
<choose>
<when test="name != null and name !=''">
AND name LIKE CONCAT('%',#{name},'%')
</when>
<when test="url != null and url !=''">
AND url LIKE CONCAT('%',#{url},'%')
</when>
<otherwise>
AND age is not null
</otherwise>
</choose>
</select>
</mapper>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 3、where标签
用于简化and、or语句,类型于where语句:if 语句中判断条件为 true 时,where 关键字才会加入到组装的 SQL 里面,否则就不加入。where 会检索语句,它会将 where 后的第一个 SQL 条件语句的 AND 或者 OR 关键词去掉。
<!-- if条件成立时,才会引入where语句 -->
<select id="selectWebsite" resultType="net.biancheng.po.Website">
select id,name,url from website
<where>
<if test="name != null">
AND name like #{name}
</if>
<if test="url!= null">
AND url like #{url}
</if>
</where>
</select>
2
3
4
5
6
7
8
9
10
11
12
# 4、set标签
update 语句可以使用 set 标签动态更新列。set 标签可以为 SQL 语句动态的添加 set 关键字,剔除追加到条件末尾多余的逗号。
<update id="updateWebsite" parameterType="net.biancheng.po.Website">
UPDATE website
<set>
<if test="name!=null">name=#{name}</if>
<if test="url!=null">url=#{url}</if>
</set>
WHERE id=#{id}
</update>
2
3
4
5
6
7
8
# 5、foreach标签
对于一些 SQL 语句中含有 in 条件,需要迭代条件集合来生成的情况,可以使用 foreach 来实现 SQL 条件的迭代。
- foreach 标签主要有以下属性:
- item:表示集合中每一个元素进行迭代时的别名
- index:指定一个名字,表示在迭代过程中每次迭代到的位置
- open:表示该语句以什么开始(既然是 in 条件语句,所以必然以
(
开始) - separator:表示在每次进行迭代之间以什么符号作为分隔符(既然是 in 条件语句,所以必然以
,
作为分隔符)
- close:表示该语句以什么结束(既然是 in 条件语句,所以必然以
)
开始)
- 使用 foreach 标签时,最关键、最容易出错的是 collection 属性,该属性是必选的,但在不同情况下该属性的值是不一样的,主要有以下 3 种情况:
- 如果传入的是单参数且参数类型是一个 List,collection 属性值为 list
- 如果传入的是单参数且参数类型是一个 array 数组,collection 的属性值为 array
- 如果传入的参数是多个,需要把它们封装成一个 Map,当然单参数也可以封装成 Map。Map 的 key 是参数名,collection 属性值是传入的 List 或 array 对象在自己封装的 Map 中的 key
<!-- WebsiteMapper.xml -->
<select id="selectWebsite" parameterType="net.biancheng.po.Website" resultType="net.biancheng.po.Website">
SELECT id,name,url,age,country
FROM website WHERE age in
<foreach item="age" index="index" collection="list" open="(" separator="," close=")">
#{age}
</foreach>
</select>
//WebsiteMapper.java
public List<Website> selectWebsite(List<Integer> ageList);
//test.java
public class Test {
public static void main(String[] args) throws IOException {
// 读取配置文件mybatis-config.xml
InputStream config = Resources.getResourceAsStream("mybatis-config.xml"); // 根据配置文件构建
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(config);
// 通过SqlSessionFactory创建SqlSession
SqlSession ss = ssf.openSession();
List<Integer> ageList = new ArrayList<Integer>();
ageList.add(10);
ageList.add(12);
List<Website> siteList = ss.selectList("net.biancheng.mapper.WebsiteMapper.selectWebsite", ageList);
for (Website ws : siteList) {
System.out.println(ws);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 6、bind标签
bind 标签可以通过 OGNL 表达式自定义一个上下文变量
<select id="selectWebsite" resultType="net.biancheng.po.Website">
<bind name="pattern" value="'%'+_parameter+'%'" />
SELECT id,name,url,age,country
FROM website
WHERE name like #{pattern}
</select>
2
3
4
5
6
# 7、trim标签
在 MyBatis 中除了使用 if+where 实现多条件查询,还有一个更为灵活的元素 trim 能够替代之前的做法。
trim 一般用于去除 SQL 语句中多余的 AND 关键字、逗号,
或者给 SQL 语句前拼接 where、set 等后缀,可用于选择性插入、更新、删除或者条件查询等操作。
<select id="selectWebsite" resultType="net.biancheng.po.Website">
SELECT id,name,url,age,country
FROM website
<trim prefix="where" prefixOverrides="and">
<if test="name != null and name !=''">
AND name LIKE CONCAT ('%',#{name},'%')
</if>
<if test="url!= null">
AND url like concat ('%',#{url},'%')
</if>
</trim>
</select>
2
3
4
5
6
7
8
9
10
11
12