您的位置:首页 > 数据库

MyBatis代码实例系列-06:Mybatis动态SQL标签(一)---if、where、set、trim、choose

2018-02-03 17:43 1101 查看
超级通道:MyBatis代码实例系列-绪论

本章主要记录MyBatis动态SQL的标签,涉及到的知识点有:

1. MyBatis动态SQL标签:用于降低根据不同条件拼接 SQL 语句的难度。

2. MyJsonResult:通过泛型定义通用返回类型,实现复用。

本章介绍的动态SQL的标签有:if、where、set、trim、choose,下面对这些标签进行简单说明:

- if标签:用于处理SQL语句中where条件部分可以确定、部分不能确定的情况。

- where标签:用于处理SQL语句中where条件都不可以确定的情况。

- set:用于处理SQL(update)语句中,更新字段不能确定的情况。

- trim:提供了前缀和后缀的智能消除功能,可以用于替代where和set标签,其实有更加强大的用法。

- choose:一般与WHEN和OTHERWISE标签合作,用于处理多种条件只其一的情况,类似于switch。

1.业务场景与SQL

本次主要以书籍为业务对象进行各类实例操作,其SQL如下:

drop table book;

create table `book`(
`id` int(4) unsigned not null comment '书id',
`name` varchar(20) not null comment '书名',
`number` varchar(8) not null comment '书编号',
`price` int(3) not null comment '价格',
`old` int(1) not null comment '是否二手书',
`chinese` int(1) not null comment '是否汉化书',
primary key(id)
)engine=InnoDB comment='书籍' default charset=utf8;

select * from book;

insert into book values(1,'初中数学1','CZ-SX-01',12,1,1);
insert into book values(2,'初中数学2','CZ-SX-02',22,1,0);
insert into book values(3,'初中数学3','CZ-SX-03',22,0,1);
insert into book values(4,'初中数学4','CZ-SX-04',13,0,0);
insert into book values(5,'高中数学1','GZ-SX-01',33,0,0);
insert into book values(6,'高中数学2','GZ-SX-02',9,0,1);
insert into book values(7,'高中数学3','GZ-SX-03',7,1,0);
insert into book values(8,'高中数学4','GZ-SX-04',12,1,1);
insert into book values(9,'大学英语1','DX-YW-01',33,0,0);
insert into book values(10,'大学英语2','DX-YW-01',22,0,1);
insert into book values(11,'大学英语3','DX-YW-01',20,1,0);
insert into book values(12,'大学英语4','DX-YW-01',40,1,1);


2.目录结构

src
\---main
\---java
|   \---pers
|       \---hanchao
|           \---himybatis
|               |---dynamic
|                    \---Book.java
|                    |---IBookDAO.java
|                    |---BookController.java
|                    |---BookService.java
|                    |---MyJsonResult.java
\---resources
|   \---mybatis-mappers
|   |   \---Book.xml
|   |---log4j.properties
|   |---jdbc.properties
|   |---applicationContext.xml
|   |---spring-mybatis-jdbc.xml
\---webapp
\---dynamic
|   \---book.jsp
|---WEB-INF
|   \---web.xml
|   |---spring-mvc-servlet.xml
|---index.jsp


3.XML映射文件:Book.xml

数据库操作主要集中在XML映射文件中。

3.1.何为动态SQL–以if标签为例

场景:查询所有的旧书,查询条件可能有价格、是否汉化

常规SQL(#表示不确定)如下:

SELECT * FROM `book`
where old = 1
# AND price <= ''
# AND chinese = ''


其中后面两个条件是不确定的,因为无法确定参数中price和chinese是否有值。

手动拼接

如果不通过MyBatis等框架而是手动编程,可能就需要进行一系列的判断,然后根据不同的情况,拼接SQL语句。

在Java中,可能是这么编码的:

StringBuffer sb = new StringBuffer(" SELECT * FROM `book` where old = 1 ");
//如果price不为空,则追加price条件
if(null != book.getNumber()){//如果前端无值,则Integer转换成null
sb.append(" AND price <= ").append(book.getPrice()).append("");
}
//如果chinese不为空,则追加chinese条件
if ("".equals(book.getChinese())){//如果前端无值,则String转换成''
sb.append(" AND chinese = '").append(book.getPrice()).append("'");
}
String sql = sb.toString();


上述是最轻松的情形,还可能面对以下问题:

拼接语句时,忘了注意空格,导致出错。如:

sb.append("SELECT * FROM book").append("where old = 1");
//--->SELECT * FROM bookwhere old = 1


拼接语句时,忘了AND和OR,导致出错。如:

sb.append("SELECT * FROM book where").append(" AND old = 1");
//--->SELECT * FROM book where AND old = 1


拼接语句时,忘了逗号,导致出错。如:

sb.append("UPDATE book set ").append(" price = 1,").append(" chinese= 1,");
//--->UPDATE book set price = 1, chinese= 1,


其实还有可能遇到其他各类问题,这里不再多例举。

MyBatis动态拼接

通过MyBatis的动态SQL标签,可以自动的解决大部分问题。如上面的业务场景,只需要通过一个if标签即可解决。如下:

<!--查询所有的旧书,查询条件可能有价格、是否汉化-->
<select id="selectBookWitif" parameterType="Book" resultType="Book">
SELECT * FROM `book`
where old = 1
<if test="price != null">
AND price <= #{price}
</if>
<if test="chinese != null">
AND chinese = #{chinese}
</if>
</select>


动态SQL标签都会自动在拼接的SQL语句中间添加空格,避免错误。

下面依次介绍其他标签。

3.2.where标签

where标签用于解决所有的查询条件都不确定的情形。

业务情景:查询所有书籍,查询条件可能有价格、是否汉化

普通SQL(#表示不确定):

SELECT * FROM `book`
# where
# price >= #{price}
# AND chinese = #{chinese}


MyBatis动态SQL:

where标签会智能的判断是否插入where语句,并且自动消除第一个紧随其后的AND。

<!--查询所有书籍,查询条件可能有价格、是否汉化【where形式】-->
<select id="selectBookWithwhere" parameterType="Book" resultType="Book">
SELECT * FROM `book`
<where>
<if test="price != null">
price >= #{price}
</if>
<if test="chinese != null">
AND chinese = #{chinese}
</if>
</where>
</select>


注意:每个if标签都可以开头追加AND,不过建议第一个if不追加AND。

3.3.set标签

set标签用于解决所有的更新字段都不确定的情形。

业务情景:根据id修改一本书,可能的修改字段有价格、书籍名

普通SQL(#表示不确定):

UPDATE `book`
# set
# price = #{price},
# name = #{name}
where id = #{id}


MyBatis动态SQL:

set标签会智能的判断是否插入set语句,并且自动消除最后一个逗号。

<!--根据id修改一本书,可能的修改条件有价格、书籍名【set形式】-->
<update id="updateBookWithset" parameterType="Book">
UPDATE `book`
<set>
<if test="price != null">
price = #{price},
</if>
<if test="name != ''">
name = #{name}
</if>
</set>
<where>
id = #{id}
</where>
</update>


注意:每个if标签都可以在结尾追加逗号,不过建议最后一个不追加逗号。

3.4.trim标签

trim标签会智能的判断是否插入prefix或者suffix语句,并且自动消除第一个紧随其后的prefixOverrides或者最后一个suffixOverrides,能够用来替代where标签和set标签

3.4.1.替代where

业务情景:根据id修改一本书,可能的修改条件有价格、书籍名

trim标签会智能的判断是否插入prefix(“where”),并且自动消除第一个紧随其后的prefixOverrides(“AND|OR”)

<!--查询所有书籍,查询条件可能有价格、是否汉化【trim形式】-->
<select id="selectBookWithtrim" resultType="Book" parameterType="Book">
SELECT * FROM `book`
<trim prefix="where" prefixOverrides="AND|OR">
<if test="price != null">
price >= #{price}
</if>
<if test="chinese != null">
AND chinese = #{chinese}
</if>
</trim>
</select>


3.4.2.替代set

业务情景:根据id修改一本书,可能的修改条件有价格、书籍名

trim标签会智能的判断是否插入prefix(“set”),并且自动消除最后一个的suffixOverrides(“,”)

<!--根据id修改一本书,可能的修改条件有价格、书籍名【trim形式】-->
<update id="updateBookWithtrim" parameterType="Book">
UPDATE `book`
<trim prefix="set" suffixOverrides=",">
<if test="price != null">
price = #{price},
</if>
<if test="name != ''">
name = #{name}
</if>
</trim>
<where>
id = #{id}
</where>
</update>


3.5.choose标签

choose标签用于解决所有的查询条件或者更新字段只取其一的情形。

业务情景:查询书籍,如果查询条件有编号按编号查询,有书名就按书名查询,有价格按价格查询,都没有就查询旧书

普通SQL(#表示不确定):

SELECT * FROM `book`
# WHERE number = #{number}
# WHERE name = #{name}
# WHERE price = #{price}
# WHERE old = TRUE


MyBatis动态SQL:

choose标签依照书写顺序依次检查when条件,当有一个when满足时,不会再进行后续when条件的判断;但所有的when条件都不满足时,会进入otherwise进行处理。

<!--查询书籍,如果查询条件有编号按编号查询,有书名就按书名查询,有价格按价格查询,都没有就查询旧书-->
<select id="selectBookWithChoose" parameterType="Book" resultType="Book">
SELECT * FROM `book`
<choose>
<when test="number != ''">
WHERE number = #{number}
</when>
<when test="name != ''">
WHERE name = #{name}
</when>
<when test="price != null">
WHERE price = #{price}
</when>
<otherwise>
WHERE old = TRUE
</otherwise>
</choose>
</select>


完整的Book.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">
<!--MyBatis的分配置文件,分别对应每个实体,用来配置SQL操作及SQL语句-->
<!--namespace,定义这个映射的命名域,这里指向Dao层接口-->
<mapper namespace="pers.hanchao.himybatis.dynamic.IBookDAO">
<!--Integer的空值形式是null,String的空值形式是''-->

<!--根据id查询一本书-->
<select id="selectBookById" resultType="Book" parameterType="Integer">
SELECT * FROM `book` WHERE id = #{id}
</select>

<!--查询所有的旧书,查询条件可能有价格、是否汉化-->
<select id="selectBookWitIf" parameterType="Book" resultType="Book">
SELECT * FROM `book`
WHERE old = 1
<if test="price != null">
AND price <= #{price}
</if>
<if test="chinese != null">
AND chinese = #{chinese}
</if>
</select>

<!--查询所有书籍,查询条件可能有价格、是否汉化【where形式】-->
<select id="selectBookWithWhere" parameterType="Book" resultType="Book">
SELECT * FROM `book`
<where>
<if test="price != null">
price >= #{price}
</if>
<if test="chinese != null">
AND chinese = #{chinese}
</if>
</where>
</select>
<!--查询所有书籍,查询条件可能有价格、是否汉化【trim形式】-->
<select id="selectBookWithTrim" resultType="Book" parameterType="Book">
SELECT * FROM `book`
<trim prefix="WHERE" prefixOverrides="AND|OR">
<if test="price != null">
price >= #{price}
</if>
<if test="chinese != null">
AND chinese = #{chinese}
</if>
</trim>
</select>

<!--根据id修改一本书,可能的修改条件有价格、书籍名【set形式】-->
<update id="updateBookWithSet" parameterType="Book">
UPDATE `book`
<set>
<if test="price != null">
price = #{price},
</if>
<if test="name != ''">
name = #{name}
</if>
</set>
<where>
id = #{id}
</where>
</update>
<!--根据id修改一本书,可能的修改条件有价格、书籍名【trim形式】-->
<update id="updateBookWithTrim" parameterType="Book">
UPDATE `book`
<trim prefix="SET" suffixOverrides=",">
<if test="price != null">
price = #{price},
</if>
<if test="name != ''">
name = #{name}
</if>
</trim>
<where>
id = #{id}
</where>
</update>

<!--查询书籍,如果查询条件有编号按编号查询,有书名就按书名查询,有价格按价格查询,都没有就查询旧书--> <select id="selectBookWithChoose" parameterType="Book" resultType="Book"> SELECT * FROM `book` <choose> <when test="number != ''"> WHERE number = #{number} </when> <when test="name != ''"> WHERE name = #{name} </when> <when test="price != null"> WHERE price = #{price} </when> <otherwise> WHERE old = TRUE </otherwise> </choose> </select>
</mapper>


4.其他文件

4.1.实体类Book.java

package pers.hanchao.himybatis.dynamic;

/**
* <p>书籍</p>
* @author hanchao 2018/1/30 22:46
**/
public class Book {
/** 书籍id */
private Integer id;
/** 书籍名称 */
private String name;
/** 书籍编号 */
private String number;
/** 价钱 */
private Integer price;
/** 是否二手书 */
private Integer old;
/** 是否汉化书 */
private Integer chinese;

//constructor toString setter getter
}


4.2.业务控制类:BookController.java

package pers.hanchao.himybatis.dynamic;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* <p>MyBatis动态SQL:If,Where,Set,Trim,Choose,ForEach,Bind</p>
* @author hanchao 2018/1/31 22:46
**/
@RestController
public class BookController {
private static final Logger LOGGER = Logger.getLogger(BookController.class);

@Autowired
private BookService bookService;

/**
* <p>查询所有的旧书,查询条件可能有价格、是否汉化</p>
* @author hanchao 2018/1/31 22:52
**/
@PostMapping("/book/if")
public MyJsonResult<Book> selectBookWithIf(@RequestBody Book book){
StringBuffer sb = new StringBuffer(" SELECT * FROM `book` WHERE old = 1 ");
//如果price不为空,则追加price条件
if(null != book.getNumber()){//如果前端无值,则Integer转换成null
sb.append(" AND price <= ").append(book.getPrice()).append("");
}
//如果chinese不为空,则追加chinese条件
if ("".equals(book.getChinese())){//如果前端无值,则String转换成''
sb.append(" AND chinese = '").append(book.getPrice()).append("'");
}
String sql = sb.toString();

LOGGER.info(book);
List<Book> bookList = this.bookService.selectBookWithIf(book);
if (null == bookList || bookList.size() == 0){
return new MyJsonResult<Book>(2,"暂无记录!");
}else {
return new MyJsonResult<Book>(1,"success!",bookList);
}

}
/**
* <p>查询所有书籍,查询条件可能有价格、是否汉化【where形式】</p>
* @author hanchao 2018/1/31 22:53
**/
@PostMapping("/book/where")
public MyJsonResult<Book> selectBookWithWhere(@RequestBody Book book){
LOGGER.info(book);
List<Book> bookList = this.bookService.selectBookWithWhere(book);
if (null == bookList || bookList.size() == 0){
return new MyJsonResult<Book>(2,"暂无记录!");
}else {
return new MyJsonResult<Book>(1,"success!",bookList);
}
}

/**
* <p>查询所有书籍,查询条件可能有价格、是否汉化【trim形式】</p>
* @author hanchao 2018/1/31 22:54
**/
@PostMapping("/book/trim/where")
public MyJsonResult<Book> selectBookWithTrim(@RequestBody Book book){
LOGGER.info(book);
List<Book> bookList = this.bookService.selectBookWithTrim(book);
if (null == bookList || bookList.size() == 0){
return new MyJsonResult<Book>(2,"暂无记录!");
}else {
return new MyJsonResult<Book>(1,"success!",bookList);
}
}

/**
* <p>根据id修改一本书,可能的修改条件有价格、书籍名【set形式】</p>
* @author hanchao 2018/1/31 22:54
**/
@PostMapping("/book/set")
public MyJsonResult<Book> updateBookWithSet(@RequestBody Book book){
LOGGER.info(book);
//先查书籍是否存在
Book book1 = this.bookService.selectBookById(book.getId());
if (null == book1){
return new MyJsonResult<Book>(2,"此书不存在,无法修改!");
}else {
this.bookService.updateBookWithSet(book);
return new MyJsonResult<Book>();
}
}

/**
* <p>根据id修改一本书,可能的修改条件有价格、书籍名【trim形式】</p>
* @author hanchao 2018/1/31 22:54
**/
@PostMapping("/book/trim/set")
public MyJsonResult<Book> updateBookWithTrim(@RequestBody Book book){
LOGGER.info(book);
//先查书籍是否存在
Book book1 = this.bookService.selectBookById(book.getId());
if (null == book1){
return new MyJsonResult<Book>(2,"此书不存在,无法修改!");
}else {
this.bookService.updateBookWithTrim(book);
return new MyJsonResult<Book>();
}
}

/**
* <p>查询书籍,如果查询条件有编号按编号查询,有书名就按书名查询,有价格按价格查询,都没有就查询旧书</p>
* @author hanchao 2018/1/31 23:08
**/
@PostMapping("/book/choose")
public MyJsonResult<Book> updateBookWithChoose(@RequestBody Book book){
LOGGER.info(book);
List<Book> bookList = this.bookService.updateBookWithChoose(book);
if (null == bookList || bookList.size() == 0){
return new MyJsonResult<Book>(2,"暂无记录!");
}else {
return new MyJsonResult<Book>(1,"success!",bookList);
}
}
}


4.3.DAO层接口类:IBookDAO.java

package pers.hanchao.himybatis.dynamic;

import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Map;

/**
* <p>书籍类的Dao层接口</p>
* @author hanchao 2018/1/30 22:47
**/
@Repository
public interface IBookDAO {
/** 根据id查询一本书 */
Book selectBookById(Integer id);

/** 查询所有的旧书,查询条件可能有价格、是否汉化 */
List<Book> selectBookWitIf(Book book);

/** 查询所有书籍,查询条件可能有价格、是否汉化【where形式】 */
List<Book> selectBookWithWhere(Book book);

/** 查询所有书籍,查询条件可能有价格、是否汉化【trim形式】 */
List<Book> selectBookWithTrim(Book book);

/** 根据id修改一本书,可能的修改条件有价格、书籍名【set形式】 */
void updateBookWithSet(Book book);

/** 根据id修改一本书,可能的修改条件有价格、书籍名【trim形式】 */
void updateBookWithTrim(Book book);

/** 查询书籍,如果查询条件有编号按编号查询,有书名就按书名查询,有价格按价格查询,都没有就查询旧书 */
List<Book> selectBookWithChoose(Book book);
}


4.4.返回封装类:MyJsonResult.java

package pers.hanchao.himybatis.dynamic;

import java.util.List;

/**
* <p>Json返回值</p>
* @author hanchao 2018/1/31 22:44
**/
public class MyJsonResult<E> {
/** 状态码 */
private Integer code = 1;
/** 消息 */
private String message = "success!";
/** 数据 */
private List<E> data;

public void setCodeAndMessage(Integer code,String message){
this.code = code;
this.message = message;
}
//constructor toString setter getter
}


4.5.前台页面:book.jsp

<%--
Created by IntelliJ IDEA.
User: hanchao
Date: 2018/1/31
Time: 23:13
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>书籍管理</title>
<style type="text/css">
div{margin: 5px 5px; }
.div-left {float: left;margin: 5px 5px;border:1px solid #96c2f1; background:#eff7ff;}
.div-text-center {text-align:center; border:1px solid #96c2f1; background:#eff7ff;width: 300px;}
textarea{border:1px solid #9bdf70;background:#f0fbeb}
</style>
</head>
<body>
<div class="div-left div-text-center">
<textarea id="show" style="width: 300px;height: 575px;"></textarea>
</div>
<div class="div-left">
<div class="div-text-center">
<label>书籍主键:</label><input type="text" id="id" name="id"/><hr/>
<label>书籍编号:</label><input type="text" id="number" name="number"/><hr/>
<label>书籍名称:</label><input type="text" id="name" name="name"/><hr/>
<label>书籍价格:</label><input type="text" id="price" name="price"/><hr/>
<label>是否旧书:</label><input type="text" id="old" name="old"/><hr/>
<label>是否汉化:</label><input type="text" id="chinese" name="chinese"/><hr/>
</div>
<div>
<input type="button" value="selectBookWithIf" onclick="book('if')"/><hr/>
<input type="button" value="selectBookWithWhere" onclick="book('where')"/>
<input type="button" value="selectBookWithTrim" onclick="book('trim/where')"/><hr/>
<input type="button" value="selectBookWithChoose" onclick="book('choose')"/><hr/>
<input type="button" value="updateBookWithSet" onclick="book('set')"/>
<input type="button" value="updateBookWithTrim" onclick="book('trim/set')"/><hr/>
</div>
</div>
</body>
<script type="text/javascript" src="../static/jquery-3.2.1.min.js"></script>
<script type="text/javascript">
function book(url_suffix) {
$.ajax({
type:'POST',
url:'/book/' + url_suffix,
data:JSON.stringify({
id:$('#id').val(),
number:$('#number').val(),
name:$('#name').val(),
price:$('#price').val(),
old:$('#old').val(),
chinese:$('#chinese').val()
}),
contentType:'application/json;charset=utf-8',
success:function (data) {
console.log(data);
var html = "url:/book/" + url_suffix + "\nresult:\n" + JSON.stringify(data,null,4);
$("#show").html(html)
},
error:function (data) {
alert(data.method);
}
});
}
</script>
</html>


5.result

5.1.if标签

情景:查询所有的旧书,查询条件可能有价格、是否汉化



5.2.where标签与trim标签

情景:查询所有书籍,查询条件可能有价格、是否汉化



5.3.set标签与trim标签

情景:根据id修改一本书,可能的修改条件有价格、书籍名



5.4.choose标签

情景:查询书籍,如果查询条件有编号按编号查询,有书名就按书名查询,有价格按价格查询,都没有就查询旧书

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息