7

1. Background

1.1 Disadvantages of traditional Mybatis

1.1.1 Scene description

Suppose there are two tables: a product table and an order table. The fields of the specific table are as follows:

following requirements:

  • Query all the information of the product table and the order table according to the id
  • Query order form information based on payment status and notification status
  • Add an order status to the order table, and query order information according to the order status

1.1.2 Requirements

demand a : query the product table according to id:

​@Select(" SELECT p.id ,p.name ,p.picture ,p.type ,p.price, p.type, p.time
          FROM product p  where id = #{id} ")
List<Product> getProductsBYId(@Param("id") Integer id);

Query all information of the order table according to id:

@Select(" SELECT o.id ,o.pay_no ,o.user_id ,o.product_id ,o.pay_price, o.num, o.pay_time, o.order_type, o.notif_type
          FROM order o  where id = #{id} ")
List<Order> getOrderBYId(@Param("id") Integer id);

Demand b : Query order form information according to payment status and notification status

​@Select(" SELECT o.id ,o.pay_no ,o.user_id ,o.product_id ,o.pay_price, o.num, o.pay_time, o.order_type, o.notif_type
          FROM order o  where order_type= #{orderType} ")
List<Order> getOrderBYId(@Param("orderType") Integer orderType);
 
@Select(" SELECT o.id ,o.pay_no ,o.user_id ,o.product_id ,o.pay_price, o.num, o.pay_time, o.order_type, o.notif_type
          FROM order o  where notify_type= #{notifyType} ")
List<Order> getOrderBYId(@Param("notifyType") Integer notifyType);

demand c : add an order status status to the order table, and query order information according to the order status.

Traditional mybaits requires three steps: First, you need to add a field to the order table, then add this attribute to the entity class of the order, and modify all the query sql of this state in the dao layer design, and add this field.

1.1.3 What is the problem with the above method?

Requirement a: For different entity classes, even if the purpose of the query is the same, it is still necessary to repeatedly construct similar SQL statements, only the table fields and table information are different.

Requirement b: For similar query conditions, different SQL must be constructed for a single scenario, resulting in a large amount of redundancy in SQL statements.

Requirement c: All SQL related to the newly added fields of the dao layer needs to be modified. This process is cumbersome and error-prone.

The above problems can be solved by using mybatis-plus.

1.1.4 Myatis-plus solution

First let ProductMapper and OrderMapper inherit the BaseMapper class:

public interface ProductMapper extends BaseMapper<Product> {
}
 
public interface OrderMapper extends BaseMapper<Order> {
  
}

demand a:

Query the commodity table and the order table respectively according to the id: Since the selectById method is provided in BaseMapper, you can directly pass in the specified parameters such as (id=1) according to the specific business scenario. There is no need to write specific sql statements, as for the principle of sql automatic generation will be introduced below;

productMapper.selectById(1);
orderMapper.selectById(1);

demand b:

At this time, use the BaseMapper.selectList(Wapper queryWrapper) method to directly construct the query conditions, for example, query the order information with the payment status of 2 and the notification status of 1.

orderMapper.selectList(new QueryWrapper<Order>().eq("orderType",2));
orderMapper.selectList(new QueryWrapper<Order>().eq("notifyType",1));
orderMapper.selectList(new QueryWrapper<Order>().eq("orderType",2));orderMapper.selectList(new QueryWrapper<Order>().eq("notifyType",1));

At this point, we can find that after using Mybatis-plus, we focus more on the business itself. For the above-mentioned similar application scenarios, there is no need to construct the same SQL, and use the wrapper to directly pass in the query conditions.

demand c:

The first two steps are consistent with the traditional mybatis. Since MyBatis-plus does not need to manually create SQL, it reduces a lot of duplication of labor.

1.2 Positioning of MyBatis-Plus

MyBatis-Plus (opens new window) (MP for short) is an enhancement tool for MyBatis (opens new window). On the basis of MyBatis, only enhancements are made without changes. It is born to simplify development and improve efficiency.

1.3 Features

  • No intrusion: only enhancements are made without making changes. The introduction of it will not affect the existing project, it is as smooth as silk;
  • Low loss: basic CURD will be automatically injected at startup, performance is basically no loss, direct object-oriented operation;
  • Powerful CRUD operations: built-in general Mapper and general Service, only a small amount of configuration can realize most of the CRUD operations of a single table, and a more powerful condition builder to meet various usage requirements;
  • Support Lambda form call: through Lambda expressions, it is convenient to write various query conditions, no need to worry about writing wrong fields;
  • Supports automatic primary key generation: supports up to 4 primary key strategies (including a distributed unique ID generator-Sequence), which can be freely configured to perfectly solve the primary key problem;
  • Support ActiveRecord mode: Support ActiveRecord form call, entity classes only need to inherit Model class to perform powerful CRUD operations;
  • Support custom global general operations: support global general method injection (Write once, use anywhere);
  • Built-in code generator: Use code or Maven plug-in to quickly generate Mapper, Model, Service, Controller layer code, support template engine, and more custom configurations for you to use;
  • Built-in paging plug-in: Based on MyBatis physical paging, developers do not need to care about specific operations. After configuring the plug-in, writing paging is equivalent to ordinary List query;
  • Paging plug-in supports multiple databases: supports MySQL, MariaDB, Oracle, DB2, H2, HSQL, SQLite, Postgre, SQLServer and other databases;
  • Built-in performance analysis plug-in: Sql statement and its execution time can be output. It is recommended to enable this function during development and testing to quickly detect slow queries;
  • Built-in global interception plug-in: Provides intelligent analysis and blocking of delete and update operations of the entire table, and can also customize interception rules to prevent misoperation.

1.4 Principle analysis

This article focuses on the core function of MyBatis-plus: SQL automatic injection function, process analysis and principle exploration.

2. Preparation

Start with a simple demo and feel the convenience of MyBatis-plus.

2.1 Basic interface BaseMapper

A basic interface is provided in the source code, which contains basic methods of adding, deleting, modifying and checking.

2.2 Create an entity class object

2.3 Business Interface UserMapper

In the business, the abstract interface is inherited according to the concrete entity object.

2.4 Test case

The console shows: MyBatis-plus finally automatically generated SQL statements for us. According to the above operation analysis: UserMapper inherits BaseMapper and has the deleteById method, but MyBatis-plus is an enhanced version based on mybatis. The is that you still need to provide specific SQL statements for database operations .

The following is a top-down analysis of how mybatis-plus generates business SQL and automatically injects it through debug.

3. Where are the SQL statements stored?

mappedStatements: Describe sql information

As shown in the figure below: mybatis has generated a proxy object for us, which contains some important attributes.

are as follows:

userMapper——>mybatisMapperProxy——>sqlSession——>sqlSessionFactory——>configuration——>mappedStatements——>mappedStatement——>sql statement

At this point we can find each SQL statement corresponds to a mappedStatement , mappedstatements stored in the configuration file (configuration is mybatis global configuration file that contains the data source, mapper, other configuration information) in.

4. When was the SQL statement injected?

4.1 AbstractMethod.addMappedStatement

Based on the above analysis, to know when the SQL statement is obtained, it is necessary to find the location where the mappedStatement is added. Trace to the abstract method of AbstractMethod.

Found the addMappedStatement() method

All methods of BaseMapper (deleteById, delete, insert, select, update, etc.) inherit this abstract method.

According to the mapper method (deleteById), the addDeleteMappedStatement method is obviously called.

Here we can find that the source code inherits AbstractMethod to implement different implementation classes according to different methods, and implements the injectMappedStatement method. sqlSource is also added to the configuration file in this place.

4.2 AbstractMethod.inject

Continue to study the AbstractMethod abstract class, the inject method realizes the action of automatically injecting SQL.

From the above source code, we can see that when the project is started, the default injector generates the basic CRUD implementation class object, and then traverses the implementation class list, injects the respective template SQL in turn, and finally adds it to the mapped statement.

5. How are SQL statements generated?

5.1 SQL template

There are two key parameters in the above method: SqlMethod and SqlSource;

Continue to study the source code and found that: sqlMethod is essentially an enumeration class that stores two key elements:

Method name in BaseMapper;

The SQL statement template corresponding to the method name (that is, the string wrapped by the <scripe> tag).

At this point, we have basically understood the essence of mybaits-plus's automatic sql generation: according to different methods, some general templates are provided, and then loaded into the mappedStatement after the project is started.

5.2 SqlSource

At this time, SqlSource constructs a SQL statement by parsing the SQL template, and the incoming table information and primary key information.

5.3 How is the database table information obtained?

Analyze the initTableName() method: the entity class information clazz is passed in the source code to obtain the table name information. In fact, the table name is obtained through the @TableName annotation on the entity;

When we define the entity class, we specify the table name corresponding to the entity class.

analysis initTableFields() method:

Get primary key and other field information

So far, the tableInfo information has been injected.

After studying the core process of parsing the mapper, let's briefly look at the process of adding the mapper file to the configuration (mybatis core configuration file).

Six, the process of adding the mapper file

ISqlInjector: Sql injector

MybatisMapperAnnotationBuilder: mapper parser

The parse method in MybatisMapperAnnotationBuilder gets sqlInjector (Sql injector) for SQL injection.

Mybatis adds the inherent process of mapper : MybatisMapperRegistry

Call the MapperAnnotionBuilder parser for parsing

MybatisConfiguration.addMapper

MybatisXMLConfigBuilder.mapperElemnt

MybayisXMLConfigBuilder.parseConfiguration

The analysis of the process of adding the mapper file is now complete.

Seven, summary

7.1 Process combing

The following summarizes the main process of mybatis-plus parsing mapper files and automatically injecting SQL.

7.2 The core idea of Mybatis-plus ORM

1) Entity classes and database tables are mapped one by one through custom annotations.

2) Object attributes and fields also use annotations to correspond one-to-one (note that the naming should be the same).

3) In order to improve the reusability, the specific mapper inherits the general add, delete, modify, and check interface.

4) Use template methods and object attribute values to dynamically splice SQL.

Eight, reference documents

MyBatis-plus official document: https://mp.baomidou.com/

Author: vivo Internet server team-Li Lei

vivo互联网技术
3.3k 声望10.2k 粉丝