Mybatis 映射文件的 SQL 深入

1.1 概述

  • Mybatis 的映射文件中,有些时候业务逻辑复杂时,我们的 SQL是动态变化的
  • 把判断放在映射文件中,使用动态SQL标签:<if>、<where>、 <foreach>、<sql>、<include>

 

 1.2动态SQL之<if>标签

  • 我们根据实体类的不同取值,使用不同的 SQL 语句来进行查询。比如在 id 如果不为空时可以根据 id查询,如果 username 不同空时还要加入用户名作为条件。这种情况在我们的多条件组合查询中经常会碰到。

  1. 需求: 根据id或者名称查询
  2. dao查询方法
package com.sunny.dao;

import com.sunny.entity.QueryVo;
import com.sunny.entity.User;

import java.util.List;

/**
 * 用户数据访问接口
 */
public interface IUserDao {

    //根据id或username条件查询用户列表
    List<User> findByCondition(User user);

    //根据主键查询实体类对象
    User findById(int id);
}

3.接口映射 IUserDao.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">
<!--namespace名称空间,用于定义是哪个类的映射文件,这里需要写所映射接口的类全名-->
<mapper namespace="com.sunny.dao.IUserDao">
    <!--根据主键查询实体类对象-->
    <select id="findById" resultType="com.sunny.entity.User" parameterType="int">
        SELECT *  FROM USER WHERE id = #{id}
    </select>

        动态SQL
            <if> 条件判断标签
                test属性,用来判断的表达式,返回boolean类型
                    test=" id != 0"这里的id就是parameterType中User对象的id属性
                                   这里的id也是获取对象的id属性值
    <select id="findByCondition" resultType="com.sunny.entity.User">
        SELECT * FROM USER WHERE 1=1
        <if test="id != 0">
        AND id = #{id}
        </if>
        <if test="username != null and username != ''">
        and username like #{username}
        </if>
    </select>

</mapper>

 4.测试

import com.sunny.dao.IUserDao;
import com.sunny.entity.QueryVo;
import com.sunny.entity.User;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;


import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class UserDaoTest {

    InputStream in = null;
    SqlSession sqlSession = null;

    @Before
    public void before() throws IOException {
        //1.获取主配置文件的输入流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.创建数据库连接工厂构建类
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        //3.根据数据库连接工厂构建类创建数据库连接工厂
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(in);
        //4.根据数据库连接工厂获取数据库连接SqlSession对象
        sqlSession = sqlSessionFactory.openSession();
    }

    /**
     * 根据主键查询实体类对象
     */
    @Test
    public void findById(){
        IUserDao userDao = sqlSession.getMapper(IUserDao.class);
        System.out.println(userDao.findById(51));
    }

    /**
     * 根据id或username条件查询用户列表
     */
    @Test
    public  void findByCondition() throws IOException, ParseException {
        IUserDao userDao = sqlSession.getMapper(IUserDao.class);
        User user = new User();
        user.setUsername("%张%");
        user.setId(41);
        List<User> userList = userDao.findByCondition(user);
        userList.forEach(System.out::println);
    }

 
    
    @After
    public void after() throws IOException {
        //提交事务
        sqlSession.commit();
        //关闭资源
        sqlSession.close();
        in.close();
    }
}

5.测试结果


 1.3 动态SQL之<where>标签

  • 作用:通过<where>标签拼接where条件,简化where写法。
  <!--使用where标签优化上面的where-->
    <select id="findByCondition" resultType="com.sunny.entity.User">
        SELECT * FROM USER
            <where>
            <if test="id != 0">
            and id = #{id}
            </if>
            <if test="id != null and id !='' ">
            and username like #{username}
            </if>
        </where>
    </select>

1.4 动态标签之foreach标签

  1. 作用:遍历参数值。
  2. 查询需求:根据多个id查询。

    SELECT * FROM USER WHERE id=41 OR id=42

    SELECT * FROM USER WHERE id IN (41,42)
  3. 定义查询扩展对象,封装多个id值
package com.sunny.entity;

import java.util.List;

/**
 * 当前QueryVo支持User对象的所有属性查询以及扩展属性
 */
public class QueryVo extends User {

    //条件:多个id
    private List<Integer> ids;

    public List<Integer> getIds() {
        return ids;
    }

    public void setIds(List<Integer> ids) {
        this.ids = ids;
    }
}

 5.dao接口,映射

package com.sunny.dao;

import com.sunny.entity.QueryVo;
import com.sunny.entity.User;

import java.util.List;

/**
 * 用户数据访问接口
 */
public interface IUserDao {

    //根据多个id查询用户列表
    List<User> findByCondition2(QueryVo queryVo);
    
}
<?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">
<!--namespace名称空间,用于定义是哪个类的映射文件,这里需要写所映射接口的类全名-->
<mapper namespace="com.sunny.dao.IUserDao">
    <!--根据主键查询实体类对象-->
    <select id="findById" resultType="com.sunny.entity.User" parameterType="int">
        SELECT *  FROM USER WHERE id = #{id}
    </select>
    
    <!--根据多个id查询用户列表
        目标:使用mybatis动态标签foreach标签实现sql,select * from user where id in(41,43)
        foreach:用于循环遍历
        collection:用于遍历的集合,必须时输入类型的属性
        open:拼接SQL的开始部分
        close:拼接SQL的结束部分
        separator:循环拼接的分隔符号
        item:要遍历的元素(集合里面的每个元素名字)
        #{id}:占位符的值,里面id名字与item设置的名字保持一致
    -->
    <sql id="selectUser">
        SELECT * FROM USER
    </sql>
    <select id="findByCondition2" resultType="com.sunny.entity.User" parameterType="com.sunny.entity.QueryVo">
        <include refid="selectUser"></include>
        <where>
            <if test="ids!=null and ids.size()>0">
                <foreach collection="ids" open=" id IN (" separator="," close=")" item="id">
                    #{id}
                </foreach>
            </if>
        </where>
    </select>
    
</mapper>

 6.测试

import com.sunny.dao.IUserDao;
import com.sunny.entity.QueryVo;
import com.sunny.entity.User;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;


import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class UserDaoTest {

    InputStream in = null;
    SqlSession sqlSession = null;

    @Before
    public void before() throws IOException {
        //1.获取主配置文件的输入流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.创建数据库连接工厂构建类
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        //3.根据数据库连接工厂构建类创建数据库连接工厂
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(in);
        //4.根据数据库连接工厂获取数据库连接SqlSession对象
        sqlSession = sqlSessionFactory.openSession();
    }

    /**
     * 根据多个id查询用户列表
     * @throws IOException
     */
    @Test
    public  void findByCondition2() throws IOException, ParseException {
        IUserDao userDao = sqlSession.getMapper(IUserDao.class);
        QueryVo queryVO = new QueryVo();
        List<Integer> ids = new ArrayList<>();
        ids.add(42);
        ids.add(47);
        ids.add(45);
        ids.add(46);
        queryVO.setIds(ids);

        List<User> userList = userDao.findByCondition2(queryVO);
        userList.forEach(System.out::println);
    }
    
    @After
    public void after() throws IOException {
        //提交事务
        sqlSession.commit();
        //关闭资源
        sqlSession.close();
        in.close();
    }
}

1.5mybatis中简化编写的SQL片段

  • Sql 中可将重复的 sql 提取出来,使用时用 include 引用即可,最终达到 sql 重用的目的。我们先到 UserDao.xml 文件中使用<sql>标签,定义出公共部分。
<!--
    sql标签:定义SQL片段,抽取公用的SQL部分
    include标签: 用来引用sql片段
-->
<sql id="selectUser">
    SELECT * FROM USER WHERE 1=1
</sql>
<select id="findByCondition2" parameterType="queryvo" resultType="user">
    <!--引用SQL片段-->
    <include refid="selectUser"></include>
    <if test="ids != null and ids.size()>0">
        <foreach collection="ids" open="and id IN (" separator="," close=")" item="id">
            #{id}
        </foreach>
    </if>
</select>
  •  再通过<include>标签的 refid 属性的值就是<sql> 标签定义 id 的取值。

    注意:如果引用其它 mapper.xml 的 sql 片段,则在引用时需要加上 namespace,如下:
    <include refid="namespace.sql 片段”/>
     

猜你喜欢

转载自blog.csdn.net/Huangyuhua068/article/details/83152447
今日推荐