MyBatis dynamic SQL (take a look carefully, writing SQL will be much better in the future)

Java technology stack

www.javastack.cn

Follow to read more quality articles

One of the favorite features of MyBatis is dynamic SQL. In the process of using JDBC, splicing SQL based on conditions is very troublesome and error-prone.

The emergence of MyBatis dynamic SQL solves this trouble. MyBatis uses OGNL to use dynamic SQL.

Currently, dynamic SQL supports the following types of tags:

element effect Remarks
if Judge sentences Single conditional branch
choose(when、otherwise) Equivalent to if else in Java Multiple conditional branches
trim(where、set) Auxiliary element Used to deal with SQL splicing issues
foreach loop statement Bulk inserts, updates, and queries are often used
bind Create a variable and bind it to the context Used to be compatible with different databases, prevent SQL injection, etc.

1 Data preparation

For the following demonstration, a Maven project mybatis-dynamic was created, and the corresponding database and table were created

DROP TABLE IF EXISTS `student`;CREATE TABLE `student` (
  `student_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '编号',
  `name` varchar(20) DEFAULT NULL COMMENT '姓名',`phone` varchar(20) DEFAULT NULL COMMENT '电话',
  `email` varchar(50) DEFAULT NULL COMMENT '邮箱',
  `sex` tinyint(4) DEFAULT NULL COMMENT '性别',
  `locked` tinyint(4) DEFAULT NULL COMMENT '状态(0:正常,1:锁定)',
  `gmt_created` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '存入数据库的时间',
  `gmt_modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改的时间',
  `delete` int(11) DEFAULT NULL,
  PRIMARY KEY (`student_id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='学生表';

Corresponding project structure

2 if tag

The if tag is the one we use most often. It is likely to be used when querying, deleting, and updating. Must be used in conjunction with the test attribute.

2.1 Use the if tag in the WHERE condition

This is a common phenomenon. There may be many situations when we perform a conditional query.

2.1.1 Query conditions

Conditional search based on the entered student information

  1. When only the user name is input, the user name is used for fuzzy search;

  2. When only entering gender, use gender for exact match

  3. When both the user name and gender exist, use these two conditions for query matching query

2.1.2 Dynamic SQL

Interface function

    /**
 * 根据输入的学生信息进行条件检索
 * 1. 当只输入用户名时, 使用用户名进行模糊检索;
 * 2. 当只输入邮箱时, 使用性别进行完全匹配
 * 3. 当用户名和性别都存在时, 用这两个条件进行查询匹配的用
 * @param student
 * @return
 */
List<Student> selectByStudentSelective(Student student);

Corresponding dynamic SQL

  <select id="selectByStudentSelective" resultMap="BaseResultMap" parameterType="com.homejim.mybatis.entity.Student">
    select
    <include refid="Base_Column_List" />
    from student
    where 1=1
    <if test="name != null and name !=''">
      and name like concat('%', #{name}, '%')
    </if>
    <if test="sex != null">
      and sex=#{sex}
    </if>
  </select>

In this SQL statement, where 1=1 is a small technique for splicing multiple conditions, and you can use and for all subsequent conditional queries.

At the same time, we added an if tag to handle dynamic SQL

    <if test="name != null and name !=''">
      and name like concat('%', #{name}, '%')
    </if>
    <if test="sex != null">
      and sex=#{sex}
    </if>

The value of the test attribute of this if tag is an expression that conforms to OGNL, and the expression can be true or false. If the expression returns a numeric value, 0 is false, and non-zero is true;

2.1.3 Test

      @Test
    public void selectByStudent() {
        SqlSession sqlSession = null;
        sqlSession = sqlSessionFactory.openSession();
        StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
		Student search = new Student();
        search.setName("明");
		System.out.println("只有名字时的查询");
        List<Student> studentsByName = studentMapper.selectByStudentSelective(search);
        for (int i = 0; i < studentsByName.size(); i++) {
            System.out.println(ToStringBuilder.reflectionToString(studentsByName.get(i), ToStringStyle.MULTI_LINE_STYLE));
        }
		search.setName(null);
        search.setSex((byte) 1);
        System.out.println("只有性别时的查询");
        List<Student> studentsBySex = studentMapper.selectByStudentSelective(search);
        for (int i = 0; i < studentsBySex.size(); i++) {
            System.out.println(ToStringBuilder.reflectionToString(studentsBySex.get(i), ToStringStyle.MULTI_LINE_STYLE));
        }
		System.out.println("姓名和性别同时存在的查询");
        search.setName("明");
        List<Student> studentsByNameAndSex = studentMapper.selectByStudentSelective(search);
        for (int i = 0; i < studentsByNameAndSex.size(); i++) {
            System.out.println(ToStringBuilder.reflectionToString(studentsByNameAndSex.get(i), ToStringStyle.MULTI_LINE_STYLE));
        }
		 sqlSession.commit();
        sqlSession.close();
    }

Query when there is only name , sent statement and result

The query conditions are only sent

where 1=1 and name like concat('%', ?, '%')

The query when there is only gender , the sentence and the result sent

The query conditions are only sent

 where 1=1 and sex=?

Inquiries where name and gender exist at the same time , sentences and results sent

Query conditions

where 1=1 and name like concat('%', ?, '%') and sex=?

2.2 Use the if tag in the UPDATE column

Sometimes we don't want to update all the fields, only the changed fields.

2.2.1 Update conditions

Only the changed fields are updated, and the null value is not updated.

2.2.1 Dynamic SQL

Interface method

    /**
* 更新非空属性
*/
int updateByPrimaryKeySelective(Student record);

Corresponding SQL

  <update id="updateByPrimaryKeySelective" parameterType="com.homejim.mybatis.entity.Student">
    update student
    <set>
      <if test="name != null">
        `name` = #{name,jdbcType=VARCHAR},
      </if>
      <if test="phone != null">
        phone = #{phone,jdbcType=VARCHAR},
      </if>
      <if test="email != null">
        email = #{email,jdbcType=VARCHAR},
      </if>
      <if test="sex != null">
        sex = #{sex,jdbcType=TINYINT},
      </if>
      <if test="locked != null">
        locked = #{locked,jdbcType=TINYINT},
      </if>
      <if test="gmtCreated != null">
        gmt_created = #{gmtCreated,jdbcType=TIMESTAMP},
      </if>
      <if test="gmtModified != null">
        gmt_modified = #{gmtModified,jdbcType=TIMESTAMP},
      </if>
    </set>
    where student_id = #{studentId,jdbcType=INTEGER}

2.2.3 Test

    @Test
public  void updateByStudentSelective() {
        SqlSession sqlSession = null;
        sqlSession = sqlSessionFactory.openSession();
        StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
		Student student = new Student();
        student.setStudentId(1);
        student.setName("明明");
        student.setPhone("13838438888");
        System.out.println(studentMapper.updateByPrimaryKeySelective(student));
        sqlSession.commit();
        sqlSession.close();
}

The result is as follows

2.3 Use if tag in INSERT dynamic insert

We insert a record in the database, not every field has a value, but changes dynamically. Using the if tag at this time can help us solve this problem.

2.3.1 Insertion conditions

Only non-empty attributes are inserted.

2.3.2 Dynamic SQL

Interface method

    /**
* 非空字段才进行插入
*/
int insertSelective(Student record);

Corresponding SQL

<insert id="insertSelective" parameterType="com.homejim.mybatis.entity.Student">
    insert into student
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="studentId != null">
        student_id,
      </if>
      <if test="name != null">
        `name`,
      </if>
      <if test="phone != null">
        phone,
      </if>
      <if test="email != null">
        email,
      </if>
      <if test="sex != null">
        sex,
      </if>
      <if test="locked != null">
        locked,
      </if>
      <if test="gmtCreated != null">
        gmt_created,
      </if>
      <if test="gmtModified != null">
        gmt_modified,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="studentId != null">
        #{studentId,jdbcType=INTEGER},
      </if>
      <if test="name != null">
        #{name,jdbcType=VARCHAR},
      </if>
      <if test="phone != null">
        #{phone,jdbcType=VARCHAR},
      </if>
      <if test="email != null">
        #{email,jdbcType=VARCHAR},
      </if>
      <if test="sex != null">
        #{sex,jdbcType=TINYINT},
      </if>
      <if test="locked != null">
        #{locked,jdbcType=TINYINT},
      </if>
      <if test="gmtCreated != null">
        #{gmtCreated,jdbcType=TIMESTAMP},
      </if>
      <if test="gmtModified != null">
        #{gmtModified,jdbcType=TIMESTAMP},
      </if>
    </trim>
  </insert>

This SQL should be familiar to everyone, after all, it is automatically generated. A colleague was fired directly because of an accident caused by a SQL statement. Take a look at this recommendation.

2.3.3 Test

    @Test
public  void insertByStudentSelective() {
    SqlSession sqlSession = null;
    sqlSession = sqlSessionFactory.openSession();
    StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
    Student student = new Student();
    student.setName("小飞机");
    student.setPhone("13838438899");
    student.setEmail("[email protected]");
    student.setLocked((byte) 0);    System.out.println(studentMapper.insertSelective(student));

    sqlSession.commit();
    sqlSession.close();
}    

Corresponding result

In SQL, only non-empty fields are inserted.

3 choose tag

The choose when otherwise tag can help us implement the logic of if else.

A choose tag has at least one when and at most one otherwise

The following is an example of a query.

3.1 Query conditions

Assuming name is unique, query a student

  • When studen_id has a value, use studen_id to query;

  • When studen_id has no value, use name to query;

  • Otherwise return empty

3.2 Dynamic SQL

Interface method

    /**
* - 当 studen_id 有值时, 使用 studen_id 进行查询;
* - 当 studen_id 没有值时, 使用 name 进行查询;
* - 否则返回空
*/
Student selectByIdOrName(Student record);

Corresponding SQL

  <select id="selectByIdOrName" resultMap="BaseResultMap" parameterType="com.homejim.mybatis.entity.Student">
    select
    <include refid="Base_Column_List" />
    from student
    where 1=1
    <choose>
      <when test="studentId != null">
        and student_id=#{studentId}
      </when>
      <when test="name != null and name != ''">
        and name=#{name}
      </when>
      <otherwise>
        and 1=2
      </otherwise>
    </choose>
  </select>

3.3 Testing

@Test
public void selectByIdOrName() {
        SqlSession sqlSession = null;
        sqlSession = sqlSessionFactory.openSession();
        StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
         Student student = new Student();
        student.setName("小飞机");
        student.setStudentId(1);
		Student studentById = studentMapper.selectByIdOrName(student);
        System.out.println("有 ID 则根据 ID 获取");
        System.out.println(ToStringBuilder.reflectionToString(studentById, ToStringStyle.MULTI_LINE_STYLE));
        student.setStudentId(null);
        Student studentByName = studentMapper.selectByIdOrName(student);
        System.out.println("没有 ID 则根据 name 获取");
        System.out.println(ToStringBuilder.reflectionToString(studentByName, ToStringStyle.MULTI_LINE_STYLE));
        student.setName(null);
        Student studentNull = studentMapper.selectByIdOrName(student);
        System.out.println("没有 ID 和 name, 返回 null");
        Assert.assertNull(studentNull);
        sqlSession.commit();
        sqlSession.close();
}

If there is an ID, get it according to the ID , the result

Without ID, get it according to name

Without ID and name, return null

4 trim(set、where)

These three actually solve similar problems. For example, when we wrote the previous [ Use if tag in WHERE condition ] SQL, we did not want the condition where 1=1 to exist. " Two magical uses of Mybatis trim tags" is recommended.

4.1 where

4.1.1 Query conditions

Perform conditional search based on the entered student information.

  1. When only the user name is input, the user name is used for fuzzy search;

  2. When only entering gender, use gender for exact match

  3. When both the user name and gender exist, use these two conditions for query matching query

Do not use where 1=1 .

4.1.2 Dynamic SQL

Obviously, we have to solve these problems

  • When the conditions are not met: there should be no where in the SQL at this time, otherwise it will cause an error

  • When the if condition is met: where is required in SQL, and the and | or under the first established if tag must be removed

At this time, we can use the where tag.

Interface method

    /**
* 根据输入的学生信息进行条件检索
* 1. 当只输入用户名时, 使用用户名进行模糊检索;
* 2. 当只输入邮箱时, 使用性别进行完全匹配
* 3. 当用户名和性别都存在时, 用这两个条件进行查询匹配的用
*/
List<Student> selectByStudentSelectiveWhereTag(Student student);

Corresponding SQL

  <select id="selectByStudentSelectiveWhereTag" resultMap="BaseResultMap" parameterType="com.homejim.mybatis.entity.Student">
    select
    <include refid="Base_Column_List" />
    from student
   <where>
    <if test="name != null and name !=''">
      and name like concat('%', #{name}, '%')
    </if>
    <if test="sex != null">
      and sex=#{sex}
    </if>
   </where>
  </select>

4.1.3 Test

    
    @Test
    public void selectByStudentWhereTag() {
        SqlSession sqlSession = null;
        sqlSession = sqlSessionFactory.openSession();
        StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
        Student search = new Student();
        search.setName("明");
        System.out.println("只有名字时的查询");
        List<Student> studentsByName = studentMapper.selectByStudentSelectiveWhereTag(search);
        for (int i = 0; i < studentsByName.size(); i++) {
            System.out.println(ToStringBuilder.reflectionToString(studentsByName.get(i), ToStringStyle.MULTI_LINE_STYLE));
        }
        search.setSex((byte) 1);
        System.out.println("姓名和性别同时存在的查询");
        List<Student> studentsBySex = studentMapper.selectByStudentSelectiveWhereTag(search);
        for (int i = 0; i < studentsBySex.size(); i++) {
            System.out.println(ToStringBuilder.reflectionToString(studentsBySex.get(i), ToStringStyle.MULTI_LINE_STYLE));
        }
        System.out.println("姓名和性别都不存在时查询");
        search.setName(null);
        search.setSex(null);
        List<Student> studentsByNameAndSex = studentMapper.selectByStudentSelectiveWhereTag(search);
        for (int i = 0; i < studentsByNameAndSex.size(); i++) {
            System.out.println(ToStringBuilder.reflectionToString(studentsByNameAndSex.get(i), ToStringStyle.MULTI_LINE_STYLE));
        }
        sqlSession.commit();
        sqlSession.close();
    }

Query when there is only name , there is where

The query where name and gender exist at the same time , there is where

Query when the name and gender do not exist , at this time, where will no longer appear.

4.2 set

The set tag is similar. In [ 2.2 Use if tag in UPDATE update column ], if our method updateByPrimaryKeySelectivedoes not use tags, then we have to find a way to deal with the condition that all fields are empty, the condition that the fields are not empty, etc. With this, we only need to write the if tag, no need to deal with similar problems.

4.3 trim

Both set and where are actually a type of trim tag, and both functions can be implemented using trim tags.

4.3.1 trim to indicate where

Like the where tag above, we can also write

<trim prefix="where" prefixOverrides="AND |OR">
</trim>

It means that when there is content in trim, add where, and when the first one is and or or, it will be removed. If there is no content, no where is added.

4.3.2 trim to represent set

Correspondingly, the set tag can be expressed as follows

<trim prefix="SET" suffixOverrides=",">
</trim>

Indicates that when there is content in trim, set is added and the final content is, and it will be removed. Without content, no set is added

4.3.3 Several attributes of trim

  • prefix: When the trim element contains content, add the prefix specified by prefix

  • prefixOverrides: When the trim element contains content, remove the prefix specified by prefixOverrides

  • suffix: When the trim element contains content, add the suffix specified by suffix

  • suffixOverrides: When the trim element contains content, remove the suffix specified by suffixOverrides

5 foreach label

The foreach tag can implement the Iterable interface for arrays, Maps or.

There are several attributes in foreach

  • collection: required, the name of the collection/array/Map.

  • item: variable name. That is, every value taken from the iterated object

  • index: The attribute name of the index. When the iterated object is a Map, the value is the Key in the Map.

  • open: the string at the beginning of the loop

  • close: the string at the end of the loop

  • separator: separator for each loop

Others are easier to understand. How should the value in the collection be set?

Related to the parameters in the interface method. Recommended reading: Mybatis 4 ways to pass multiple parameters . Follow the public number Java technology stack to get more Mybatis dry goods tutorials.

1. Only one array parameter or collection parameter

Default : collection=list, array is collection=array

Recommendation : Use @Param to specify the name of the parameter, if we @Param("ids") before the parameter, then fill in collection=ids

2. Multiple parameters

Please use @Param to specify multiple parameters, otherwise it will be very inconvenient in SQL.

3. The parameter is Map

Just specify it as the corresponding Key in the Map. In fact, @Param above is finally transformed into Map.

4. Parameters are objects

Use attributes. Attributes.

5.1 Use foreach in where

Use in where conditions, such as query by id set, delete by id set, etc.

5.1.1 Query conditions

We want to query all user information in the user id set.

5.1.2 Dynamic SQL

Function interface

    /**
* 获取 id 集合中的用户信息
* @param ids
* @return
*/
List<Student> selectByStudentIdList(List<Integer> ids);

Corresponding SQL

  <select id="selectByStudentIdList" resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List" />
    from student
    where student_id in
    <foreach collection="list" item="id" open="(" close=")" separator="," index="i">
      #{id}
    </foreach>
  </select>

5.1.3 Test

    @Test
    public void selectByStudentIdList() {
        SqlSession sqlSession = null;
        sqlSession = sqlSessionFactory.openSession();
        StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
         List<Integer> ids = new LinkedList<>();
        ids.add(1);
        ids.add(3);
		 List<Student> students = studentMapper.selectByStudentIdList(ids);
        for (int i = 0; i < students.size(); i++) {
            System.out.println(ToStringBuilder.reflectionToString(students.get(i), ToStringStyle.MULTI_LINE_STYLE));
        }
		sqlSession.commit();
        sqlSession.close();
    }

result

5.2 foreach realizes batch insertion

You can use foreach to achieve batch insertion.

5.2.1 Dynamic SQL

Interface method

    /**
* 批量插入学生
*/
int insertList(List<Student> students);

Corresponding SQL

  <insert id="insertList">
    insert into student(name, phone, email, sex, locked)
    values
    <foreach collection="list" item="student" separator=",">
      (
      #{student.name}, #{student.phone},#{student.email},
      #{student.sex},#{student.locked}
      )
    </foreach>
  </insert>

5.2.2 Test

    @Test
    public void insertList() {
        SqlSession sqlSession = null;
        sqlSession = sqlSessionFactory.openSession();
        StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
		 List<Student> students = new LinkedList<>();
        Student stu1 = new Student();
        stu1.setName("批量01");
        stu1.setPhone("13888888881");
        stu1.setLocked((byte) 0);
        stu1.setEmail("[email protected]");
        stu1.setSex((byte) 1);
        students.add(stu1);
		Student stu2 = new Student();
        stu2.setName("批量02");
        stu2.setPhone("13888888882");
        stu2.setLocked((byte) 0);
        stu2.setEmail("[email protected]");
        stu2.setSex((byte) 0);
        students.add(stu2);
		System.out.println(studentMapper.insertList(students));
        sqlSession.commit();
        sqlSession.close();
    }

result

6 bind label

The bind tag uses OGNL expressions to define a context variable, which is convenient for us to use.

As in the selectByStudentSelectivemethod, there are as follows

<if test="name != null and name !=''">
      and name like concat('%', #{name}, '%')
</if>

In MySQL, this function supports multiple parameters, but in Oracle only two parameters are supported. Then we can use bind to make the SQL support two databases

<if test="name != null and name !=''">
     <bind name="nameLike" value="'%'+name+'%'"/>
     and name like #{nameLike}
</if>

The changed query results are as follows

Usage example: https://github.com/homejim/mybatis-examples

Author: A intake desk

Source: https://www.cnblogs.com/homejim/

The copyright of this article belongs to the author and the blog garden, welcome to reprint, but without the author’s consent, this statement must be retained, and the original link must be given in an obvious place on the article page, otherwise the right to pursue legal responsibility is reserved.

Pay attention to the Java technology stack to see more dry goods

Click the original text to get more benefits!

Guess you like

Origin blog.csdn.net/youanyyou/article/details/108543919