1.什麼是動態SQL
MyBatis 的強大特性之一便是它的動態 SQL。如果你有使用 JDBC 或其它類似框架的經驗,你就能體會到根據不同條件拼接 SQL 語句的痛苦。例如拼接時要確保不能忘記添加必要的空格,還要注意去掉列表最後一個列名的逗號。利用動態 SQL 這一特性可以徹底擺脫這種痛苦。簡單說動態SQL就是指根據不同的條件生成不同的SQL語句
雖然在以前使用動態 SQL 並非一件易事,但正是 MyBatis 提供了可以被用在任意 SQL 映射語句中的強大的動態 SQL 語言得以改進這種情形。
動態 SQL 元素和 JSTL 或基於類似 XML 的文本處理器相似。在 MyBatis 之前的版本中,有很多元素需要花時間瞭解。MyBatis 3 大大精簡了元素種類,現在只需學習原來一半的元素便可。MyBatis 採用功能強大的基於 OGNL 的表達式來淘汰其它大部分元素。
學好動態sql其實就是學好幾個標籤,因爲創建工程的思路都是一樣的,模板化套路的編程,首先導包,寫全局配置文件,寫實體類,然後是寫實體類對應的接口和接口對應的xml文件,將接口對應的xml文件在全局配置中註冊,然後測試方法。
2.xml 標籤
動態SQL編程三個重要的部分:
- 實體類
- 實體類對應的接口
- 每一個接口對應的xml文件
2.1插入數據
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
2.導入依賴
<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>
3.全局配置文件
<?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="STDOUT_LOGGING"/>
<!--開啓駝峯自動命名-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!--包的別名配置-->
<typeAliases>
<package name="com.yang.pojo"/>
</typeAliases>
<!-- 一個environments 標籤元素可以有多套配置 -->
<environments default="development">
<!-- 裏面的每一個environment代表一個具體的環境 -->
<environment id="development">
<!--transactionManager 事務管理器 -->
<transactionManager type="JDBC"/>
<!-- dataSource 數據源配置 -->
<dataSource type="POOLED">
<!-- 連接數據庫的配置i-->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${pwd}"/>
</dataSource>
</environment>
</environments>
</configuration>
4.兩個資源配置文件
db.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8
username=root
pwd=123456
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
5.實體類和工具類
實體類
@Data
public class Blog {
private int id;
private String title;
private String author;
private Date createDate;
private int views;
}
工具類
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);
}
}
public class IDutils {
public static String getId(){
return UUID.randomUUID().toString().replaceAll("-","");
}
}
6.接口類:
public interface BolgMapper {
//寫方法來插入數據
int addBlog(Blog blog);
}
7.接口類對應的xml文件
<?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.yang.mapper.BolgMapper">
<insert id="addBlog" parameterType="Blog">
insert into mybatis.blog(id,title,author,create_time,views)
values (#{id},#{title},#{author},#{createDate},#{views})
</insert>
</mapper>
8.在Mybatis-config文件中進行對應綁定註冊
<mappers>
<mapper namespace="com.yang.mapper.BlogMapper">
</mapper>
9.測試
@Test
public void testAddBlog() {
SqlSession session = MyBatisUtils.getSession(true);
BolgMapper mapper = session.getMapper(BolgMapper.class);
Blog blog = new Blog();
blog.setId(IDUtils.getId());
blog.setCreateDate(new Date());
blog.setAuthor("ly");
blog.setTitle("mybatis");
blog.setViews(9999);
mapper.addBlog(blog);
Blog blog1 = new Blog();
blog1.setId(IDUtils.getId());
blog1.setCreateDate(new Date());
blog1.setAuthor("ly");
blog1.setTitle("mybatis");
blog1.setViews(9999);
mapper.addBlog(blog1);
Blog blog2 = new Blog();
blog2.setId(IDUtils.getId());
blog2.setCreateDate(new Date());
blog2.setAuthor("ly");
blog2.setTitle("mybatis");
blog2.setViews(9999);
mapper.addBlog(blog2);
Blog blog3 = new Blog();
blog3.setId(IDUtils.getId());
blog3.setCreateDate(new Date());
blog3.setAuthor("ly");
blog3.setTitle("mybatis");
blog3.setViews(9999);
mapper.addBlog(blog3);
Blog blog4 = new Blog();
blog4.setId(IDUtils.getId());
blog4.setCreateDate(new Date());
blog4.setAuthor("ly");
blog4.setTitle("mybatis");
blog4.setViews(9999);
mapper.addBlog(blog4);
}
每一個接口類都有一個對應的xml文件,而且需要注意的是xml文件需要配置在全局xml中
2.2 IF
在接口中寫方法,然後在對應的xml中設置“作者”和“標題”搜索。
方法:
List<Blog> getBlogByIf(Map map);
xml配置
<select id="getBlogIF" parameterType="map" resultType="blog">
select * from mybatis.blog where
<if test="title != null">
and title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</select>
測試:
@Test
public void testgetBlogByIf() {
SqlSession session = MyBatisUtils.getSession(true);
BolgMapper mapper = session.getMapper(BolgMapper.class);
HashMap<String, String> map = new HashMap<String, String>();
// map.put("title","mybatis");
map.put("author","ly");
List<Blog> list = mapper.getBlogByIf(map);
for (Blog blog : list) {
System.out.println(blog);
}
}
如果沒有傳入“title”,where後也沒有條件,那麼就會查詢失敗,反之若傳入了“title”,那麼就會對“title”一列進行模糊查找並返回 blog 結果。where title=?
也可以增加參數,“title”和“author”兩個參數進行搜索,會自動拼接兩個參數進行搜索where title=? and authour=?
如果我們需要拼接where後面的條件,又不希望客戶端傳遞的錯誤信息,需要更加智能的where標籤<where><where/>
,如果有後面的判斷語句,自動添加where後面的條件內容,如果後面語句開頭是and或者or,它可以自動去掉。
2.3 choose (when, otherwise)
準備工作不變,然後在接口中寫入方法,對應接口的xml中添加配置,測試
方法
List<Blog> queryBlogChoose(Map map);
xml配置
<select id="queryBlogChoose" parameterType="map" resultType="blog">
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>
測試:
//只想查詢作者爲ly的數據
@Test
public void testqueryBlogChoose() {
SqlSession session = MyBatisUtils.getSession(true);
BolgMapper mapper = session.getMapper(BolgMapper.class);
HashMap<String, String> map = new HashMap<String, String>();
map.put("author","ly");
List<Blog> list = mapper.queryBlogChoose(map);
for (Blog blog : list) {
System.out.println(blog);
}
}
有時不想用到所有的條件語句,而只想從中擇其一二。針對這種情況,MyBatis 提供了 choose 元素,它有點像 Java 中的 switch 語句。
思路是提供了"title"就按"title"查找,提供了"author"就按"author"查找,若兩者都沒有提供,就返回所有符合條件(兩者都不滿足會走otherwise,相當於Java-switch中的default)的結果.
2.4 trim (where,set)
準備工作不變,更新語句不用寫接口方法,直接配置對應接口的xml文件,測試
xml配置
select * from mybatis.blog
<where>
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</where>
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>
測試:
@Test
public void testupdateBlog(){
SqlSession session = MyBatisUtils.getSession(true);
BolgMapper mapper = session.getMapper(BolgMapper.class);
HashMap<String, String> map = new HashMap<String, String>();
map.put("id","680b9f77c78c4429be1a9b7992da54a9");
map.put("author","ly");
mapper.updateBlog(map);
}
where的前身是:
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
set的前身是:
<trim prefix="SET" suffixOverrides=",">
...
</trim>
更新語句:where 元素只會在至少有一個子元素的條件返回 SQL 子句的情況下才去插入“WHERE”子句。而且,若語句的開頭爲“AND”或“OR”,where 元素也會將它們去除。
2.4 Foreach
動態 SQL 的另外一個常用的操作需求是對一個集合進行遍歷,通常是在構建 IN 條件語句的時候,
foreach 元素的功能非常強大,它允許你指定一個集合,聲明可以在元素體內使用的集合項(item)和索引(index)變量。它也允許你指定開頭與結尾的字符串以及在迭代結果之間放置分隔符。
xml配置
<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>
<!--查詢當id=?時所有數據-->
<!--collection:集合類型;item:集合的變量名;
open循環以什麼開始;close循環以什麼結束,separator:以什麼分割
-->
測試:
@Test
public void testqueryBlogForeach() {
SqlSession session = MyBatisUtils.getSession(true);
BolgMapper mapper = session.getMapper(BolgMapper.class);
HashMap<String,Object> map = new HashMap<String,Object>();
ArrayList<String> list = new ArrayList<String>();
list.add("6ff5e719ea004feab2f08d35ac6dd2fe");
list.add("a79fe6f2fe96403e9a5349b2aaabded6");
list.add("680b9f77c78c4429be1a9b7992da54a9");
map.put("ids",list);
List<Blog> blogs = mapper.queryBlogForeach(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
}
2.5 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標籤