1、动态SQL
MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其他类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句有多么痛苦。拼接的时候要确保不能忘了必要的空格,还要注意省掉列名列表最后的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。
通常使用动态 SQL 不可能是独立的一部分,MyBatis 当然使用一种强大的动态 SQL 语言来改进这种情形,这种语言可以被用在任意的 SQL 映射语句中。
环境搭建
项目结构
建表语句
CREATE TABLE `blog` ( `id` int(10) NOT NULL AUTO_INCREMENT COMMENT '博客id', `title` varchar(30) NOT NULL COMMENT '博客标题', `author` varchar(30) NOT NULL COMMENT '博客作者', `create_time` datetime NOT NULL COMMENT '创建时间', `views` int(30) NOT NULL COMMENT '浏览量', `state` int(2) DEFAULT NULL COMMENT '状态', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
实体类
@Data@NoArgsConstructor@AllArgsConstructorpublic class Blog { private Integer id; private String title; private String author; private Date createTime; private int views; private int active;}
MybatisUtil工具类
public class MybatisUtil { static SqlSessionFactory sqlSessionFactory; static{ try{ String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); }catch (Exception e) { e.printStackTrace(); } } public static SqlSession getSqlSession() { return sqlSessionFactory.openSession(); }}
mybatis-config.xml核心配置文件
<?xml version="1.0" encoding="UTF-8" ?>/span> PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration> <properties resource="db.properties" /> <settings> <setting name="logImpl" value="LOG4J"/> settings> <typeAliases> <package name="com.liziba.mybatis.pojo"/> typeAliases> <environments default="development"> <environment id="development"> <transactionManager type="JDBC" /> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> dataSource> environment> environments> <mappers> <mapper resource="com/liziba/mybatis/mapper/BlogMapper.xml" /> mappers>configuration>
db.properties数据库配置文件
driver=com.mysql.cj.jdbc.Driverurl=jdbc:mysql://localhost:3306/mybatis?userSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTCusername=xxxxpassword=xxxx
log4j.properties日志配置文件
# 将等级为DEBUG级别的日志输出到console和file两个目的地log4j.rootLogger=DEBUG,console,file# 控制台输出配置log4j.appender.console=org.apache.log4j.ConsoleAppenderlog4j.appender.console.Target=System.outlog4j.appender.console.Threshold=DEBUGlog4j.appender.console.layout=org.apache.log4j.PatternLayoutlog4j.appender.console.layout.ConversionPattern=[%c]-%m%n# 文件输出配置log4j.appender.file=org.apache.log4j.RollingFileAppenderlog4j.appender.file.File=./log/rzp.loglog4j.appender.file.MaxFileSize=10mblog4j.appender.file.Threshold=DEBUGlog4j.appender.file.layout=org.apache.log4j.PatternLayoutlog4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n# 日志输出级别配置log4j.logger.org.mybatis=DEBUGlog4j.logger.java.sql=DEBUGlog4j.logger.java.sql.Statement=DEBUGlog4j.logger.java.sql.ResultSet=DEBUGlog4j.logger.java.sq1.PreparedStatement=DEBUG
if
这条语句提供了一个可选的文本查找类型的功能。如果没有传入"title",那么所有active = 1的BLOG都会返回;反之若传入了"title",那么就会把模糊查找"title"内容的BLOG结果返回
<select id="findActiveBlogsWithTitleLike" parameterType="string" resultType="blog"> select * from blog where active = 1 <if test="title != null"> and title like #{title} if>select>
如果想可选地通过"title"和"author"两个条件搜索
<select id="selectBlogsUseIf" parameterType="map" resultType="blog"> select * from blog where active = 1 <if test="title != null"> and title = #{title} if> <if test="author != null"> and author = #{author} if>select>
choose (when, otherwise)
有些时候,我们不想用到所有的条件语句,而只想从中择其一二
<select id="findActiveBlogsOrViewsWithLike" parameterType="map" resultType="blog"> select * from blog where active = 1 <choose> <when test="title != null"> and title like #{title} when> <when test="author != null"> and author like #{author} when> <otherwise> and views > 1000 otherwise> choose>select>
trim (where, set)
where
现在考虑回到"if"示例,这次我们将active = 1也设置成动态的条件,通过参数传入,看看会发生什么。
<select id="findActiveBlogsWithTitleLike" parameterType="map" resultType="blog"> select * from blog where <if test="active != null"> active = #{active} if> <if test="title != null"> and title like #{title} if>select>
如果这些条件没有一个能匹配上将会怎样?最终这条 SQL 会变成这样
select * from blog where
这会导致查询失败。如果仅仅第二个条件匹配又会怎样?这条 SQL 最终会是这样
select * from blog where and title like 'xxxx'
这个查询也会失败
如何解决呢?我们通过where来解决,where 元素知道只有在一个以上的if条件有值的情况下才去插入"WHERE"子句。而且,若最后的内容是"AND"或"OR"开头的,where 元素也知道如何将他们去除。
<select id="findActiveBlogsWithTitleLike" parameterType="map" resultType="blog"> select * from blog <where > <if test="active != null"> active = #{active} if> <if test="title != null"> and title like #{title} if> where>select>
set
类似的用于动态更新语句的解决方案叫做 set。set 元素可以被用于动态包含需要更新的列,而舍去其他的
这里需要注意的是每个值后面的 ,
<update id="updateBlogIfNecessary" parameterType="blog"> update blog <set> <if test="title != null"> title = #{title}, if> <if test="author != null"> author = #{author}, if> <if test="views != null"> views = #{views}, if> <if test="active != null"> active = #{active}, if> set> where id = #{id}update>
foreach
动态 SQL 的另外一个常用的必要操作是需要对一个集合进行遍历,通常是在构建 IN 条件语句的时候
<select id="selectBlogsByIds" parameterType="list" resultType="blog"> select * from blog where id in <foreach collection="list" item="id" index="index" open="(" separator="," close=")"> #{id} foreach>select>
foreach 元素的功能是非常强大的,它允许你指定一个集合,声明可以用在元素体内的集合项和索引变量。它也允许你指定开闭匹配的字符串以及在迭代中间放置分隔符。这个元素是很智能的,因此它不会偶然地附加多余的分隔符。
注意 你可以将一个 List 实例或者数组作为参数对象传给 MyBatis,当你这么做的时候,MyBatis 会自动将它包装在一个 Map 中并以名称为键。List 实例将会以"list"作为键,而数组实例的键将是"array"。
SQL 片段
有的时候,我们可能会将一些功能的部分抽取出来,方便使用!
- 使用SQL标签抽取公共部分
<sql id="if-title-author"> <if test="title!=null"> title = #{title} if> <if test="author!=null"> and author = #{author} if>sql>
- 在需要使用的地方使用Include标签引用即可
<select id="getBlogs" parameterType="map" resultType="blog"> select * from blog <where> <include refid="if-title-author">include> where>select>
自定义标签
where等价于
<trim prefix="WHERE" prefixOverrides="AND |OR "> ... trim>
set等价于
<trim prefix="SET" suffixOverrides=","> ...trim>