動態SQL
動態 SQL,主要用於解決查詢條件不確定的情況:在程序運行期間,根據用戶提交的查詢條件進行查詢。提交的查詢條件不同,執行的 SQL 語句不同。若將每種可能的情況均逐一列出,對所有條件進行排列組合,將會出現大量的 SQL 語句。此時,可使用動態 SQL 來解決這樣的問題。
動態 SQL,即通過 MyBatis 提供的各種標籤對條件作出判斷以實現動態拼接 SQL 語句。這裏的條件判斷使用的表達式爲 OGNL 表達式。 常用的動態 SQL 標籤有<if/>、 <where/>、<choose/>、<foreach/>等。
MyBatis 的動態 SQL 語句,與 JSTL 中的語句非常相似。
項目實例
(1) 定義數據庫表
(2) 定義實體
public class Student {
private Integer id;
private String name;
private int age;
private double score;
}
(3) 創建工具類
public class MyBatisUtils {
private static SqlSessionFactory sqlSessionFactory;
public static SqlSession getSqlSession() {
try {
InputStream is = Resources.getResourceAsStream("mybatis.xml");
if (sqlSessionFactory == null) {
sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
}
return sqlSessionFactory.openSession();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
(4)定義測試類
定義 @Before 與 @After 方法
public class MyTest {
private IStudentDao dao;
private SqlSession sqlSession;
@Before
public void setUp() {
sqlSession = MyBatisUtils.getSqlSession();
dao = sqlSession.getMapper(IStudentDao.class);
}
@After
public void tearDown() {
if (sqlSession != null) {
sqlSession.close();
}
}
}
注意:
在 mapper 的動態 SQL 中若出現大於號(>)、小於號(<)、大於等於號(>=),小於等於號(<=)等符號,最好將其轉換爲實體符號。否則, XML 可能會出現解析出錯問題。特別是對於小於號(<),在 XML 中是絕對不能出現的。否則,一定出錯。
<if/>標籤
對於該標籤的執行,當 test 的值爲 true 時,會將其包含的 SQL 片斷拼接到其所在的 SQL語句中。這引發的問題是,查詢條件不確定,查詢條件依賴於用戶提交的內容。此時,就可使用動態 SQL 語句,根據用戶提交內容對將要執行的 SQL 進行拼接。
(1)定義 Dao 接口
public interface IStudentDao {
List<Student> selectStudentsByIf(Student student);
}
(2)定義映射文件
爲了解決兩個條件均未做設定的情況,在 where 後添加了一個“1=1”的條件。這樣就不至於兩個條件均未設定而出現只剩下一個 where,而沒有任何可拼接的條件的不完整 SQL 語句。
<!-- if標籤 -->
<select id="selectStudentsByIf" resultType="Student">
select id,name,age,score
from student
where 1 = 1
<if test="name != null and name != ''">
and name like '%' #{name} '%'
</if>
<if test="age > 0">
and age > #{age}
</if>
</select>
(3)修改測試類
@Test
public void test01() {
Student stu = new Student("張", 18, 0);
// Student stu = new Student("", 23, 0);
// Student stu = new Student("", 0, 0);
List<Student> students = dao.selectStudentsByIf(stu);
for (Student student : students) {
System.out.println(student);
}
}
(4)運行結果
<where/>標籤
<if/>標籤的中存在一個比較麻煩的地方:需要在 where 後手工添加 1=1 的子句。因爲,
若 where 後的所有<if/>條件均爲 false,而 where 後若又沒有 1=1 子句,則 SQL 中就會只剩下一個空的 where, SQL 出錯。所以,在 where 後,需要添加永爲真子句 1=1,以防止這種情況的發生。但當數據量很大時,會嚴重影響查詢效率。
(1)修改 Dao 接口
List<Student> selectStudentsByWhere(Student student);
(2)定義映射文件
<!-- where標籤 -->
<select id="selectStudentsByWhere" resultType="Student">
select id,name,age,score
from student
<where>
<if test="name != null and name != ''">
and name like '%' #{name} '%'
</if>
<if test="age > 0">
and age > #{age}
</if>
</where>
</select>
(3)修改測試類
@Test
public void test02() {
Student stu = new Student("張", 18, 0);
// Student stu = new Student("", 23, 0);
// Student stu = new Student("", 0, 0);
List<Student> students = dao.selectStudentsByWhere(stu);
for (Student student : students) {
System.out.println(student);
}
}
(4)運行結果
<choose/>標籤
該標籤中只可以包含<when/><otherwise/>,可以包含多個與一個<otherwise/>。它們聯合使用,完成 Java 中的開關語句 switch…case 功能。
(1)修改 Dao 接口
List<Student> selectStudentsByChoose(Student student);
(2)定義映射文件
<!-- choose標籤 -->
<select id="selectStudentsByChoose" resultType="Student">
select id,name,age,score
from student
<where>
<choose>
<when test="name != null and name !=''">
and name like '%' #{name} '%'
</when>
<when test="age > 0">
and age > #{age}
</when>
<otherwise>
1 = 2
</otherwise>
</choose>
</where>
</select>
(3)修改測試類
@Test
public void test03() {
// Student stu = new Student("張", 18, 0);
Student stu = new Student("", 23, 0);
// Student stu = new Student("", 0, 0);
List<Student> students = dao.selectStudentsByChoose(stu);
for (Student student : students) {
System.out.println(student);
}
}
<foreach/>標籤
<foreach/>標籤用於實現對於數組與集合的遍歷。對其使用,需要注意:
- collection 表示要遍歷的集合類型,這裏是數組,即 array。
- open、 close、 separator 爲對遍歷內容的 SQL 拼接。
(1)修改 Dao 接口
List<Student> selectStudentsByForeach(int[] ids);
List<Student> selectStudentsByForeach2(List<Integer> ids);
List<Student> selectStudentsByForeach3(List<Student> ids);
(2)定義映射文件
<!-- foreach標籤 ,遍歷數組-->
<select id="selectStudentsByForeach" resultType="Student">
<!-- select id,name,age,score from student where id in (1,3,5) -->
select id,name,age,score
from student
<if test="array.length > 0">
where id in
<foreach collection="array" item="myid" open="(" close=")" separator=",">
#{myid}
</foreach>
</if>
</select>
<!-- foreach標籤 ,遍歷泛型爲基本類型的 List-->
<select id="selectStudentsByForeach2" resultType="Student">
<!-- select id,name,age,score from student where id in (1,3,5) -->
select id,name,age,score
from student
<if test="list.size > 0">
where id in
<foreach collection="list" item="myid" open="(" close=")" separator=",">
#{myid}
</foreach>
</if>
</select>
<!-- foreach標籤遍歷泛型爲自定義類型的 List -->
<select id="selectStudentsByForeach3" resultType="Student">
<!-- select id,name,age,score from student where id in (1,3,5) -->
select id,name,age,score
from student
<if test="list.size > 0">
where id in
<foreach collection="list" item="stu" open="(" close=")" separator=",">
#{stu.id}
</foreach>
</if>
</select>
(3)修改測試類
@Test
public void test04() {
int[] ids = {1, 3, 4};
List<Student> students = dao.selectStudentsByForeach(ids);
for (Student student : students) {
System.out.println(student);
}
}
@Test
public void test05() {
List<Integer> ids = new ArrayList<>();
ids.add(1);
ids.add(3);
List<Student> students = dao.selectStudentsByForeach2(ids);
for (Student student : students) {
System.out.println(student);
}
}
@Test
public void test06() {
Student stu1 = new Student();
stu1.setId(1);
Student stu2 = new Student();
stu2.setId(3);
List<Student> stus = new ArrayList<>();
stus.add(stu1);
stus.add(stu2);
List<Student> students = dao.selectStudentsByForeach3(stus);
for (Student student : students) {
System.out.println(student);
}
}
<sql/>標籤
<sql/>標籤用於定義 SQL 片斷,以便其它 SQL 標籤複用。而其它標籤使用該 SQL 片斷,
需要使用<include/>子標籤。 該<sql/>標籤可以定義 SQL 語句中的任何部分,所以<include/>子標籤可以放在動態 SQL 的任何位置。
(1)修改 Dao 接口
List<Student> selectStudentsBySqlFragment(List<Student> ids);
(2)定義映射文件
<!-- sql標籤 -->
<select id="selectStudentsBySqlFragment" resultType="Student">
<!-- select id,name,age,score from student where id in (1,3,5) -->
select <include refid="selectColumns"/>
from student
<if test="list.size > 0">
where id in
<foreach collection="list" item="stu" open="(" close=")" separator=",">
#{stu.id}
</foreach>
</if>
</select>
<sql id="selectColumns">
id,name,age,score
</sql>
(3)修改測試類
@Test
public void test07() {
Student stu1 = new Student();
stu1.setId(1);
Student stu2 = new Student();
stu2.setId(3);
List<Student> stus = new ArrayList<>();
stus.add(stu1);
stus.add(stu2);
List<Student> students = dao.selectStudentsBySqlFragment(stus);
for (Student student : students) {
System.out.println(student);
}
}
(4)運行結果