Mybatis—动态SQL
- MyBatis 的强大特性之一便是它的动态 SQL。
- 动态SQL就是根据不同的条件生成不同的SQL语句。
- 动态 SQL 元素和 JSTL 或基于类似 XML 的文本处理器相似。在 MyBatis 之前的版本中,有很多元素需要花时间了解。MyBatis 3 大大精简了元素种类,现在只需学习原来一半的元素便可。
- MyBatis 采用功能强大的基于 OGNL 的表达式来淘汰其它大部分元素。
1、搭建环境
最完整的包结构图示:
-
创建博客表
CREATE TABLE `blog` ( `id` varchar(50) NOT NULL COMMENT '博客id', `title` varchar(100) NOT NULL COMMENT '博客标题', `author` varchar(30) NOT NULL COMMENT '博客作者', `create_time` datetime NOT NULL COMMENT '创建时间', `views` int(30) NOT NULL COMMENT '浏览量' ) ENGINE=InnoDB DEFAULT CHARSET=utf8
-
创建基础工程
-
在
pom.xml
导包<dependencies> <!--mysql驱动--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <!--mybatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.4</version> </dependency> <!--junit--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.10</version> </dependency> </dependencies>
-
在
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核心配置文件--> <configuration> <!--引入外部配置文件--> <properties resource="db.properties"/> <settings> <setting name="logImpl" value="LOG4J"/> <!--是否开启自动驼峰命名规则(camel case)映射--> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> <!--可以给实体类起别名--> <typeAliases> <package name="com.jiang.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="${pwd}"/> </dataSource> </environment> </environments> <mappers> <mapper class="com.jiang.dao.BlogMapper"/> </mappers> </configuration>
-
在
resources
下编写配置文件db.properties
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8 username=root pwd=123456
-
在
resources
下编写配置文件log4j.properties
### 设置### log4j.rootLogger = debug,stdout,D,E ### 输出信息到控制抬 ### log4j.appender.stdout = org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target = System.out log4j.appender.stdout.layout = org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n ### 输出DEBUG 级别以上的日志到=E://logs/error.log ### log4j.appender.D = org.apache.log4j.DailyRollingFileAppender log4j.appender.D.File = ./log/log.log log4j.appender.D.Append = true log4j.appender.D.Threshold = DEBUG log4j.appender.D.layout = org.apache.log4j.PatternLayout log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n ### 输出ERROR 级别以上的日志到=E://logs/error.log ### log4j.appender.E = org.apache.log4j.DailyRollingFileAppender log4j.appender.E.File =./log/error.log log4j.appender.E.Append = true log4j.appender.E.Threshold = ERROR log4j.appender.E.layout = org.apache.log4j.PatternLayout
-
在
java
包下创建com.jiang.pojo
包,在此包下编写Blog
实体类package com.jiang.pojo; import lombok.Data; import java.util.Date; @Data public class Blog { private String id; private String title; private String author; private Date createTime; //属性名和字段名不一致, private int views; }
-
在
java
包下创建com.jiang.dao
包,在此包下编写实体类对应的-
BlogMapper
接口 -
BlogMapper.XML
文件<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.jiang.dao.BlogMapper"> </mapper>
-
-
在
java
包下创建com.jiang.utils
包编写MybatisUtils
工具类package com.jiang.utils; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; import java.io.InputStream; //sqlSessionFactory --> sqlSession public class MybatisUtils { private static SqlSessionFactory sqlSessionFactory; static{ try { //使用Mybatis第一步:获取sqlSessionFactory对象 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } public static SqlSession getSqlSession(){ return sqlSessionFactory.openSession(true); } }
-
在
java
包下创建com.jiang.utils
包编写UUID
工具类//这个帮助我们等会生成随机数 package com.jiang.utils; import org.junit.Test; import java.util.UUID; @SuppressWarnings("all") //抑制警告 public class IDutils { public static String getId(){ return UUID.randomUUID().toString().replaceAll("-",""); } }
-
添加表中的数据
-
接口
//插入数据 int addBlog(Blog blog);
-
接口配置
<insert id="addBlog" parameterType="blog"> insert into mybatis.blog (id, title, author, create_time, views) values (#{id},#{title}, #{author}, #{createTime}, #{views}); </insert>
-
在
test.java
包中编写测试类MyTest
@Test public void addInitBlog() { SqlSession session = MybatisUtils.getSqlSession(); BlogMapper mapper = session.getMapper(BlogMapper.class); for (int i = 0; i < 4; i++) { Blog blog = new Blog(); blog.setId(IDutils.getId()); blog.setTitle("Mybatis So easy"); blog.setAuthor("姜嘉航"); blog.setCreateTime(new Date()); blog.setViews(9999); mapper.addBlog(blog); } Blog blog1 = new Blog(); blog1.setId(IDutils.getId()); blog1.setTitle("Java So easy"); blog1.setAuthor("姜嘉航"); blog1.setCreateTime(new Date()); blog1.setViews(9999); mapper.addBlog(blog1); Blog blog2 = new Blog(); blog2.setId(IDutils.getId()); blog2.setTitle("Spring So easy"); blog2.setAuthor("姜嘉航"); blog2.setCreateTime(new Date()); blog2.setViews(9999); mapper.addBlog(blog2); Blog blog3 = new Blog(); blog3.setId(IDutils.getId()); blog3.setTitle("微服务 So easy"); blog3.setAuthor("姜嘉航"); blog3.setCreateTime(new Date()); blog3.setViews(9999); mapper.addBlog(blog3); Blog blog4 = new Blog(); blog4.setId(IDutils.getId()); blog4.setTitle("微服务 So easy"); blog4.setAuthor("姜嘉航"); blog4.setCreateTime(new Date()); blog4.setViews(9999); mapper.addBlog(blog4); session.close(); }
插入成功!
-
-
2、If
要求:
通过作者名,和博客名字来查询博客
如果作者名字为null,则根据博客名字来查询
-
编写
BlogMapper
接口//查询博客 List<Blog> getBlogByIF(Map map);
-
编写
BlogMapper.xml
<!--如果我们需要拼接where条件,又不希望客户端传递错误信息,需要更加智能的where标签,如果后面的语句有逗号,就自动添加where,如果后面语句开头是and或者or,它可以自动去掉--> <select id="getBlogByIF" parameterType="map" resultType="Blog"> select * from mybatis.blog <if test="title != null"> title = #{title} </if> <if test="author != null"> and author = #{author} </if> </select>
-
测试不同参数结果运行
@Test public void testGetBlogIf() { SqlSession sqlSession = MybatisUtils.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); HashMap<String, String> map = new HashMap<String, String>(); map.put("title","Mybatis So easy"); map.put("author","姜小姜"); List<Blog> blogByIF = mapper.getBlogByIF(map); for (Blog blog : blogByIF) { System.out.println(blog); } sqlSession.close(); }
3、trim(where,set)
3.1、set
要求:更新博客
-
编写
BlogMapper
接口//更新博客 int updateBlog(Map map);
-
编写
BlogMapper.xml
<!--set标签,这里,set 元素会动态前置 SET 关键字,同时也会删掉无关的逗号,因为用了条件语句之后很可能就会在生成的 SQL 语句的后面留下这些逗号--> <update id="updateBlog" parameterType="map"> update mybatis.blog <set> <if test="title != null"> title = #{title}, </if> <if test="author != null"> author = #{author} </if> </set> where id = #{id} </update>
set
元素等价于自定义的trim
元素:<trim prefix="SET" suffixOverrides=","> ... </trim>
-
测试
@Test public void upadateBlog() { SqlSession session = MybatisUtils.getSqlSession(); BlogMapper mapper = session.getMapper(BlogMapper.class); HashMap<String, String> map = new HashMap<String, String>(); map.put("id", "c4cab03768a34dcbaafa36731e680b3e"); map.put("author","姜小帅"); mapper.updateBlog(map); }
3.2、where
select * from mybatis.blog
<where>
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</where>
where 元素只会在至少有一个子元素的条件返回 SQL 子句的情况下才去插入“WHERE”子句。而且,若语句的开头为“AND”或“OR”,where 元素也会将它们去除。
where
元素等价于自定义trim
元素
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
4、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="queryBlogIF" parameterType="map" resultType="blog"> select * from mybatis.blog <where> <include refid="if-title-author"></include> </where> </select>
注意事项:
- 最好基于单表来定义SQL片段!
- 不要存在where标签
5、foreach
collection传递的参数:你要给他传递的集合对象
item:遍历出来集合的每一项,可以再foreach中使用
open:用什么字符串打开
close:用什么字符串结束
separator:分隔符
-
编写
BlogMapper
接口List<Blog> queryBlogForeach(Map map);
-
编写
BlogMapper.xml
<!--相当于sql语句: select * from mybatis.blog where and(id=1 or id=2 or id=3 or...) --> <select id="queryBlogForeach" parameterType="map" resultType="blog"> select * from mybatis.blog <where> <foreach collection="ids" item="id" open="and (" close=")" separator="or"> id = #{id} </foreach> </where> </select>
-
测试
@Test public void queryBlogForEach() { SqlSession sqlSession = MybatisUtils.getSqlSession(); BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); HashMap map = new HashMap(); ArrayList<String> ids = new ArrayList<String>(); ids.add("c4cab03768a34dcbaafa36731e680b3e"); ids.add("372b3c3a0ce54cb19675e2f703afb310"); ids.add("523b2881f4d34682956b1ede6214daba"); map.put("ids", ids); List<Blog> blogs = mapper.queryBlogForeach(map); System.out.println(blogs); sqlSession.close(); }
6、choose,when,otherwise
有时我们不想应用到所有的条件语句,而只想从中择其一项。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句
还是上面的例子
select * from mybatis.blog
<where>
<choose>
<when test="title != null">
title = #{title}
</when>
<when test="author != null">
and author = #{author}
</when>
<otherwise>
and views = #{views}
</otherwise>
</choose>
</where>
</select>