1 前言
動態 SQL 是MyBatis 強大特性之一,主要包含 sql、if、choose、where、set、trim、foreach 等標籤,本文僅介紹前6個標籤的用法,對於 foreach 標籤的用法,將在下一個專題介紹。
- <sql>:定義 SQL 片段,通過 <sql id="sql_id"></sql> 定義SQL片段,<include refid="sql_id"/> 引用定義好的片段
- <if>:條件語句,用法:<if test="expression"></if>
- <choose>:單選,用法:<choose> <when test=""></when> <otherwise></otherwise> </choose>
- <where>:添加 where 關鍵字,去掉多餘的 and 和 or
- <set>:解決 SQL 語句中可能出現過多逗號的問題
- trim:截取並拼接,用法:<trim prefix="" suffix="" prefixOverrides="" suffixOverrides=""></trim>,prefix:在操作的 SQL 語句前加入某些內容,suffix:在操作的 SQL 語句後加入某些內容,prefixOverrides:把操作 SQL 語句前的某些內容去掉,suffixOverrides:把操作 SQL 語句後的某些內容去掉。
2 實驗環境
(1)導入 JAR 包
其中,前2個 jar 包下載地址見 → log4j-1.2.17.jar、 mybatis-3.4.1.jar,將 jar 包放入 lib 目錄下,並選中所有 jar 包,右鍵,選擇【Add to Build Path】。
(2) 工作目錄
注意:src 和 conf 目錄下的 com.mapper 包必須同名。
(3)配置文件
log4j.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
<param name="Encoding" value="UTF-8" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m (%F:%L)\n" />
</layout>
</appender>
<logger name="java.sql">
<level value="debug" />
</logger>
<logger name="org.apache.ibatis">
<level value="info" />
</logger>
<root>
<priority value="debug" />
<appender-ref ref="STDOUT" />
</root>
</log4j:configuration>
注意:log4j.xml文件名不能隨意更改。
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>
<!-- 設置或引入資源文件 -->
<properties resource="jdbc.properties"></properties>
<!-- 設置連接數據庫的環境,default用於設置默認使用的數據庫環境 -->
<environments default="mysql">
<!-- 設置某個具體的數據庫環境 -->
<environment id="mysql">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</dataSource>
</environment>
</environments>
<!-- 引入映射文件 -->
<mappers>
<package name="com.mapper"/>
</mappers>
</configuration>
jdbc.properties
# K = V
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/users
jdbc.username=root
jdbc.password=0.
3 案例分析
首先在 MySQL 中創建數據庫:users,再在此數據庫中創建表:students。
students 包含 sid(int)、sname(varchar)和 sex(varchar) 3個字段,其中,sid 設置了自增,students 表中數據如下:
首先介紹下公共的文件,主要包含 Student.java、StudentMapper.java,不同的是 StudentMapper.xml、Test.java,將在各節分別介紹。
Student.java
package com.bean;
public class Student {
private Integer sid;
private String sname;
private String sex;
public Student() {}
public Student(Integer sid, String sname, String sex) {
this.sid = sid;
this.sname = sname;
this.sex = sex;
}
public Integer getSid() {
return sid;
}
public void setSid(Integer sid) {
this.sid = sid;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Student [sid=" + sid + ", sname=" + sname + ", sex=" + sex + "]";
}
}
StudentMapper.java
package com.mapper;
import java.util.List;
import com.bean.Student;
public interface StudentMapper {
public Student getStudentById(Integer sid);
public List<Student> getStudents(Student student);
public void updateStudent(Student student);
}
3.1 <sql> 標籤
StudentMapper.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.mapper.StudentMapper">
<sql id="selectColumn">select sid,sname,sex from students</sql>
<!-- public Student getStudentById(Integer sid); -->
<select id="getStudentById" resultType="com.bean.Student">
<include refid="selectColumn"/> where sid=#{sid}
</select>
</mapper>
注意:<sql> 標籤標示一個 SQL 語句片段,通過 <include> 標籤使用標示的 SQL 語句片段。
Test.java
package com.test;
import java.io.IOException;
import java.io.InputStream;
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 com.bean.Student;
import com.mapper.StudentMapper;
public class Test {
public static void main(String[] args) throws IOException {
InputStream is=Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession=sqlSessionFactory.openSession(true); //自動提交事務
//getMapper:會通過動態代理動態生成StudentMapper的代理實現類
StudentMapper mapper=sqlSession.getMapper(StudentMapper.class);
Student student=mapper.getStudentById(1001);
System.out.println(student);
}
}
運行結果:
DEBUG 06-14 20:54:49,039 ==> Preparing: select sid,sname,sex from students where sid=?
Student [sid=1001, sname=張三, sex=1]
3.2 <if> 標籤
StudentMapper.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.mapper.StudentMapper">
<!-- public List<Student> getStudents(Student student); -->
<select id="getStudents" resultType="com.bean.Student">
select sid,sname,sex from students where
<if test="sid!=null">
sid=#{sid} and
</if>
<if test="sname!=null and sname!=''">
sname=#{sname} and
</if>
<if test="sex!=null and sex!='' and (sex==1 or sex==0)">
sex=#{sex}
</if>
</select>
</mapper>
注意:<if> 標籤中 test 屬性指定了標籤內語句塊能夠執行的條件。
Test.java
package com.test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
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 com.bean.Student;
import com.mapper.StudentMapper;
public class Test {
public static void main(String[] args) throws IOException {
InputStream is=Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession=sqlSessionFactory.openSession(true); //自動提交事務
//getMapper:會通過動態代理動態生成StudentMapper的代理實現類
StudentMapper mapper=sqlSession.getMapper(StudentMapper.class);
List<Student> students=mapper.getStudents(new Student(1001,"","1"));
System.out.println(students);
}
}
運行結果:
DEBUG 06-14 20:50:48,261 ==> Preparing: select sid,sname,sex from students where sid=? and sex=?
[Student [sid=1001, sname=張三, sex=1]]
可以看到:由於 sname="",where 後面只有 sid 和 sex 兩個查詢條件。
3.3 <choose> 標籤
MyBatis 中沒有 else if 和 else 標籤,而 if 標籤只有兩個分支,不能滿足用戶多分支條件查詢的需求,而 choose 標籤能夠解決這一問題,其內部只有第一個符合條件的語句塊會執行。
StudentMapper.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.mapper.StudentMapper">
<!-- public List<Student> getStudents(Student student); -->
<select id="getStudents" resultType="com.bean.Student">
select sid,sname,sex from students where
<choose>
<when test="sid!=null">
sid=#{sid}
</when>
<when test="sname!=null and sname!=''">
sname=#{sname}
</when>
<otherwise>
sex=#{sex}
</otherwise>
</choose>
</select>
</mapper>
注意:<choose> 標籤中最多隻有一個語句會執行,並且只有第一個符合條件的會執行;<when> 標籤中 test 屬性指定了標籤內語句塊能夠執行的條件;當 <when> 標籤內的條件都不滿足時,會執行 <otherwise> 標籤內語句,另外 <otherwise> 是非必須的,即可以省去。
Test.java
package com.test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
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 com.bean.Student;
import com.mapper.StudentMapper;
public class Test {
public static void main(String[] args) throws IOException {
InputStream is=Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession=sqlSessionFactory.openSession(true); //自動提交事務
//getMapper:會通過動態代理動態生成StudentMapper的代理實現類
StudentMapper mapper=sqlSession.getMapper(StudentMapper.class);
List<Student> students=mapper.getStudents(new Student(null,"李四",""));
System.out.println(students);
}
}
運行結果:
DEBUG 06-14 21:14:24,168 ==> Preparing: select sid,sname,sex from students where sname=?
[Student [sid=1002, sname=李四, sex=0]]
可以看到:where 後面只有 sname 這一個查詢條件。
3.4 <where> 標籤
3.2節 StudentMapper.xml 中部分代碼如下:
select sid,sname,sex from students where
<if test="sid!=null">
sid=#{sid} and
</if>
<if test="sname!=null and sname!=''">
sname=#{sname} and
</if>
<if test="sex!=null and sex!='' and (sex==1 or sex==0)">
sex=#{sex}
</if>
若最後一個條件不符合條件,則會在末尾多個 and,不符合 SQL 語法,因此會報錯。
方案一(添加條件【1=1】)
可以通過如下方法解決此問題:
select sid,sname,sex from students where 1=1
<if test="sid!=null">
and sid=#{sid}
</if>
<if test="sname!=null and sname!=''">
and sname=#{sname}
</if>
<if test="sex!=null and sex!='' and (sex==1 or sex==0)">
and sex=#{sex}
</if>
Test.java 同3.3節。
運行結果:
DEBUG 06-14 22:08:56,654 ==> Preparing: select sid,sname,sex from students where 1=1 and sname=?
[Student [sid=1002, sname=李四, sex=0]]
可以看到:where 後面多了個條件【1=1】。
方案二(使用 <where> 標籤)
StudentMapper.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.mapper.StudentMapper">
<!-- public List<Student> getStudents(Student student); -->
<select id="getStudents" resultType="com.bean.Student">
select sid,sname,sex from students
<where>
<if test="sid!=null">
and sid=#{sid}
</if>
<if test="sname!=null and sname!=''">
and sname=#{sname}
</if>
<if test="sex!=null and sex!='' and (sex==1 or sex==0)">
and sex=#{sex}
</if>
</where>
</select>
</mapper>
注意:<where> 標籤用於添加 where 關鍵字,並刪除第一個可能多餘的 and 和 or。
Test.java 同3.3節。
運行結果:
DEBUG 06-14 22:04:00,454 ==> Preparing: select sid,sname,sex from students WHERE sname=?
[Student [sid=1002, sname=李四, sex=0]]
可以看到:替換了多餘的 and。
3.5 <set> 標籤
StudentMapper.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.mapper.StudentMapper">
<!-- public void updateStudent(Student student); -->
<update id="updateStudent">
update students
<set>
<if test="sname!=null and sname!=''">
sname=#{sname},
</if>
<if test="sex!=null and sex!='' and (sex==1 or sex==0)">
sex=#{sex}
</if>
</set>
where sid=#{sid}
</update>
</mapper>
注意:<set> 標籤用於添加 set 關鍵字,並刪除最後一個可能多餘的逗號,如:以上代碼中,若只有第一個 <if> 執行,將會多一個逗號。
Test.java
package com.test;
import java.io.IOException;
import java.io.InputStream;
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 com.bean.Student;
import com.mapper.StudentMapper;
public class Test {
public static void main(String[] args) throws IOException {
InputStream is=Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(is);
SqlSession sqlSession=sqlSessionFactory.openSession(true); //自動提交事務
//getMapper:會通過動態代理動態生成StudentMapper的代理實現類
StudentMapper mapper=sqlSession.getMapper(StudentMapper.class);
mapper.updateStudent(new Student(1001,"張三三",""));
}
}
更新後數據庫中 students 表中數據如下:
控制檯輸出如下:
DEBUG 06-14 22:35:50,138 ==> Preparing: update students SET sname=? where sid=?
可以看到:where 後面去掉了多餘的逗號。
3.6 <trim> 標籤