Master Mybatis dynamic mapping, but I have worked hard

Master Mybatis dynamic mapping, but I have worked hard

Dynamic SQL is one of the powerful features of MyBatis. If you have used JDBC or other similar frameworks, you should be able to understand how painful it is to splice SQL statements according to different conditions. For example, make sure you don't forget to add necessary spaces when splicing, and take care to remove the comma in the last column name of the list. Using dynamic SQL, you can completely get rid of this pain.

Using dynamic SQL is not an easy task, but with the powerful dynamic SQL language that can be used in any SQL mapping statement, MyBatis significantly improves the ease of use of this feature.

If you have used JSTL or any text processor based on an XML-like language before, you may feel familiar with dynamic SQL elements. In previous versions of MyBatis, it took time to understand a large number of elements. With the help of powerful OGNL-based expressions, MyBatis 3 replaces most of the previous elements, greatly simplifying the types of elements, and now the types of elements to learn are less than half of the original.

6371c164157c7ff370437d22d5d77ff6.jpg

if: Use if to implement simple condition selection.
choose (when, otherwise): Equivalent to the switch statement in java, usually with when and otherwise.
set: Resolve dynamic update statements.
trim: Remove redundant keywords flexibly.
foreach: Iterate a collection, usually used for in conditions.

Many times in actual work, these labels are used in combination.

Today’s demonstration is used Spring-Boot+Mybatisfor demonstration, recommended for Spring-Boot integration with Mybatis:

e3d475d0a1998de952f953bdfc1f93b0.jpeg

if+where realizes multi-condition query

Create an Ang database table:

CREATE TABLE `m_user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `gender` int(11) DEFAULT NULL COMMENT '0:girl 1:boy',
  PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4;

Initialize several pieces of data:

60302a499e39389762e52358ad82eb18.jpeg

First look at the UserMapper.xml file:

<mapper namespace="com.tian.mybatis.mapper.UserMapper">
    <resultMap id="User" type="com.tian.mybatis.entity.User">
        <id column="id" property="id"/>
        <result column="name" property="userName"/>
    </resultMap>
    <select id="selectUserById"  resultMap="User">
        select * from m_user
        <where>
            <if test="id != null">
                id = #{id}
            </if>
            <if test="name != null and name != ''">
                and `name` = #{name}
            </if>
        </where>
    </select></mapper>

UserMapper.java content:

import com.tian.mybatis.entity.User;import org.apache.ibatis.annotations.Param;import org.springframework.stereotype.Repository;@Repositorypublic interface UserMapper {
    User selectUserById(@Param("name") String userName, @Param("id") Integer id);}

UserService.java content:

public interface UserService {
    User selectUserById(String userName, Integer id);}

UserServiceImpl.java content:

import com.tian.mybatis.entity.User;import com.tian.mybatis.mapper.UserMapper;import com.tian.mybatis.service.UserService;import org.springframework.stereotype.Service;import javax.annotation.Resource;@Servicepublic class UserServiceImpl implements UserService {

    @Resource
    private UserMapper userMapper;

    @Override
    public User selectUserById(String userName, Integer id) {
        return userMapper.selectUserById(userName, id);
    }}

UserController.java content:

import com.tian.mybatis.entity.User;import com.tian.mybatis.service.UserService;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;@RestControllerpublic class UserController {

    @Resource
    private UserService userService;

    @GetMapping("/test")
    public User selectUserById() {
        return userService.selectUserById("tian", 1);
    }}

Application.java content:

import org.mybatis.spring.annotation.MapperScan;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication@MapperScan("com.tian.mybatis.mapper")public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }}

Start the project, and then visit/test.

http://localhost:9002/test

return:

d993e126908db876fb4a8a5aa2bba444.jpeg

The above case is also a code case in our work, but part of our work uses this method.

3eac3e4fb4bf613c70f2d57bc93e48ce.jpeg

All the demos below are adjusted based on the above code.

Back to the topic.

Where+if is used in the above case. There seems to be a problem in the case:

27ab17b17017f587180f12da8384b628.jpeg

If id=null, isn’t there an extra and?

We modify the code in the controller

    @GetMapping("/test")
    public User selectUserById() {
        return userService.selectUserById("tian", null);
    }

In order to enable sql output, we added a configuration item in the configuration file:

logging:
  level:
    with:
      tian:
        mybatis:
          mapper: debug

Execute again, the output is the same as before. There is no and in the sql output from the console. This is one of the powerful functions of the so-called dynamic mapping.

84bab5bb7343019d4d1a3c836ac7f313.png

If we don't use dynamic mapping tags, problems are likely to occur when dealing with or or and.

The where element can intelligently handle the redundant problems of and and or, without worrying about grammatical errors caused by redundant keywords.
The test of the if element is used to determine whether the expression conforms, and if it conforms, the SQL statement continues to be spliced.

Suggest

It is recommended to use this kind of dynamic label instead of the original ecology, because sometimes there is always an unexpected judgment that leads to an additional and or or, so a bug occurs, which may seriously cause an online function to be impossible.

if+trim+foreach realizes multi-condition query

Adjust the previous code

<select id="selectUsersByIds" resultMap="User">
        select * from m_user
        <trim prefix="where" prefixOverrides="and | or">
            <if test="idList != null">
                id in
                <foreach collection="idList" item="id" open="(" close=")" separator=",">
                    #{id}
                </foreach>
            </if>
            <if test="gender != null and gender != 0">
                AND gender = #{gender}
            </if>
        </trim></select>

UserMapper.java increased

List<User> selectUsersByIds(@Param("idList") List<Integer> idList, @Param("gender") Integer gender);

New controller method:

    @GetMapping("/users")
    public List<User> selectUsersByIds() {
        List<Integer> idList = new ArrayList<>();
        idList.add(1);
        idList.add(2);
        idList.add(3);
        idList.add(4);
        idList.add(5);
        return userService.selectUsersByIds(idList, null);
    }

Project running, visit

http://localhost:9002/users

Output:

a5ea7dcd1d4b55d256fe7fab5df9e2a2.jpeg

sql output:

a0205456b3925d922fac10d48126e05b.png

Explain the above related attributes

attributes of trim

  • prefix: Prefix: The function is to add a prefix to the content contained in trim after automatically identifying whether there is a return value, such as where in the above example.
  • suffix: Suffix: The function is to add a suffix to the content contained in trim.
  • prefixOverrides:: Specify the content of the header containing the content of the trim, (such as the and | or in the above example) ignore (eliminate the remainder);
  • suffixOverrides: Ignore the specified content for the first part of the trim included content.

Properties of foreach

  • item: Represents the alias of each element in the collection during iteration.
  • index:: Specify a name to indicate the position reached during each iteration during the iteration.
  • open: Indicates where the statement starts (since it is an in conditional statement, it must start with '(')
  • separator:: Indicates what symbol is used as the separator each time the iteration is performed (since it is an in conditional statement, it must be separated by ',')
  • close:: Indicates what the statement ends (since it is an in conditional statement, it must end with ')')
  • Collection: The most critical and error-prone attribute. Note that this attribute must be specified. Under different circumstances, the value of this attribute is different.
    There are three main situations:

ea6b347cb8e669b149d882f365173f59.png

@Param is a comment in Mybatis. Don't quote it wrong when writing, @Param("name"), where name is the name we use in Mapper.xml.

I have seen many people do this in the project, that is, when the where statement is not sure that it can appear conditionally, use

slect ...from...where 1=1

See if your code also has it?

set

The set element can be used to dynamically include columns that need to be updated, ignoring other columns that are not updated.

UserMapper.xml added

<update id="updateAuthorIfNecessary">
        update m_user
        <set>
            <if test="userName != null and userName != ''">
               `name` = #{userName},
            </if>
            <if test="gender != null and gender != 0">
                gender = #{gender},
            </if>
            <if test="age != null and age != 0">
                age = #{age},
            </if>
        </set>
        where id=#{id}</update>

UserMapper.java added

int updateAuthorIfNecessary(User user);

controller added

    @PostMapping("/updateUser")
    public String update() {
        User user = new User();
        user.setAge(18);
        user.setUserName("田哥");
        user.setId(1);
        return userService.updateAuthorIfNecessary(user) == 1 ? "success" : "fail";
    }

Restart the project, visit

http://localhost:9002/updateUser

Output: success

The data in the database table has been modified successfully:

9047638a81bcfb8cd5442bd6a1cb428c.jpeg

SQL output

7ec6c44021f06275d26352ef45cabb87.png

In this example, the set element dynamically inserts the SET keyword at the beginning of the line and deletes the extra commas (these commas are introduced when the conditional statement is used to assign values ​​to the column).

Another way

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

We adjust the diam in the above xml:

    <update id="updateAuthorIfNecessary">
        update m_user
        <trim prefix="SET" suffixOverrides=",">
            <if test="userName != null and userName != ''">
                `name` = #{userName},
            </if>
            <if test="gender != null and gender != 0">
                gender = #{gender},
            </if>
            <if test="age != null and age != 0">
                age = #{age},
            </if>
        </trim>
        where id=#{id}
    </update>

Controller modification:

    @PostMapping("/updateUser")
    public String update() {
        User user = new User();
        user.setAge(19);
        user.setUserName("tian");
        user.setId(1);
        return userService.updateAuthorIfNecessary(user) == 1 ? "success" : "fail";
    }

Finally, look at the SQL output:

4ff0a8959a9cb191c8b54b31819e4d38.jpeg

The SET keyword is automatically added to us. And the database modification is successful.

57a135151c247a56c5181593933a78c3.jpeg

choose

It is equivalent to the switch statement in Java, usually combined with when and otherwise.

Sometimes, we don't want to use all the conditions, but just want to choose one from multiple conditions. In response to this situation, MyBatis provides the choose element, which is a bit like the switch statement in Java.

Let's continue to use the above case code for demonstration.

New methods in UserMapper.xml:

<select id="selectUsersByName" resultMap="User">
        select * from m_user where age = 19
        <choose>
            <when test="userName != null and userName != ''">
                and `name` = #{userName}
            </when>
            <otherwise>
                AND gender = 1
            </otherwise>
        </choose></select>

New controller method:

@GetMapping("/user/name")
    public  List<User>  selectUsersByName() {
        return userService.selectUsersByName("tian");}

return:

ce4db4269d68310b8bd673a15088d51f.png

SQL output:

ab35d93ae8867c0164bdb6d9b2fb737e.jpeg

Correct output. What if our userName is not null?

The output and the above are normal, look at the SQL output:

56bc49d40245693098b1540128e21da7.jpeg

Because our userName condition is not met, gender is executed directly.

The above <otherwise>is similar to the default in switch in our java syntax. When the current conditions are not met, the default module is executed.

Bind

This method is not used much, but it is also useful. bindElements allow you to create a variable outside of the OGNL expression and bind it to the current context. such as:

<select id="selectUserByName" resultType="User">
  <bind name="pattern" value="'%' + userName + '%'" />
  select * from m_user
  WHERE `name` LIKE #{pattern}</select>

There is also script, this is not necessary for demonstration, basically not used in work. It is to unload SQL from Java code. such as

    @Update({"<script>",
      "update m_user",
      "  <set>",
      "    <if test='username != null'>`name`=#{username},</if>",
      "    <if test='gender != null and gender != 0'>gender=#{gender},</if>",
      "  </set>",
      "where id=#{id}",
      "</script>"})
    void updateUserValues(User user);

to sum up

Part of the knowledge in the article is to demonstrate, some of the code may not be very standardized, especially the sql part, we are developing, for the use of Mybatis development, I personally summarize a few points:

  • Whether there are indexes in the table, and whether our SQL is useful when there are indexes.
  • Try not to write an asterisk * in the return field, and it is recommended to write the required field.
  • Keyword suggestions are written in uppercase to better distinguish non-keywords.
  • Remember the single quotes when the fields in the table are the same as the database key.
  • When using @Param annotations, be sure to use the annotations in Mybatis.
  • When using one parameter or multiple parameters, use the annotation @Param to specify the name, so that you need to add fields again in the future.
  • It is strongly recommended to use dynamic tags to avoid SQL errors with more and or or keywords, and no need to write where 1=1

Author: TIAN Wei Chang
original link: master Mybatis dynamic mapping, I'm under the martial arts of
the original source: Java back-end technology stack full
invasion deleted

484c8f47c31d5eecd49388ac8339553e.jpeg


Guess you like

Origin blog.51cto.com/15050718/2621768