浅谈MyBatis 之 动态SQL (三)

MyBatis


动态SQL:

MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其他类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句有多么痛苦。拼接的时候要确保不能忘了必要的空格,还要注意省掉列名列表最后的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。

通常使用动态 SQL 不可能是独立的一部分,MyBatis 当然使用一种强大的动态 SQL 语言来改进这种情形,这种语言可以被用在任意的 SQL 映射语句中。

动态 SQL 元素和使用 JSTL 或其他类似基于 XML 的文本处理器相似。在 MyBatis 之前的版本中,有很多的元素需要来了解。MyBatis 3 大大提升了它们,现在用不到原先一半的元素就可以了。MyBatis 采用功能强大的基于 OGNL 的表达式来消除其他元素。

具体查看官网:mybatis-动态SQL


主要讲解:

  • 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是值。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天涯共明月

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值