MyBatis还有一个强大特性就是它的动态SQL。在实际项目开发中,经常需要根据不同条件拼接SQL语句,拼接时还要确保不能忘了必要的空格,有时候还要注意省掉列名列表最后的逗号,等等。在使用JDBC 或其他类似持久层框架操作数据库时,处理这种情况是非常麻烦的,甚至可以用痛苦来形容,而在MyBatis中利用动态SQL这一特性可以很简单地解决这个问题。
动态SQL元素和使用JSTL或其他类似基于XML的文本处理器相似,MyBatis采用功能强大的基于OGNL的表达式来完成动态SQL。OGNL 的表达式可以被用在任意的SQL 映射语句中。
常用的动态SQL 元素包括:

  • if
  • choose (when、otherwise)
  • where
  • set
  • foreach
  • bind

下面我们就用一个简单示例来看看在MyBatis中怎么使用动态SQL。

首先,在数据库创建一个表tb_employee,并插入测试数据。SQL脚本如下:

CREATE TABLE tb_employee(
ID INT(11) PRIMARY KEY AUTO_INCREMENT,
loginname VARCHAR(18),
PASSWORD VARCHAR(18),
NAME VARCHAR(18) DEEAULT NULL,
SEX CHAR(2) DEEAULT NULL,
AGE INT(11) DEFAULT NULL,
phone VARCHAR(21),
sal DOUBLE,
state VARCHAR(18)
);
INSERT INTO tb_employee(loginname,PASSWORD,NAMB,sex,age,phone,sal,state
VALUES('jack','123456','马云",'男',26,'13900000001',9800,'ACTIVE');
INSERT INTO tb_employee (loginname,PASSWORD,NAMB,sex,age,phone,sal,state)
VALUES('rose','123456','黛西','女',21,'13900000002',6800,'ACTIVE');
INSERT INTO tb_employee (loginname,PASSWORD,NAME,sex,age,phone,sal,state)
VALUES('tom','123456','汤姆','男',25,'13900000003,8800,'ACTIVE');
INSERT INTO tb_employee (loginname,PASSWORD,NAME sex,age,phone,sal,state)
VALUES('alice','123456','爱丽丝','女’,20,'13900000004,5800,ACTIVE');

在数据库中执行SQL 脚本,完成创建数据库和表的操作。接下来,创建一个Employee对象映射tb_employee表。

public Class Employee implements Serializable {
// 主键id
private Integer id;
// 登录名
private String loginname;
// 密码
private String password;
// 真实姓名
private String name;
// 性别
private String sex;
// 年龄
private Integer age;
// 电话
private String phone;
// 薪水
private Double sal;
// 状态
private String state;
// 省略构造器和set/get方法......
}

if语句

动态SQL通常会做的事情是有条件地包含where子句的一部分。比如:

<mapper namespace="cn.mybatis.mapper.EmployeeMapper">
<select id="selectEmployeeByIdLike" resultTypem"cn.mybatis.domain.Employee">
SELECT * FROM tb_employee WHERE state = 'ACTIVE'
<!-- 可选条件,如果传进来的参数有id属性,则加上id查询条件-->
<if test="id != null">
and id = #{id}
</if>
</select>
</mapper>

以上语句提供了一个可选的根据id查找Employee的功能。如果没有传入id,那么所有处于“ACTIVE”状态的Employee都会被返回; 反之若传入了id,那么就会把查找id内容的Employee结果返回。

public Interface EmployeeMapper {
List<Employee> selectEmployeeByIdLike (HashMap<string,0bject> params);
}

以上代码提供了一个和EmployeeMapper.xml中的select元素的id同名的方法,需要注意的是,selectEmployeeByIdLike 接收一个HashMap 作为参数。在MyBatis中,#{id}表达式获取参数有两种方式:一是从HashMap 中获取集合中的property对象; 二是从javabean中获取property对象。

public static void main(string[] args) throws Exception {
// 读取mybatis-config.xml 文件
Inputstream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
// 初始化mybatis,创建SqlSessionFactory 类的实例
sqlsessionFactory sqlSessionEactory = new sqlsessionFactoryBullder().build(inputStream) ;
// 创建Session实例
sqlSesaion seasion = sqlsessionFactory.openSession();
DynamicsQlTest t = new DynamicSQITest();
t.testSelectEmployeeByIdLike(sesion);
// 提交事务
sesion.commit();
// 关闭Session
session.close();
}
// 测试<select id="selectEmployeeByIdLike"...>
public void testSelectemployeeByIdLike(Sqlsession session){ 
// 获得EmployeeMapper接口的代理对象
EmployeeMapper em = session.getMapper(EmployeeMapper.class) ;
// 创建一个HashMap存储参数
HashMap<String,Object> params = new HashMap<String,Object>() ;
// 设置id属性
params.put("id",1) ;
// 调用EmployeeMapper接口的selectEmployeeByIdLike方法
List<Employee> list = em.selectEmployeeByIdLike(params);
// 查看查询结果
list.foEach(employee->System.out.printIn(employee));
}

运行上面的main方法,其通过SqlSession的getMapper(Class<T> type)方法获得mapper接口的代理对象EmployeeMapper。调用selctEmployeeByIdLike方法时会执行EmployeeMapper.xm 中<select id="selctEmployeeByIdLike".../>元素中定义的sql语句。控制台显示如下:
DEBUG [main]==> Preparing: SELBCT * FRON tb_employee WHERE state = 'ACTIVE' and id= ?
DEBUG [main]==> Parameters:
DEBUG [main]<== Total:2
Employee [id=1,loginname=jack,password=123456,name=马云,sex=男,age=26,phone=13900000001,sal=9800.0,state=ACTIVE]
可以看到,执行的sql语句中因为传入了id属性,所以sql语句中包含了“and id= ?”,查询返回的Emplyee对象就是"id"为1的对象。
接下来注释以下代码:
// params.put("id",1);
再次执行main方法,控制台显示如下:
DEBUG [main]==> Breparing: SBLECT * FROM tb_employee WHERE state = 'ACTVE'
DBBUG [main]--> Parameters:
DEBUG [main]<== Total:4
DEBUG [main]<==
Employee [id=1,loginname=jack,password-123456,name=马云,sex=男,age=26,phone=13900000001,sal=9800.0,state=ACTIVE]
Employee [id=2,loginname=rose,password-123456,nanme=黛西,sex=女,age=21,phone=13900000002,sa1=6800.0,state=ACTIVE]
Employee [id-3,loginname=tom,password=123456,name=汤姆,sex=男,age=25,phone=13900000003,sa1=8800.0,state=ACTIVE]
Employee [id-4,loginname=alice,password=123456,name=爱丽丝,sex=女,age=20,phone=13900000004,sal=5800.0,state=ACTIVE]
可以看到,由于传递的HashMap中没有id属性,故执行的sql语句中不再包含“and id=?”,查询返回了所有state='ACTIVE'的数据。

如果想通过两个条件搜索该怎么办呢? 很简单,只要多加入一个条件即可。

<select id="selectEmgloyeeByLoginLike" resultType="cn.mybatis.domain.Employee">
SELECT * FROM tb_employee WHERE state = 'ACTIVE'
<!-- 两个可选条件,例如登录功能的登录名和密码查询-->
<if test="loginname != null and password != null">
and loginname = #{loginname} and password = #{password)
</if>
</select>
List<Employee> selectEmployeeByLoginLike(HashMap<String,Object> params) ;

publlc void testSelectEmployeeByLoginLike(SqlSession session){ 
EmployeeMapper em = session.getMapper(EmployeeMapper.class);
HashMap<String,Object> params = new HashMap<String,Object>();
// 设置loginname 和password属性
params.put("loginname","jack");
params.put("password","1245");
List<Employee> list = em.selectEmployeeByLoginlike(param);
list.forEach(employee->System.out.println(employee));
}

运行上面的main方法,测试selectEmployeeByLoginLike方法,控制台显示如下:
DBBUG [main]==> Preparing: SBLECT * EROM tb_employee WHERE state = 'ACTIVE' and loginname = ? and password = ?
DEBUG [main]==> Parameters: jack(String),123456(String)
DEBUG [main]==> Total :1
Employee [id=1,loginname=jack,password=123456,name=马云,sex=男,age=26,phone=13900000001,sal=9800.0,state=ACTIVE]

可以看到,执行的sql语句中因为传入了loginname和password属性,故sql语句中包含了“and loginname = ? and password = ? ”,查询返回的Emplyee对象就是logimame,即jack,并且password是123456的对象。

标签: none

添加新评论