MyBatis Notes | Detailed Dynamic SQL

table of Contents

Environmental preparation

Use if tags to realize expression judgment

Use trim tags to achieve string interception

Use the choose tag to implement branch selection

Use if and set tags to implement dynamic update statements

foreach tags

Use the foreach tag to traverse the collection

Two ways to use foreach to achieve batch insertion under mysql

Two built-in parameters

Use the bind tag for dynamic binding

Use sql tags to extract reused sql fragments


We think that for the SQL statements in the SQL mapping file, the SQL statements can be dynamically spliced ​​according to the difference in the value passed in. Dynamic SQL can be used at this time.

Environmental preparation

New EmployeeMapperDynamicSQLinterface:

package com.cerr.mybatis.dao;

import com.cerr.mybatis.Employee;

import java.util.List;

public interface EmployeeMapperDynamicSQL {

}

New EmployeeMapperDynamicSQL.xmlmapping file:

<?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.cerr.mybatis.dao.EmployeeMapperDynamicSQL">
    
</mapper>


Use if tags to realize expression judgment

We can use <if>tags to judge the expression, and then make the corresponding SQL splicing. <if>The tag has an testattribute used to judge the expression. The expression here uses OGNL expression. For the introduction of OGNL expression, please refer to part of the article: Struts2 study notes | value stack and OGNL .

Expand

We should pay attention to escape characters, for example ""should be written as &quot;&quot;.

We add a new method to the interface:

    //携带了哪个字段,查询条件就带上这个字段
    public List< Employee > getEmpsByConditionIf(Employee employee);

testing method:

package com.cerr.mybatis;
import com.cerr.mybatis.dao.*;
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.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MyBatisTest {

    //获取SQLSessionFactory
    public SqlSessionFactory getSqlSessionFactory() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        return new SqlSessionFactoryBuilder().build(inputStream);
    }

    @Test
    public void testDynamicSql() throws IOException {
        SqlSessionFactory factory = getSqlSessionFactory();
        SqlSession session = factory.openSession();
        try{
            EmployeeMapperDynamicSQL mapper = session.getMapper(EmployeeMapperDynamicSQL.class);
            Employee employee = new Employee(1,"%e%",null,null);
            List<Employee> employees = mapper.getEmpsByConditionIf(employee);
            for(Employee e : employees){
                System.out.println(e);
            }
        }finally {
            session.close();
        }
    }
}

Configuration in the SQL mapping file:

    <!-- 查询员工,要求:携带了哪个字段,查询条件就带上这个字段 -->
    <select id="getEmpsByConditionIf" resultType="com.cerr.mybatis.Employee">
        select * from tb1_employee
        where
            <if test="id!=null">
                id=#{id}
            </if>
            <if test="lastName!=null and lastName!=''">
                and last_name like #{lastName}
            </if>
            <if test="email!=null and email.trim()!=&quot;&quot;">
                and email=#{email}
            </if>
            <!-- ognl会进行字符串与数字的转换判断 -->
            <if test="gender==0 or gender==1">
                and gender=#{gender}
            </if>
    </select>

However, there is a problem with this configuration file. The input idmust not be empty each time . If it idis empty, assuming that any one of the following is not empty (for example lastName), then the sql statement select * from tb1_employee where and last_name like ?is obviously one more and, the sql statement Is wrong, we can use the following wheretags to improve this problem.

Using <where>tags, MyBatis will remove and remove the <where>redundant sql spliced ​​in the tags . Correct usage:andor

    <select id="getEmpsByConditionIf" resultType="com.cerr.mybatis.Employee">
        select * from tb1_employee
        <where>
            <if test="id!=null">
                id=#{id}
            </if>
            <if test="lastName!=null and lastName!=''">
                and last_name like #{lastName}
            </if>
            <if test="email!=null and email.trim()!=&quot;&quot;">
                and email=#{email}
            </if>
            <!-- ognl会进行字符串与数字的转换判断 -->
            <if test="gender==0 or gender==1">
                and gender=#{gender}
            </if>
        </where>
    </select>

But first it will only remove excess andand or, if <if>every time the label andor orwritten on the back, the label can not remove excess normal andand orso should <if>every time the label andor orEDITORIAL. For example, the following situation cannot be removed normally:

<!-- 查询员工,要求:携带了哪个字段,查询条件就带上这个字段 -->
    <select id="getEmpsByConditionIf" resultType="com.cerr.mybatis.Employee">
        select * from tb1_employee
        <where>
            <if test="id!=null">
                id=#{id} and
            </if>
            <if test="lastName!=null and lastName!=''">
                last_name like #{lastName} and
            </if>
            <if test="email!=null and email.trim()!=&quot;&quot;">
                email=#{email} and
            </if>
            <!-- ognl会进行字符串与数字的转换判断 -->
            <if test="gender==0 or gender==1">
                gender=#{gender}
            </if>
        </where>
    </select>

Use trim tags to achieve string interception

<trim>The body of the tag is the result of the entire string being assembled, with four attributes:

  • prefix: Prefix, add a prefix to the entire string after the spelling
  • prefixOverrides: Prefix coverage, remove extra characters in front of the entire string
  • suffix: Suffix, add a suffix to the entire string after the spelling
  • suffixOverrides: Suffix coverage, remove the extra characters after the entire string

The configuration can be modified as follows:

    <select id="getEmpsByConditionTrim" resultType="com.cerr.mybatis.Employee">
        select * from tb1_employee
        <trim prefix="where" suffixOverrides="and">
            <if test="id!=null">
                id=#{id} and
            </if>
            <if test="lastName!=null and lastName!=''">
                 last_name like #{lastName} and
            </if>
            <if test="email!=null and email.trim()!=&quot;&quot;">
                 email=#{email} and
            </if>
            <!-- ognl会进行字符串与数字的转换判断 -->
            <if test="gender==0 or gender==1">
                 gender=#{gender}
            </if>
        </trim>
    </select>

testing method:

package com.cerr.mybatis;
import com.cerr.mybatis.dao.*;
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.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MyBatisTest {

    //获取SQLSessionFactory
    public SqlSessionFactory getSqlSessionFactory() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        return new SqlSessionFactoryBuilder().build(inputStream);
    }

    @Test
    public void testDynamicSql() throws IOException {
        SqlSessionFactory factory = getSqlSessionFactory();
        SqlSession session = factory.openSession();
        try{
            EmployeeMapperDynamicSQL mapper = session.getMapper(EmployeeMapperDynamicSQL.class);
            Employee employee = new Employee(null,"%e%",null,null);
            List<Employee> employees = mapper.getEmpsByConditionTrim(employee);
            for(Employee e : employees){
                System.out.println(e);
            }
        }finally {
            session.close();
        }
    }
}

Use the choose tag to implement branch selection

The equivalent of java in the switchstatement, but it is a plus break, and can be used with <when>and <otherwise>label.

We now want to implement the following functions. If it is passed in id, use the idcheck, if it is passed in lastName, use the lastNamecheck. Only one will be selected.
New interface method:

public List<Employee> getEmpsByConditionChoose(Employee employee);

SQL mapping file:

    <select id="getEmpsByConditionChoose" resultType="com.cerr.mybatis.Employee">
        select * from tb1_employee
        <where>
            <!-- 如果传入`id`就用`id`查,如果传入`lastName`就用`lastName`查,只会选一个 ,如果这两个都没有传,则查女生-->
            <choose>
                <when test="id!=null">
                    id = #{id}
                </when>
                <when test="lastName!=null">
                    last_name like #{lastName}
                </when>
                <otherwise>
                    gender = 0
                </otherwise>
            </choose>
        </where>
    </select>

testing method:

    @Test
    public void testDynamicSql() throws IOException {
        SqlSessionFactory factory = getSqlSessionFactory();
        SqlSession session = factory.openSession();
        try{
            EmployeeMapperDynamicSQL mapper = session.getMapper(EmployeeMapperDynamicSQL.class);
            Employee employee = new Employee(null,"%e%",null,null);
            List<Employee> employees = mapper.getEmpsByConditionChoose(employee);
            for(Employee e : employees){
                System.out.println(e);
            }
        }finally {
            session.close();
        }
    }

The incoming lastNamesql in the test method is:

Preparing: select * from tb1_employee WHERE last_name like ? 

result:

 

 
13424350-34f80218c03512de.png
 

 

If it is changed Employee employee = new Employee(null,null,null,null);, the sql sent is:

select * from tb1_employee WHERE gender = 0

The results are as follows:

 

 
13424350-65b7ad44ec7ef44d.png
 

 


Use if and set tags to implement dynamic update statements

<set>Labels can replace the ones in our previous updatestatements set, and can support the ,removal of redundant ones .

We have a requirement. For a tb1_employeetable, we want to update one of its records, but our goal is to update which fields we pass in which parameters. If we simply use <if>tags, it is the following configuration:

    <update id="updateEmp" >
        update tb1_employee set
        <if test="lastName!=null">
            last_name = #{lastName},
        </if>
        <if test="email!=null">
            email=#{email},
        </if>
        <if test="gender!=null">
            gender=#{gender}
        </if>
            where id=#{id}
    </update>

But this will cause problems. Assuming I only pass one last_namefield, the SQL statement looks like this:

update tb1_employee set last_name = ? , where id = ?

You can see that there last_name = ?is one more in the back ,. At this time, we can use <set>tags to make improvements. The improved version is as follows:

    <update id="updateEmp" >
        update tb1_employee
        <set>
            <if test="lastName!=null">
                last_name = #{lastName},
            </if>
            <if test="email!=null">
                email=#{email},
            </if>
            <if test="gender!=null">
                gender=#{gender}
            </if>
        </set>
        where id=#{id}
    </update>

At this time, if you only pass in last_name, there will be no extra commas. The SQL sent is:

  update tb1_employee set last_name = ? where id=?

You can also use the <trim>label to modify, add the prefix set, and remove the extra suffix ,:

    <update id="updateEmp" >
        update tb1_employee
        <trim prefix="set" suffixOverrides=",">
            <if test="lastName!=null">
                last_name = #{lastName},
            </if>
            <if test="email!=null">
                email=#{email},
            </if>
            <if test="gender!=null">
                gender=#{gender}
            </if>
        </trim>
        where id=#{id}
    </update>

The remaining code:
interface method:

public void updateEmp(Employee employee);

testing method:

package com.cerr.mybatis;
import com.cerr.mybatis.dao.*;
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.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MyBatisTest {

    //获取SQLSessionFactory
    public SqlSessionFactory getSqlSessionFactory() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        return new SqlSessionFactoryBuilder().build(inputStream);
    }

    @Test
    public void testDynamicSql() throws IOException {
        SqlSessionFactory factory = getSqlSessionFactory();
        SqlSession session = factory.openSession();
        try{
            EmployeeMapperDynamicSQL mapper = session.getMapper(EmployeeMapperDynamicSQL.class);
            Employee employee = new Employee(1,"Admin",null,null);
            mapper.updateEmp(employee);
            session.commit();
        }finally {
            session.close();
        }
    }
}

foreach tags

<foreach>Tags can be used to traverse the collection, with the following properties:

  • collection: Specifies to traverse the collection in which Listthe type of specific processing parameters encapsulated Mapin Mapthe key is called list.
  • item: Assign the element currently traversed to the specified variable
  • separator: The separator between each element
  • open: Traverse all the results and splice a starting character
  • close: Traverse all the results and join an ending character
  • index:index. When traversing list, index is the index, which itemis the current value; when traversing the map, it indexis the key itemof the map , which is the value of the map.

#{变量名}The value of the variable can be retrieved by using it, which is the element currently traversed.

Use the foreach tag to traverse the collection

Next, we want to implement a circular query, that is, I pass in an array of id in the method, and then use it <foreach>to make our query conditions can be dynamically changed, that is, I pass in a few ids and check the records corresponding to those ids, for example The following sql statement:

select * from tb1_employee where id in(1,4);
select * from tb1_employee where id in(1,2,3,4,5,6);

Query the corresponding record according to the set of incoming ids.

First, the interface method is as follows:

public List<Employee> getEmpsByConditionForeach(@Param("ids") List<Integer> ids);

In this interface, we use @Paramannotations to pass in named parameters so that the collectionproperties of our SQL mapping file can use the collection. For parameter processing, please refer to this article. Click to view: MyBatis Notes | Detailed parameter processing (the difference between multiple types of parameter processing, source code analysis, and two formats for reading parameters) .

The SQL mapping file is as follows:

    <select id="getEmpsByConditionForeach" resultType="com.cerr.mybatis.Employee">
        select * from tb1_employee where id in
            <foreach collection="ids" item="item_id" separator="," open="(" close=")">
                #{item_id}
            </foreach>
    </select>

testing method:

package com.cerr.mybatis;
import com.cerr.mybatis.dao.*;
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.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MyBatisTest {

    //获取SQLSessionFactory
    public SqlSessionFactory getSqlSessionFactory() throws IOException {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        return new SqlSessionFactoryBuilder().build(inputStream);
    }

    @Test
    public void testDynamicSql1() throws IOException {
        SqlSessionFactory factory = getSqlSessionFactory();
        SqlSession session = factory.openSession();
        try{
            EmployeeMapperDynamicSQL mapper = session.getMapper(EmployeeMapperDynamicSQL.class);
            List<Employee> employees = mapper.getEmpsByConditionForeach(Arrays.asList(1,4));
            for(Employee e : employees){
                System.out.println(e);
            }
        }finally {
            session.close();
        }
    }

Because the Listset passed by our method at this time has only two elements, the sql sent is as follows:

Preparing: select * from tb1_employee where id in( ? , ? ) 

result:

 

 
13424350-3e739cdd11d42a38.png
 

 

Two ways to use foreach to achieve batch insertion under mysql

The first is to use insert into table_name(...) values(),(),()this syntax format directly , that is, to use the parentheses after the foreachtraversal values.
The interface method is as follows:

public void addEmps(@Param("emps") List<Employee> emps);

The configuration is as follows:

<insert id="addEmps">
        insert into tb1_employee(last_name,email,gender,d_id)
        values
        <foreach collection="emps" item="emp" separator=",">
            (#{emp.lastName},#{emp.email},#{emp.gender},#{emp.department.id})
        </foreach>

</insert>

The second is to send a insertstatement every time a statement is inserted , that is, to use a foreachloop to traverse the insertstatement, with a ;split in the middle , and the interface is the same as above.
To use this method, you need to set an allowMultiQueriesattribute in the database connection , which means that ;splitting between multiple queries is allowed , that is, the url is:

jdbc:mysql://localhost:3306/mybatis?allowMultiQueries=true

The configuration is as follows:

<insert id="addEmps">
        <foreach collection="emps" item="emp" separator=";">
            insert into tb1_employee(last_name,email,gender,d_id)
            values
            (#{emp.lastName},#{emp.email},#{emp.gender},#{emp.department.id})
        </foreach>
</insert>

Two built-in parameters

Not only the parameters passed by the method can be used to judge and value. MyBatis also has two built-in parameters by default:

  • _parameter: Represents the entire parameter. If the passed parameter is a single parameter, then it _parameteris the passed parameter; if the passed parameter is multiple parameters, the parameter will be encapsulated into a map, which _parameteris this map.
  • _databaseId: If the DatabaseIdProvider tag _databaseIdis configured, it is the alias of the current database.

For example, we now want to write a SQL mapping: when the database is mysql and Oracle, use different sql, and when the input Employeeparameter is empty, there is no conditional query, then the above two parameters can be used.
The interface is as follows:

public List<Employee> getEmpsTestInnerParameter(Employee employee);

The configuration is as follows:

<select id="getEmpsTestInnerParameter" resultType="com.cerr.mybatis.Employee">
        <if test="_databaseId=='mysql'">
            select * from tb1_employee
            <if test="_parameter!=null">
                where last_name = #{_parameter.lastName}
            </if>
        </if>

        <if test="_databaseId=='oracle'">
            select * from employee
        </if>
</select>

When we pass in is Employeenot empty, the sql statement is:

select * from tb1_employee where last_name = ? 

When the input Employeeis empty, the sql statement is:

select * from tb1_employee

Use the bind tag for dynamic binding

You can bind the value of the OGNL expression to a variable, which is convenient for referencing the value of this variable later.

<select id="getEmpsTestInnerParameter" resultType="com.cerr.mybatis.Employee">
        <bind name="_lastName" value="'%'+lastName+'%'"/>
        select * from tb1_employee
        <if test="_parameter!=null">
                where last_name like #{_lastName}
        </if>
</select>

The <bind>tags in the above SQL mapping are lastNameadded before and after the incoming value %and assigned to it _lastName. In the SQL statement, we can use #{_lastName}parameters to make fuzzy query conditions.


Use sql tags to extract reused sql fragments

In SQL statements, there are always some SQL fragments that can be reused. We can use <sql>tags to extract these reusable sql fragments, and then use <include>tags to refer to it, <include>there can also be customized property, these custom attributes can be used <sql>inside the tag ${变量名}(cannot be used #{}to get the value of these custom attributes )

<sql id="insertColumn">
        last_name,email,gender,d_id
</sql>

In the above code, we use some fields of the sqlextracted tb1_employeetable, and then we can directly quote when we want to insert the table:

    <insert id="addEmps">
        <foreach collection="emps" item="emp" separator=";">
            insert into tb1_employee(
                <include refid="insertColumn"/>
            )
            values
            (#{emp.lastName},#{emp.email},#{emp.gender},#{emp.department.id})
        </foreach>
    </insert>

Is equivalent to:

    <insert id="addEmps">
        <foreach collection="emps" item="emp" separator=";">
            insert into tb1_employee(
                  last_name,email,gender,d_id
            )
            values
            (#{emp.lastName},#{emp.email},#{emp.gender},#{emp.department.id})
        </foreach>
    </insert>

Many tags that can be written in sql can also be used in this tag.

Guess you like

Origin blog.csdn.net/qq_14810195/article/details/103505421