0
点赞
收藏
分享

微信扫一扫

Mybatis二:动态SQL

深夜瞎琢磨 2022-01-05 阅读 45

文章目录

1 动态SQL

元素说明
<if>判断语句,用于单条件分支判断
<choose>
<when>
<otherwise>
相当于java中的 switch...case(break)...default,用于多条件分支判断。
当第一个when条件为真时,则只拼接第一个SQL, 否则就向下判断第二个条件是否为真,以此类推,
当所有的when条件都不为真时,才拼装otherwise中的SQL
<where>
<trim>
<set>
辅助元素,用于处理一些 SQL 拼装、特殊字符问题
<foreach>循环语句,常用语 IN 语句等列举条件中
<bind>从 OGNL 表达式中创建一个变量,并将其绑定到上下文,常用语模糊查询的 SQL 中
-- SQL脚本如下
CREATE TABLE `t_user` (
  `id` int(11) NOT NULL auto_increment,
  `username` varchar(32) NOT NULL COMMENT '用户名称',
  `birthday` datetime default NULL COMMENT '生日',
  `sex` char(1) default NULL COMMENT '性别',
  `address` varchar(256) default NULL COMMENT '地址',
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert  into `t_user`(`id`,`username`,`birthday`,`sex`,`address`) 
values  (41,'张三','2018-02-27 17:47:08','男','北京'),
	(42,'小明','2018-03-02 15:09:37','女','北京金燕龙'),
	(43,'小明','2018-03-04 11:34:34','女','北京金燕龙'),
	(45,'李四','2018-03-04 12:04:06','男','北京金燕龙'),
	(46,'王五','2018-03-07 17:37:26','男','北京'),
	(47,'王六','2018-03-07 17:37:26','男','洛阳'),
	(48,'赵六','2018-03-08 11:44:00','女','北京修正');
  1. 依赖

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.5</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.21</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>
    
  2. 配置

    1. log4j.properties

      # Set root category priority to INFO and its only appender to CONSOLE.
      #log4j.rootCategory=INFO, CONSOLE            debug   info   warn error fatal
      log4j.rootCategory=debug, CONSOLE, LOGFILE
      # Set the enterprise logger category to FATAL and its only appender to CONSOLE.
      log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
      # CONSOLE is set to be a ConsoleAppender using a PatternLayout.
      log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
      log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
      log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
      # LOGFILE is set to be a File appender using a PatternLayout.
      log4j.appender.LOGFILE=org.apache.log4j.FileAppender
      log4j.appender.LOGFILE.File=d:\axis.log
      log4j.appender.LOGFILE.Append=true
      log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
      log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n
      
    2. 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">
      <!-- mybatis的主配置文件 -->
      <configuration>
          <!-- 配置环境 -->
          <environments default="mysql">
              <!-- 配置mysql的环境-->
              <environment id="mysql">
                  <!-- 配置事务的类型-->
                  <transactionManager type="JDBC"></transactionManager>
                  <!-- 配置数据源(连接池) -->
                  <dataSource type="POOLED">
                      <!-- 配置连接数据库的4个基本信息 -->
                      <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                      <property name="url" value="jdbc:mysql://192.168.181.160:3306/test?serverTimezone=CTT"/>
                      <property name="username" value="root"/>
                      <property name="password" value="root"/>
                  </dataSource>
              </environment>
          </environments>
      
          <!-- 指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件 -->
          <mappers>
              <mapper resource="mapper/UserMapper.xml"/>
          </mappers>
      </configuration>
      
  3. 编码

    package cn.pojo;
    import lombok.Data;
    @Data
    public class User {
        /**
         * 加个 user 前缀,特意与数据库中的列不一样,测试 ResultMap
         * <p>
         * 不同的数据类型 ResultMap 映射结果如下
         * private Date userBirthday;    ==>  userBirthday=Tue Feb 27 17:47:08 CST 2018
         * private String userBirthday;  ==>  userBirthday=2018-02-27 17:47:08
         */
        private Integer userId;
        private String userName;
        // private Date userBirthday;
        private String userBirthday;
        private String userSex;
        private String userAddress;
    }
    
    package cn.pojo;
    import lombok.Data;
    @Data
    public class UserDto {
        /**
         * User的部分属性
         * 加入 t_user 表中有50个字段,那么User类就有与之对应的50个属性
         * 而大部分查询时只需要查其中的20个属性,因此可以把该20个属性单独定义成一个dto对象
         */
        private String userName;
        private String userSex;
        private String userAddress;
    }
    
    package cn.mapper;
    import cn.pojo.User;
    import cn.pojo.UserDto;
    import java.util.List;
    public interface UserMapper {
        // 查询所有用户,主要用于环境测试
        List<User> findAll();
    }
    
    <?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="cn.mapper.UserMapper">
        <select id="findAll" resultMap="userMap">
            select *
            from t_user
        </select>
        
        <!--在此配置userMap,后面的动态SQL均引用此处的userMap,后面不在重复写-->
        <resultMap id="userMap" type="cn.pojo.User">
            <id property="userId" column="id"/>
            <result property="userName" column="username"/>
            <result property="userBirthday" column="birthday"/>
            <result property="userAddress" column="address"/>
            <result property="userSex" column="sex"/>
        </resultMap>
    </mapper>
    
  4. 测试

    /**
     * 动态SQL测试类
     */
    public class TestDynamicSql {
    
        private InputStream inputStream = null;
        private SqlSession session = null;
        private UserMapper userMapper = null;
    
        @Before
        public void init() throws IOException {
            // 1.读取配置文件
            inputStream = Resources.getResourceAsStream("mybatis-config.xml");
            // 2.创建SqlSessionFactory工厂
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
            // 3.使用工厂生产SqlSession对象
            session = factory.openSession();
            // 4.使用SqlSession创建Dao接口的代理对象
            userMapper = session.getMapper(UserMapper.class);
            // 5.执行对象方法,即下面的各个测例
        }
    
        @After
        public void destroy() throws IOException {
            // 6.释放资源
            session.close();
            inputStream.close();
        }
    
        /**
         * findAll环境测试
         */
        @Test
        public void testEnv() {
            List<User> list = userMapper.findAll();
            for (User user : list) {
                System.out.println(user);
            }
            /**
             * 结果
             * User(userId=41, userName=张三, userBirthday=2018-02-27 17:47:08, userSex=男, userAddress=北京)
             * User(userId=42, userName=小明, userBirthday=2018-03-02 15:09:37, userSex=女, userAddress=北京金燕龙)
             * User(userId=43, userName=小明, userBirthday=2018-03-04 11:34:34, userSex=女, userAddress=北京金燕龙)
             * User(userId=45, userName=李四, userBirthday=2018-03-04 12:04:06, userSex=男, userAddress=北京金燕龙)
             * User(userId=46, userName=王五, userBirthday=2018-03-07 17:37:26, userSex=男, userAddress=北京)
             * User(userId=47, userName=王六, userBirthday=2018-03-07 17:37:26, userSex=男, userAddress=洛阳)
             * User(userId=48, userName=赵六, userBirthday=2018-03-08 11:44:00, userSex=女, userAddress=北京修正)
             */
        }
    }
    
  5. 项目结构图(源码见文末连接)

    在这里插入图片描述

1.1 if

  1. 接口

    /**
     * 需求:如果名字不为空,则根据名字查找用户
     * 如果地址也不为空,则根据名字和地址查找用户
     * 如果两者都为空,则查找全部用户
     * <p>
     * 用于多条件查询,比如书城买书时根据书名、作者等信息进行综合查找
     */
    List<User> findUserByUsernameAndAddress(User user);
    
  2. 实现

    <select id="findUserByUsernameAndAddress" parameterType="cn.pojo.User" resultMap="userMap">
        SELECT * FROM t_user WHERE 1=1
        <if test="userName != null and userName!='' ">
            and username like concat('%',#{userName},'%')
        </if>
        <if test="userAddress != null and userAddress!='' ">
            and address like concat('%',#{userAddress},'%')
        </if>
    </select>
    
  3. 测试

    /**
     * if标签测试
     * <p>
     * 需求:如果名字不为空,则根据名字查找用户
     * 如果地址也不为空,则根据名字和地址查找用户
     * 如果两者都为空,则查找全部用户
     * <p>
     * 用于多条件查询,比如书城买书时根据书名、作者等信息进行综合查找
     * <p>
     * 查找名字中含有"王"字,且住址是"北京"的人(可以注释条件观察控制台打印的sql语句)
     */
    @Test
    public void test1() {
        User user = new User();
        user.setUserName("王");
        user.setUserAddress("北京");
        List<User> users = userMapper.findUserByUsernameAndAddress(user);
        users.forEach(u -> System.out.println(u));
        /**
         * 结果
         * (1) 条件只有 user.setUserName("王");
         * User(userId=46, userName=王五, userBirthday=2018-03-07 17:37:26, userSex=男, userAddress=北京)
         * User(userId=47, userName=王六, userBirthday=2018-03-07 17:37:26, userSex=男, userAddress=洛阳)
         *
         * (2) 条件只有 user.setUserAddress("北京");
         * User(userId=41, userName=张三, userBirthday=2018-02-27 17:47:08, userSex=男, userAddress=北京)
         * User(userId=42, userName=小明, userBirthday=2018-03-02 15:09:37, userSex=女, userAddress=北京金燕龙)
         * User(userId=43, userName=小明, userBirthday=2018-03-04 11:34:34, userSex=女, userAddress=北京金燕龙)
         * User(userId=45, userName=李四, userBirthday=2018-03-04 12:04:06, userSex=男, userAddress=北京金燕龙)
         * User(userId=46, userName=王五, userBirthday=2018-03-07 17:37:26, userSex=男, userAddress=北京)
         * User(userId=48, userName=赵六, userBirthday=2018-03-08 11:44:00, userSex=女, userAddress=北京修正)
         *
         * (3) 条件有 user.setUserName("王"); 和 user.setUserAddress("北京");
         * User(userId=46, userName=王五, userBirthday=2018-03-07 17:37:26, userSex=男, userAddress=北京)
         *
         * (4) 无条件(即上面两个set赋值均注释掉)
         * 结果为所有用户、同上面 testEnv 测试结果,因此此处略
         */
    }
    

1.2 choose、when、otherwise

  1. 接口

    /**
     * 需求:当用户名不为空时,则只根据用户名进行筛选 (即优先级用户名大于地址)
     * 当用户名为空,而住址不为空时,则只根据住址进行筛选
     * 当用户名和住址都为空时,则筛选所有男性用户
     */
    List<User> findUserByUsernameOrAddress(User user);
    
  2. 实现

    <select id="findUserByUsernameOrAddress" parameterType="cn.pojo.User" resultMap="userMap">
        select * from t_user where 1=1
        <choose>
            <when test="userName != null and userName!=''">
                and username like concat('%',#{userName},'%')
            </when>
            <when test="userAddress != null and userAddress!=''">
                and address = #{userAddress}
            </when>
            <otherwise>
                and sex="男"
            </otherwise>
        </choose>
    </select>
    
  3. 测试

    /**
     * choose、when、otherwise标签测试
     * <p>
     * 需求:当用户名不为空时,则只根据用户名进行筛选 (即优先级用户名大于地址)
     * 当用户名为空,而住址不为空时,则只根据住址进行筛选
     * 当用户名和住址都为空时,则筛选所有男性用户
     */
    @Test
    public void test2() {
        User user = new User();
    //        user.setUserName("王");
    //        user.setUserAddress("北京");
        List<User> users = userMapper.findUserByUsernameOrAddress(user);
        users.forEach(u -> System.out.println(u));
        /**
         * 结果
         * (1) 条件只有 user.setUserName("王");
         * User(userId=46, userName=王五, userBirthday=2018-03-07 17:37:26, userSex=男, userAddress=北京)
         * User(userId=47, userName=王六, userBirthday=2018-03-07 17:37:26, userSex=男, userAddress=洛阳)
         *
         * (2) 条件只有 user.setUserAddress("北京");
         * User(userId=41, userName=张三, userBirthday=2018-02-27 17:47:08, userSex=男, userAddress=北京)
         * User(userId=46, userName=王五, userBirthday=2018-03-07 17:37:26, userSex=男, userAddress=北京)
         *
         * (3) 条件有 user.setUserName("王"); 和 user.setUserAddress("北京");
         * User(userId=46, userName=王五, userBirthday=2018-03-07 17:37:26, userSex=男, userAddress=北京)
         * User(userId=47, userName=王六, userBirthday=2018-03-07 17:37:26, userSex=男, userAddress=洛阳)
         *
         * (4) 无条件(即上面两个set赋值均注释掉)
         * User(userId=41, userName=张三, userBirthday=2018-02-27 17:47:08, userSex=男, userAddress=北京)
         * User(userId=45, userName=李四, userBirthday=2018-03-04 12:04:06, userSex=男, userAddress=北京金燕龙)
         * User(userId=46, userName=王五, userBirthday=2018-03-07 17:37:26, userSex=男, userAddress=北京)
         * User(userId=47, userName=王六, userBirthday=2018-03-07 17:37:26, userSex=男, userAddress=洛阳)
         */
    }
    

1.3 where、trim、set

  1. 接口

    /**
     * 用where标签替换 "select * from user where 1=1 "中的 " 1=1 " ,
     * 在前面的sql中若是没有 "1=1",导致where后直接拼接and,造成sql语法错误
     */
    List<User> findUserByWhere(User user);
    
    /**
     * 除了使用where外,还可以使用trim来制定需要的功能。
     * trim的功能是去除一些特殊的字符串。
     */
    List<User> findUserByTrim(User user);
    
    /**
     * 在更新的时候,大多数情况下都是更新的某一个或几个字段,
     * 若是将一条数据的所有属性都更新一遍,那么执行效率非常差,
     * 因此可以用mybatis中的set关键字解决此问题。
     */
    List<User> updateUserBySet(User user);
    
  2. 实现

    <select id="findUserByWhere" parameterType="cn.pojo.User" resultMap="userMap">
        SELECT * FROM t_user
        <where>
            <if test="userName != null and userName!='' ">
                and username like concat('%',#{userName},'%')
            </if>
        </where>
    </select>
    
    <!--
        trim元素的作用是去除一些特殊的字符串
        prefix:代表前缀。这里使用where来连接后面的sql片段。
        prefixOverrides:代表要去除的特殊字符串。这里定义了要去除sql中的and。
     -->
    <select id="findUserByTrim" parameterType="cn.pojo.User" resultMap="userMap">
        select * from t_user
        <trim prefix="where" prefixOverrides="and">
            <if test="userName != null and userName!='' ">
                and username like concat('%',#{userName},'%')
            </if>
        </trim>
    </select>
    
    <!--
        set元素主要用于更新操作,
        其主要作用是在动态包含的sql语句中输出一个set关键字,
        并将sql语句的最后一个多余的逗号删除。
    -->
    <select id="updateUserBySet" parameterType="cn.pojo.User">
        update t_user
        <set>
            <if test="userName != null and userName!='' ">
                username = #{userName} ,
            </if>
            <if test="userAddress != null and userAddress!=''">
                address = #{userAddress} ,
            </if>
        </set>
        where id=#{userId}
    </select>
    
  3. 测试

    /**
     * where标签测试
     * <p>
     * 用where标签替换 "select * from user where 1=1 "中的 " 1=1 " ,
     * 在前面的sql中若是没有 "1=1",导致where后直接拼接and,造成sql语法错误
     */
    @Test
    public void test3() {
        User user = new User();
        user.setUserName("王");
        List<User> users = userMapper.findUserByWhere(user);
        users.forEach(u -> System.out.println(u));
        /**
         * 无条件结果
         * 即查询所有用户,结果同testEnv,略
         *
         * 含条件 user.setUserName("王");
         * User(userId=46, userName=王五, userBirthday=2018-03-07 17:37:26, userSex=男, userAddress=北京)
         * User(userId=47, userName=王六, userBirthday=2018-03-07 17:37:26, userSex=男, userAddress=洛阳)
         */
    }
    
    /**
     * trim标签测试
     * <p>
     * 除了使用where外,还可以使用trim来制定需要的功能。
     * trim的功能是去除一些特殊的字符串。
     */
    @Test
    public void test4() {
        User user = new User();
        user.setUserName("王");
        List<User> users = userMapper.findUserByTrim(user);
        users.forEach(u -> System.out.println(u));
        /**
         * 无条件结果
         * 即查询所有用户,结果同testEnv,略
         *
         * 含条件 user.setUserName("王");
         * User(userId=46, userName=王五, userBirthday=2018-03-07 17:37:26, userSex=男, userAddress=北京)
         * User(userId=47, userName=王六, userBirthday=2018-03-07 17:37:26, userSex=男, userAddress=洛阳)
         */
    }
    
    /**
     * set标签测试
     * <p>
     * 在更新的时候,大多数情况下都是更新的某一个或几个字段,
     * 若是将一条数据的所有属性都更新一遍,那么执行效率非常差,
     * 因此可以用mybatis中的set关键字解决此问题。
     */
    @Test
    public void test5() {
        User user = new User();
        user.setUserId(43);
        user.setUserName("刘小邦");
        user.setUserAddress("咸阳");
        userMapper.updateUserBySet(user);
    }
    

1.4 foreach

  1. 接口

    /**
     * mybatis中提供了一种用于数组和集合循环遍历的方式。
     * foreach常和in连用。
     */
    List<User> findUsersByIds(List<Integer> ids);
    
  2. 实现

    <!-- item指定的即为循环元素的名称。如 for(String str: books) 中的str-->
    <select id="findUsersByIds" parameterType="List" resultMap="userMap">
        select * from t_user where id in
        <foreach collection="list" item="currentId" open="(" separator="," close=")">
            #{currentId}
        </foreach>
    </select>
    
  3. 测试

    /**
     * foreach标签测试
     * <p>
     * mybatis中提供了一种用于数组和集合循环遍历的方式。
     * foreach常和in连用。
     */
    @Test
    public void test6() {
        List<Integer> ids = Arrays.asList(41, 43, 46, 48);
        List<User> users = userMapper.findUsersByIds(ids);
        users.forEach(u -> System.out.println(u));
        /**
         * User(userId=41, userName=张三, userBirthday=2018-02-27 17:47:08, userSex=男, userAddress=北京)
         * User(userId=43, userName=刘小邦, userBirthday=2018-03-04 11:34:34, userSex=女, userAddress=咸阳)
         * User(userId=46, userName=王五, userBirthday=2018-03-07 17:37:26, userSex=男, userAddress=北京)
         * User(userId=48, userName=赵六, userBirthday=2018-03-08 11:44:00, userSex=女, userAddress=北京修正)
         */
    }
    

1.5 bind

  1. 接口

    /**
     * 在进行sql模糊查询的时候,如果使用"${}",则无法防止sql注入问题;
     * 如果使用concat()函数进行连接,则只针对mysql数据库有效;
     * 若是使用oracle数据库,则需要使用"||"
     * 因此,mybatis提供了bind标签来解决这个问题
     */
    List<User> findUserByBind(User user);
    
  2. 实现

    <select id="findUserByBind" parameterType="cn.pojo.User" resultMap="userMap">
        <bind name="bindUserName" value="'%' + userName + '%'"/>
        select * from t_user
        where username like #{bindUserName}
    </select>
    
  3. 测试

    /**
     * bind 标签测试
     * <p>
     * 在进行sql模糊查询的时候,如果使用"${}",则无法防止sql注入问题;
     * 如果使用concat()函数进行连接,则只针对mysql数据库有效;
     * 若是使用oracle数据库,则需要使用"||"
     * 因此,mybatis提供了bind标签来解决这个问题
     */
    @Test
    public void test7() {
        User user = new User();
        user.setUserName("六");
        List<User> users = userMapper.findUserByBind(user);
        users.forEach(u -> System.out.println(u));
        /**
         * User(userId=47, userName=王六, userBirthday=2018-03-07 17:37:26, userSex=男, userAddress=洛阳)
         * User(userId=48, userName=赵六, userBirthday=2018-03-08 11:44:00, userSex=女, userAddress=北京修正)
         */
    }
    

1.6 sql

  1. 接口

    /**
     * 可将重复的sql提取出来,使用时include标签引入,
     * 这样不仅可以使代码简洁,而且还能达到sql重用的目的。
     * <p>
     * 特别是一张表中字段非常多时,假设50个,好多查询的sql语句都只需要其中20个字段,
     * 那么此时sql标签就非常有用,把这经常用到的20个字段用sql标签定义用的时候引入即可
     */
    List<UserDto> findUserBySql();
    
  2. 实现

    <!--用include标签(单标签)进行引入,后面续写sql即可(eg. from user)-->
    <select id="findUserBySql" resultMap="userDtoMap">
        select
        <include refid="needColumn"/>
        from t_user
    </select>
    
    <!--定义查询时所需要的的列-->
    <sql id="needColumn">
        username,sex,address
    </sql>
    
    <resultMap id="userDtoMap" type="cn.pojo.UserDto">
        <result property="userName" column="username"/>
        <result property="userAddress" column="address"/>
        <result property="userSex" column="sex"/>
    </resultMap>
    
  3. 测试

    /**
     * sql标签测试
     * <p>
     * 可将重复的sql提取出来,使用时include标签引入,
     * 这样不仅可以使代码简洁,而且还能达到sql重用的目的。
     * <p>
     * 特别是一张表中字段非常多时,假设50个,好多查询的sql语句都只需要其中20个字段,
     * 那么此时sql标签就非常有用,把这经常用到的20个字段用sql标签定义用的时候引入即可
     */
    @Test
    public void test8() {
        List<UserDto> users = userMapper.findUserBySql();
        users.forEach(u -> System.out.println(u));
    
        /**
         * UserDto(userName=张三, userSex=男, userAddress=北京)
         * UserDto(userName=小明, userSex=女, userAddress=北京金燕龙)
         * UserDto(userName=刘小邦, userSex=女, userAddress=咸阳)
         * UserDto(userName=李四, userSex=男, userAddress=北京金燕龙)
         * UserDto(userName=王五, userSex=男, userAddress=北京)
         * UserDto(userName=王六, userSex=男, userAddress=洛阳)
         * UserDto(userName=赵六, userSex=女, userAddress=北京修正)
         */
    }
    
  • 源码链接:blogs-mybatis
举报

相关推荐

MyBatis 动态 SQL

Mybatis 动态SQL

MyBatis动态sql

【MyBatis】动态SQL

Mybatis—动态SQL

Mybatis动态SQL

0 条评论