iBATIS - 动态SQL



动态SQL是iBATIS的一个非常强大的功能。有时您需要根据参数对象的狀態更改WHERE子句条件。在这种情况下,iBATIS提供了一组动态SQL标签,可用于映射语句中,以增强SQL的可重用性和灵活性。

所有逻辑都使用一些额外的标签放在.XML文件中。以下是一个示例,其中SELECT语句将以两种方式工作:

  • 如果传递了一个ID,则它将返回与该ID对应的所有记录。
  • 否则,它将返回所有员工ID设置为NULL的记录。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-2.dtd">

<sqlMap namespace="Employee">

   <select id="findByID" resultClass="Employee">
      SELECT * FROM EMPLOYEE
		
      <dynamic prepend="WHERE ">
         <isNull property="id">
            id IS NULL
         </isNull>
			
         <isNotNull property="id">
            id = #id#
         </isNotNull>
      </dynamic>
		
   </select>
</sqlMap>

您可以使用<isNotEmpty>标签检查条件,如下所示。此处,仅当传递的属性不为空时才会添加条件。

..................
<select id="findByID" resultClass="Employee">
   SELECT * FROM EMPLOYEE
	
   <dynamic prepend="WHERE ">
      <isNotEmpty property="id">
         id = #id#
      </isNotEmpty>
   </dynamic>
	
</select>
..................

如果您想要一个可以选择员工ID和/或姓名的查询,则您的SELECT语句如下所示:

..................
<select id="findByID" resultClass="Employee">
   SELECT * FROM EMPLOYEE
	
   <dynamic prepend="WHERE ">
      <isNotEmpty prepend="AND" property="id">
         id = #id#
      </isNotEmpty>
		
      <isNotEmpty prepend="OR" property="first_name">
         first_name = #first_name#
      </isNotEmpty>
   </dynamic>
</select>
..................

动态SQL示例

以下示例展示了如何使用动态SQL编写SELECT语句。假设我们在MySQL中拥有以下EMPLOYEE表:

CREATE TABLE EMPLOYEE (
   id INT NOT NULL auto_increment,
   first_name VARCHAR(20) default NULL,
   last_name  VARCHAR(20) default NULL,
   salary     INT  default NULL,
   PRIMARY KEY (id)
);

假设此表只有一条记录,如下所示:

mysql> select * from EMPLOYEE;
+----+------------+-----------+--------+
| id | first_name | last_name | salary |
+----+------------+-----------+--------+
|  1 | Zara       | Ali       |   5000 |
+----+------------+-----------+--------+
1 row in set (0.00 sec)

Employee POJO类

为了执行读取操作,让我们在Employee.java中创建一个Employee类,如下所示:

public class Employee {
   private int id;
   private String first_name; 
   private String last_name;   
   private int salary;  

   /* Define constructors for the Employee class. */
   public Employee() {}
  
   public Employee(String fname, String lname, int salary) {
      this.first_name = fname;
      this.last_name = lname;
      this.salary = salary;
   }

   /* Here are the method definitions */
   public int getId() {
      return id;
   }
	
   public String getFirstName() {
      return first_name;
   }
	
   public String getLastName() {
      return last_name;
   }
	
   public int getSalary() {
      return salary;
   }
	
} /* End of Employee */

Employee.xml文件

为了使用iBATIS定义SQL映射语句,我们将在Employee.xml中添加以下修改后的<select>标签,并在该标签定义内,我们将定义一个“id”,它将在IbatisReadDy.java中用于对数据库执行动态SQL SELECT查询。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-2.dtd">

<sqlMap namespace="Employee">
   <select id="findByID" resultClass="Employee">
      SELECT * FROM EMPLOYEE
	
      <dynamic prepend="WHERE ">
         <isNotNull property="id">
            id = #id#
         </isNotNull>
      </dynamic>
		
   </select>
</sqlMap>

以上SELECT语句将以两种方式工作:

  • 如果传递了一个ID,则它将返回与该ID对应的记录;否则,它将返回所有记录。

IbatisReadDy.java文件

此文件具有应用程序级逻辑,用于从Employee表读取条件记录:

import com.ibatis.common.resources.Resources;
import com.ibatis.sqlmap.client.SqlMapClient;
import com.ibatis.sqlmap.client.SqlMapClientBuilder;

import java.io.*;
import java.sql.SQLException;
import java.util.*;

public class IbatisReadDy{
   public static void main(String[] args) throws IOException,SQLException{
   
      Reader rd=Resources.getResourceAsReader("SqlMapConfig.xml");
      SqlMapClient smc=SqlMapClientBuilder.buildSqlMapClient(rd);

      /* This would read all records from the Employee table.*/
      System.out.println("Going to read records.....");
      Employee rec = new Employee();
      rec.setId(1);

      List <Employee> ems = (List<Employee>)  
         smc.queryForList("Employee.findByID", rec);
      Employee em = null;
		
      for (Employee e : ems) {
         System.out.print("  " + e.getId());
         System.out.print("  " + e.getFirstName());
         System.out.print("  " + e.getLastName());
         System.out.print("  " + e.getSalary());
         em = e; 
         System.out.println("");
      }    
      System.out.println("Records Read Successfully ");
   }
} 

编译和运行

以下是编译和运行上述软件的步骤。在继续编译和执行之前,请确保已正确设置PATH和CLASSPATH。

  • 创建如上所示的Employee.xml。
  • 创建如上所示的Employee.java并编译它。
  • 创建如上所示的IbatisReadDy.java并编译它。
  • 执行IbatisReadDy二进制文件以运行程序。

您将获得以下结果,并且将从EMPLOYEE表中读取一条记录。

Going to read records.....
   1  Zara  Ali  5000
Record Reads Successfully

尝试将null作为smc.queryForList("Employee.findByID", null)传递,运行以上示例。

iBATIS OGNL表达式

iBATIS提供了强大的基于OGNL的表达式,以消除大多数其他元素。

  • if语句
  • choose、when、otherwise语句
  • where语句
  • foreach语句

if语句

在动态SQL中最常见的事情是根据条件包含where子句的一部分。例如:

<select id="findActiveBlogWithTitleLike" parameterType="Blog" resultType="Blog">
   SELECT * FROM BLOG
   WHERE state = 'ACTIVE.
	
   <if test="title != null">
      AND title like #{title}
   </if>
	
</select>

此语句提供了一种可选的文本搜索类型功能。如果您不传递标题,则将返回所有活动的博客。但是,如果您确实传递了一个标题,它将查找具有给定like条件的标题。

您可以包含多个if条件,如下所示:

<select id="findActiveBlogWithTitleLike" parameterType="Blog" resultType="Blog">
   SELECT * FROM BLOG
   WHERE state = 'ACTIVE.
	
   <if test="title != null">
      AND title like #{title}
   </if>
	
   <if test="author != null">
      AND author like #{author}
   </if>
	
</select>

choose、when和otherwise语句

iBATIS提供了一个choose元素,它类似于Java的switch语句。它有助于在许多选项中只选择一个情况。

以下示例将仅根据提供的标题进行搜索,然后仅根据提供的作者进行搜索。如果两者都没有提供,则仅返回特色博客:

<select id="findActiveBlogWithTitleLike" parameterType="Blog" resultType="Blog">
   SELECT * FROM BLOG
   WHERE state = 'ACTIVE.
	
   <choose>
      <when test="title != null">
         AND title like #{title}
      </when>
		
      <when test="author != null and author.name != null">
         AND author like #{author}
      </when>
		
      <otherwise>
         AND featured = 1
      </otherwise>
   </choose>
	
</select>

where语句

查看我们之前的示例,了解如果未满足任何条件会发生什么。您最终将得到如下所示的SQL:

SELECT * FROM BLOG
WHERE

这将失败,但iBATIS有一个简单的解决方案,只需一个简单的更改,一切正常:

<select id="findActiveBlogLike" parameterType="Blog" resultType="Blog">
   SELECT * FROM BLOG
	
   <where>
      <if test="state != null">
         state = #{state}
      </if>
		
      <if test="title != null">
         AND title like #{title}
      </if>
		
      <if test="author != null>
         AND author like #{author}
      </if>
   </where>
	
</select>

where元素仅在包含的标签返回任何内容时插入WHERE。此外,如果该内容以ANDOR开头,它就知道将其去除。

foreach语句

foreach元素允许您指定一个集合并声明可以在元素主体内部使用的项和索引变量。

它还允许您指定开始和结束字符串,并在迭代之间添加分隔符。您可以构建一个IN条件,如下所示:

<select id="selectPostIn" resultType="domain.blog.Post">
   SELECT *
   FROM POST P
   WHERE ID in
	
   <foreach item="item" index="index" collection="list"
      open="(" separator="," close=")">
      #{item}
   </foreach>
	
</select>
广告