MyBatis
动态SQL:
MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其他类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句有多么痛苦。拼接的时候要确保不能忘了必要的空格,还要注意省掉列名列表最后的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。
通常使用动态 SQL 不可能是独立的一部分,MyBatis 当然使用一种强大的动态 SQL 语言来改进这种情形,这种语言可以被用在任意的 SQL 映射语句中。
动态 SQL 元素和使用 JSTL 或其他类似基于 XML 的文本处理器相似。在 MyBatis 之前的版本中,有很多的元素需要来了解。MyBatis 3 大大提升了它们,现在用不到原先一半的元素就可以了。MyBatis 采用功能强大的基于 OGNL 的表达式来消除其他元素。
主要讲解:
- if
- when
- where
- set
- foreach
主配置文件
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" />
<!-- 配置别名 -->
<typeAliases>
<typeAlias alias="Dept" type="com.wm.mybatis.POJO.Dept"/>
</typeAliases>
<environments default="development">
<environment id="development" >
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driverClass}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!-- 配置的映射文件 -->
<mappers>
<mapper resource="mapper/DeptDynamicSQL.xml"/>
</mappers>
</configuration>
POJO
package com.wm.mybatis.POJO;
public class Dept {
private Integer id ;
private String name ;
private String address ;
public Dept(){}
public Dept(Integer id, String name, String address) {
this.id = id;
this.name = name;
this.address = address;
}
public Integer getId() {
System.out.println(id);
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
System.out.println(name);
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
System.out.println(address);
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Dept [id=" + id + ", name=" + name + ", address=" + address
+ "]";
}
}
数据库
表数据:
工具类
SessionManagerUtil.java
获取sqlsession 和 关闭 sqlsession
package com.wm.mybatis.util;
import java.io.IOException;
import java.io.Reader;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class SessionManagerUtil {
// 同一个线程 下 session 操作
private static ThreadLocal<SqlSession> threadLocal = new ThreadLocal<SqlSession>();
private static SqlSessionFactory sessionFactory = null;
// 静态加载块 加载配置文件
static{
try {
Reader config = Resources.getResourceAsReader("MyBatis-config.xml");
sessionFactory = new SqlSessionFactoryBuilder().build(config);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException();
}
}
// 防止直接new
private SessionManagerUtil(){}
// 获取session
public static SqlSession getSession(){
SqlSession sqlSession = threadLocal.get();
if (sqlSession == null) {
sqlSession = sessionFactory.openSession();
threadLocal.set(sqlSession);
}
return sqlSession;
}
/// 关闭session
public static void closeSession(){
SqlSession sqlSession = threadLocal.get();
if (sqlSession != null) {
sqlSession.close();
threadLocal.remove(); // 与当前线程 分离
}
}
}
IF
IF:动态 SQL 通常要做的事情是有条件地包含 where 子句的一部分。
配置映射文件:DeptDynamicSQL.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.wm.mybatis.dao.IDeptMapperDao">
<!-- 由于数据库表字段 和 JavaBean Dept类属性 不一致 所以 要配置 resultMap 来实现一一对应-->
<resultMap type="Dept" id="resultDept">
<result property="id" column="d_id" />
<result property="name" column="d_name" />
<result property="address" column="d_address" />
</resultMap>
<!-- 查询 使用if 动态查询 -->
<select id="getDeptUseIf" parameterType="map" resultMap="resultDept">
select * from base_55demo.demo_mawei_dept t where t.d_id = #{id}
<if test="name != null">
and t.d_name = #{name}
</if>
<if test="address != null">
and t.d_address = #{address}
</if>
</select>
</mapper>
DAO层
DeptDynamicSQLMapperDaoImpl.java
package com.wm.mybatis.dao;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.session.SqlSession;
import com.wm.mybatis.POJO.Dept;
import com.wm.mybatis.util.SessionManagerUtil;
public class DeptDynamicSQLMapperDaoImpl {
// 使用 If 实现 动态SQL 查询
public Dept getDeptUseIf(Integer id, String name , String address){
// 获取session连接
SqlSession session = SessionManagerUtil.getSession();
Map<String, Object> map = new HashMap<String, Object>();
map.put("id", id);
map.put("name", name);
map.put("address", address);
// 配置的映射文件的命名空间:IDeptMapperDao.class.getName()
Dept dept = session.selectOne(IDeptMapperDao.class.getName()+".getDeptUseIf", map);
// 关闭session连接
SessionManagerUtil.closeSession();
return dept ;
}
}
测试
TestDeptDynamicSQL.java
package com.wm.mybatis.Test;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import com.wm.mybatis.POJO.Dept;
import com.wm.mybatis.dao.DeptDynamicSQLMapperDaoImpl;
public class TestDeptDynamicSQL {
// 使用 IF 实现 动态SQL 查询
@Test
public void getDeptUseIf(){
DeptDynamicSQLMapperDaoImpl dao = new DeptDynamicSQLMapperDaoImpl();
Dept dept = dao.getDeptUseIf(1, "开发部", null);
System.out.println(dept);
}
}
测试结果
只要if条件中不为空的,则都会生成SQL。
when
choose、when、otherwise
有些时候,我们不想用到所有的条件语句,而只想从中择其一二。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
配置映射
<!-- 查询 使用 choose when otherwise 动态查询 -->
<select id="getDeptUseWhen" parameterType="map" resultMap="resultDept">
select * from base_55demo.demo_mawei_dept t where t.d_id = #{id}
<choose>
<when test="name != null">
and t.d_name = #{name}
</when>
<when test="address != null">
and t.d_address = #{address}
</when>
<otherwise>
and 1=1
</otherwise>
</choose>
</select>
DAO层
// 使用 when 实现 动态SQL查询
public Dept getDeptUseWhen(Integer id, String name , String address){
SqlSession session = SessionManagerUtil.getSession();
Map<String, Object> map = new HashMap<String, Object>();
map.put("id", id);
map.put("name", name);
map.put("address", address);
Dept dept = session.selectOne(IDeptMapperDao.class.getName()+".getDeptUseWhen", map);
SessionManagerUtil.closeSession();
return dept ;
}
测试
// 使用 when 实现 动态SQL 查询
@Test
public void getDeptUseWhen(){
DeptDynamicSQLMapperDaoImpl dao = new DeptDynamicSQLMapperDaoImpl();
Dept dept = dao.getDeptUseWhen(1, "开发部", "成都");
System.out.println(dept);
}
结果
when :只要有一个条件成立,则就生成SQL,后面的就不会生效。
where
我们有时候要判断所有的条件,则if、when都不适合了。
where 元素知道只有在一个以上的if条件有值的情况下才去插入“WHERE”子句。而且,若最后的内容是“AND”或“OR”开头的,where 元素也知道如何将他们去除。
配置映射
<!-- 查询 使用 where + if 动态查询 -->
<select id="getDeptUseWhere" parameterType="map" resultMap="resultDept">
select * from base_55demo.demo_mawei_dept t
<where>
<if test="id != null">
and t.d_id = #{id}
</if>
<if test="name != null">
and t.d_name = #{name}
</if>
<if test="address != null">
and t.d_address = #{address}
</if>
</where>
</select>
DAO层
// 使用 where 实现 动态SQL查询
public List<Dept> getDeptUseWhere(Integer id, String name , String address){
SqlSession session = SessionManagerUtil.getSession();
Map<String, Object> map = new HashMap<String, Object>();
map.put("id", id);
map.put("name", name);
map.put("address", address);
List<Dept> depts = session.selectList(IDeptMapperDao.class.getName()+".getDeptUseWhere", map);
SessionManagerUtil.closeSession();
return depts ;
}
测试
// 使用 where 实现 动态SQL 查询
@Test
public void getDeptUseWhere(){
DeptDynamicSQLMapperDaoImpl dao = new DeptDynamicSQLMapperDaoImpl();
List<Dept> depts = dao.getDeptUseWhere(null, null, null);
for (Dept dept : depts) {
System.out.println(dept);
}
}
结果
where 会去掉多余的 and\or ,如果一个条件都不满足,则会去掉where。
set
set 元素会动态前置 SET 关键字,同时也会消除无关的逗号,因为用了条件语句之后很可能就会在生成的赋值语句的后面留下这些逗号。
配置映射
<!-- 更新 使用 set 实现 动态SQL 修改 -->
<update id="updateDeptUseSet">
update base_55demo.demo_mawei_dept
<set>
<if test="name != null">d_name = #{name},</if>
<if test="address != null">d_address = #{address}</if>
</set>
where d_id = #{id}
</update>
DAO层
// 通过 set 标签 来实现 动态SQL更新修改
public void updateDeptUseSet(Dept dept) throws Exception{
SqlSession session = null;
try {
session = SessionManagerUtil.getSession();
int count = session.update(IDeptMapperDao.class.getName()+".updateDeptUseSet", dept);
System.out.println("更新了记录:" + count + " 条!");
session.commit();
} catch (Exception e) {
e.printStackTrace();
session.rollback();
throw e;
} finally{
SessionManagerUtil.closeSession();
}
}
测试
// 使用 set 实现动态SQL 更新修改
@Test
public void updateDeptUseSet() throws Exception{
DeptDynamicSQLMapperDaoImpl dao = new DeptDynamicSQLMapperDaoImpl();
dao.updateDeptUseSet(new Dept(31,null,"广州"));
}
结果
数据库表:
foreach
动态 SQL 的另外一个常用的必要操作是需要对一个集合进行遍历,通常是在构建 IN 条件语句的时候。
配置映射
<!-- 查询 部门 使用 foreach 遍历 集合 -->
<select id="getDeptUseForeach" resultMap="resultDept" parameterType="list">
select * from base_55demo.demo_mawei_dept t
where t.d_id in
<foreach collection="list" index="i" item="item"
open="(" separator="," close=")">
#{item}
</foreach>
</select>
DAO层
// 使用 foreach 实现 动态SQL查询
public List<Dept> getDeptUseForeach(ArrayList<Object> list){
SqlSession session = SessionManagerUtil.getSession();
List<Dept> depts = session.selectList(IDeptMapperDao.class.getName()+".getDeptUseForeach", list);
SessionManagerUtil.closeSession();
return depts ;
}
测试
// 使用 foreach 实现动态SQL 查询
@Test
public void getDeptUseForeach(){
DeptDynamicSQLMapperDaoImpl dao = new DeptDynamicSQLMapperDaoImpl();
ArrayList<Object> list = new ArrayList<Object>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
List<Dept> depts = dao.getDeptUseForeach(list);
for (Dept dept : depts) {
System.out.println(dept);
}
}
结果
注意:你可以将任何可迭代对象(如列表、集合等)和任何的字典或者数组对象传递给foreach作为集合参数。当使用可迭代对象或者数组时,index是当前迭代的次数,item的值是本次迭代获取的元素。当使用字典(或者Map.Entry对象的集合)时,index是键,item是值。