table of Contents
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
Use the foreach tag to traverse the collection
Two ways to use foreach to achieve batch insertion under mysql
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 EmployeeMapperDynamicSQL
interface:
package com.cerr.mybatis.dao;
import com.cerr.mybatis.Employee;
import java.util.List;
public interface EmployeeMapperDynamicSQL {
}
New EmployeeMapperDynamicSQL.xml
mapping 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 test
attribute 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 ""
.
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()!=""">
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 id
must not be empty each time . If it id
is 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 where
tags to improve this problem.
Using <where>
tags, MyBatis will remove and remove the <where>
redundant sql spliced in the tags . Correct usage:and
or
<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()!=""">
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 and
and or
, if <if>
every time the label and
or or
written on the back, the label can not remove excess normal and
and or
so should <if>
every time the label and
or or
EDITORIAL. 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()!=""">
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 spellingprefixOverrides
: Prefix coverage, remove extra characters in front of the entire stringsuffix
: Suffix, add a suffix to the entire string after the spellingsuffixOverrides
: 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()!=""">
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 switch
statement, 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 id
check, if it is passed in lastName
, use the lastName
check. 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 lastName
sql in the test method is:
Preparing: select * from tb1_employee WHERE last_name like ?
result:
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:
Use if and set tags to implement dynamic update statements
<set>
Labels can replace the ones in our previous update
statements set
, and can support the ,
removal of redundant ones .
We have a requirement. For a tb1_employee
table, 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_name
field, 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 whichList
the type of specific processing parameters encapsulatedMap
inMap
the key is calledlist
.item
: Assign the element currently traversed to the specified variableseparator
: The separator between each elementopen
: Traverse all the results and splice a starting characterclose
: Traverse all the results and join an ending characterindex
:index. When traversinglist
, index is the index, whichitem
is the current value; when traversing the map, itindex
is the keyitem
of 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 @Param
annotations to pass in named parameters so that the collection
properties 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 List
set 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:
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 foreach
traversal 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 insert
statement every time a statement is inserted , that is, to use a foreach
loop to traverse the insert
statement, with a ;
split in the middle , and the interface is the same as above.
To use this method, you need to set an allowMultiQueries
attribute 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_parameter
is the passed parameter; if the passed parameter is multiple parameters, the parameter will be encapsulated into a map, which_parameter
is this map._databaseId
: If the DatabaseIdProvider tag_databaseId
is 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 Employee
parameter 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 Employee
not empty, the sql statement is:
select * from tb1_employee where last_name = ?
When the input Employee
is 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 lastName
added 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 sql
extracted tb1_employee
table, 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.