MyBatisPlus overview
Required Foundation: MyBatis, Spring, Spring MVC
Why learn it? MyBatisPlus can save us a lot of working time, and all CRUD codes can be completed automatically!
JPA,tk-mapper,MyBatisPlus
Lazy!
brief introduction
What is it? MyBatis is meant to simplify JDBC operations!
Official website: Introduction | mybatis plus (Baidu. Com) , simplify Mybaits!
characteristic
- No invasion: it is only enhanced without change, and its introduction will not affect the existing project, which is as smooth as silk
- Low loss: the basic CURD will be injected automatically upon startup, with basically no loss of performance and direct object-oriented operation
- Powerful crud operation: built in general Mapper and general Service, most CRUD operations of a single table can be realized only through a small number of configurations, and there is a powerful condition constructor to meet various use requirements
- Support Lambda formal call: it is convenient to write various query conditions through Lambda expression, and there is no need to worry about wrong fields
- Support automatic generation of primary key: support up to 4 primary key strategies (including distributed unique ID generator - Sequence), which can be configured freely to perfectly solve the problem of primary key
- Support ActiveRecord mode: support ActiveRecord formal calls. Entity classes only need to inherit Model classes to perform powerful CRUD operations
- Support custom global general operations: support global general method injection (Write once, use anywhere)
- Built in code generator: code or Maven plug-in can be used to quickly generate Mapper, Model, Service and Controller layer code, support template engine, and have 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
- The paging plug-in supports multiple databases: MySQL, MariaDB, Oracle, DB2, H2, HSQL, SQLite, Postgre, SQLServer and other databases
- Built in performance analysis plug-in: it can output Sql statements and their execution time. It is recommended to enable this function during development and testing to quickly find out slow queries
- Built in global interception plug-in: it provides intelligent analysis and blocking of full table delete and update operations, and can also customize interception rules to prevent misoperation
Support database
Any database that can crud using mybatis and supports standard sql
Frame structure
[the external chain picture transfer fails, and the source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-lMZkyekV-1625980277476)(MyBatisPlus notes crazy God said Java. Assets / mybatis plus framework. JPG)]
Code hosting
Gitee (opens new window)| Github
Quick start
Using third-party components:
1. Import corresponding dependencies
2. Study how dependencies are configured
3. How to write the code
4. Improve and expand technical capability!
Steps:
-
Create database mybatis_plus
-
Create user table
DROP TABLE IF EXISTS user; CREATE TABLE user ( id BIGINT(20) NOT NULL COMMENT 'Primary key ID', name VARCHAR(30) NULL DEFAULT NULL COMMENT 'full name', age INT(11) NULL DEFAULT NULL COMMENT 'Age', email VARCHAR(50) NULL DEFAULT NULL COMMENT 'mailbox', PRIMARY KEY (id) ); --In real development, version(Le Guansuo) delete(Logical deletion) gmt_create,gmt_modified
The corresponding database Data script is as follows:
DELETE FROM user; INSERT INTO user (id, name, age, email) VALUES (1, 'Jone', 18, 'test1@baomidou.com'), (2, 'Jack', 20, 'test2@baomidou.com'), (3, 'Tom', 28, 'test3@baomidou.com'), (4, 'Sandy', 21, 'test4@baomidou.com'), (5, 'Billie', 24, 'test5@baomidou.com');
- Initialization project
Create an empty Spring Boot project
have access to Spring Initializer (opens new window) Quickly initialize a Spring Boot project
- Add dependency
<!-- Database driven --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.3</version> </dependency>
Note: we can save a lot of code by using mybatis plus. Try not to import mybatis and mybatis plus at the same time! Version difference!
- Configuration database
In application Add MySQL database configuration in the YML configuration file:
# mysql 5 drives different com mysql. jdbc. Driver # mysql 8 drives different com mysql. cj. jdbc. Driver, need to add time zone configuration serverTimezone=GMT%2B8 # DataSource Config spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver username: root password: 123456 url: jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
Add the @ MapperScan annotation in the Spring Boot startup class and scan the Mapper folder:
@SpringBootApplication @MapperScan("com.mingm.mapper") public class Application { public static void main(String[] args) { SpringApplication.run(QuickStartApplication.class, args); } }
- code
Write entity class user Java (used here) Lombok (opens new window) Simplified code)
@Data @AllArgsConstructor @NoArgsConstructor public class User { private Long id; private String name; private Integer age; private String email; }
Write Mapper class usermapper java
//Inherit the basic interface BaseMapper on the corresponding mapper @ResponseBody //Represents the persistence layer public interface UserMapper extends BaseMapper<User> { //All CRUD operations have been built in //You don't need a lot of configuration files like before }
- Start using
Add a test class for function test:
@SpringBootTest public class SampleTest { // It inherits BaseMapper, and all methods use their own parent class // We can also write our own extension methods! @Autowired private UserMapper userMapper; @Test public void testSelect() { System.out.println(("----- selectAll method test ------")); // The parameter is a Wrapper and a condition constructor. We don't need to use null here // Query all users List<User> userList = userMapper.selectList(null); Assert.assertEquals(5, userList.size()); userList.forEach(System.out::println); } }
The parameter of the selectList() method in UserMapper is the MP built-in conditional Wrapper, so it is unconditional if it is not filled in
Console output:
User(id=1, name=Jone, age=18, email=test1@baomidou.com)User(id=2, name=Jack, age=20, email=test2@baomidou.com)User(id=3, name=Tom, age=28, email=test3@baomidou.com)User(id=4, name=Sandy, age=21, email=test4@baomidou.com)User(id=5, name=Billie, age=24, email=test5@baomidou.com)
TIP
For a complete code example, please move to: Spring Boot quick start example (opens new window)| Spring MVC quick start example (opens new window)
Thinking?
1. Who wrote SQL for us? Mybatis plus is all written!
2. Where did the method come from? Mybatis plus is all written!
Configuration log
All our sql is invisible now. We want to know how it is executed, so we must look at the log
mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
After configuring the log, you need to pay attention to the automatically generated SQL in later learning, and you will like mybatis plus!
Crud & Extension
Insert operation
Insert insert
@Testpublic void testInsert() { User user = new User(); user.setName("Dainel"); user.setAge(3); user.setEmail("daniel@alibaba.com"); int result = userMapper.insert(user);// Help us automatically generate system id out. println(result);// Number of rows affected system out. println(user);// Discovery: id auto backfill}
The default value of the database insert id is the global unique id
Primary key generation strategy
Default ID_WORKER globally unique id
Generation of unique id of distributed system: https://www.cnblogs.com/haoxinyue/p/5208136.html
Snowflake algorithm:
snowflake is Twitter's open source distributed ID generation algorithm, and the result is a long ID. Its core idea is to use 41bit as the number of milliseconds, 10bit as the machine ID (5 bits are the data center and 5 bit machine ID), 12bit as the serial number within milliseconds (meaning that each node can generate 4096 IDS per millisecond), and finally there is a symbol bit, which is always 0. Can guarantee almost the only one in the world!
Primary key auto increment
We need to configure primary key auto increment:
- @ TableId(type = IdType.AUTO) on entity class field
- The database fields must be self incremented
- Just test again
The remaining IdType source code is explained
public enum IdType { AUTO(0), // Add NONE(1) to the database id. / / the primary key INPUT(2) is not set. / / enter it manually and write the id yourself_ Worker (3), / / default globally unique id UUID (4), / / globally unique id UUID id_ WORKER_ STR(5); // id_ Worker string notation}
update operation
//Test update @Testpublic void testUpdate () {User user = new User (); / / automatically splice SQL SQL user.setId (6L) by condition; user.setName ("pay attention to my WeChat official account"); (18); / / Note: but parameter is an object = = = (()); (()); (});}
All sql is automatically configured for you dynamically!
Auto fill
Creation time and modification time! These operations are generally completed automatically. We don't want to update them manually!
**Alibaba Development Manual: * * all database tables: gmt_create,gmt_modified almost all tables should be configured! And need automation!
Method 1: database level (database modification is not allowed during work)
-
Add a new field create in the table_ time,update_ time
-
To test the insertion method again, we need to synchronize the entity class first!
private LocalDateTime createTime;private LocalDateTime updateTime;
Just update the view results again.
Method 2: code level
- Delete the default value and update the database!
- Annotation needs to be added to the field attribute of the entity class
public class User { .... // be careful! Here, it needs to be marked as the filling field @ tablefield (fill = fieldfill. Insert) private date createtime@ TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime;}
- Write a processor to process this annotation! Custom implementation class MyMetaObjectHandler
@Slf4j@Component // Don't forget to add the processor to the IOC container. public class MyMetaObjectHandler implements MetaObjectHandler {/ / filling policy during insertion @ override public void insertfill (metaobject metaobject) {log.info ("start insert fill..."); this. strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); this. strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); // Starting version 3.3.0 (recommended) / / or / / this strictInsertFill(metaObject, "createTime", () -> LocalDateTime. now(), LocalDateTime. class); // Starting version 3.3.3 (recommended) / / or / / this fillStrategy(metaObject, "createTime", LocalDateTime.now()); // You can also use (3.3.0 this method has a bug)} / / the filling policy during update @ override public void updatefill (metaobject metaobject) {log.info ("start update fill..."); this. strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); // Starting version 3.3.0 (recommended) / / or / / this strictUpdateFill(metaObject, "updateTime", () -> LocalDateTime. now(), LocalDateTime. class); // Starting version 3.3.3 (recommended) / / or / / this fillStrategy(metaObject, "updateTime", LocalDateTime.now()); // You can also use (3.3.0 has a bug)}}
-
Test insertion
-
Test update, observation time
Source FieldFill
public enum FieldFill { /** * Do not process by default */ DEFAULT, /** * Insert fill field */ INSERT, /** * Update populated fields */ UPDATE, /** * Insert and update populated fields */ INSERT_UPDATE}
matters needing attention:
- The filling principle is to directly set the value for the attribute of entity!!!
- Annotation specifies that the attribute must have a value in the corresponding case. If there is no value, the receipt will be null
- The policies of the default methods provided by MetaObjectHandler are: if the property has a value, it will not be overwritten; if the filling value is null, it will not be filled
- The field must declare a TableField annotation, and the attribute fill selects the corresponding policy. This declaration tells mybatis plus that SQL fields need to be reserved and injected
- The population processor MyMetaObjectHandler needs to declare @ Component or @ Bean injection in Spring Boot
- To use the annotation fieldfill XXX is distinguished by field name and field type. The strictInsertFill or strictUpdateFill methods of the parent class must be used
- There is no need to distinguish the fillStrategy method that can use the parent class according to any
Optimistic lock
During the interview, we are often asked about optimistic lock and pessimistic lock.
Optimistic lock: as the name suggests, it always thinks that there will be no problem. No matter what you do, you don't lock it! If there is a problem, update the value test again!
Pessimistic lock: as the name suggests, it always thinks that there is always a problem and locks everything! Do it again!
Implementation of OptimisticLockerInnerInterceptor for optimistic lock:
- When fetching records, get the current version
- When updating, bring this version
- When updating, set version = newVersion where version = oldVersion
- If the version is not correct, the update fails. Optisticlockerinnerinterceptor
Optimistic lock: 1. Query first to obtain the version number version = 1-- Aupdate user set name = "MINGM", version = version + 1where id = 2 and version = 1-- B The thread finishes first. At this time version = 2,Will cause A Modification failed! update user set name = "MINGM", version = version + 1where id = 2 and version = 1
Steps:
- Add a version field to the database
- Add the @ Version annotation to the field of the entity class
@Version // Version annotation of optimistic lock private Integer version;
explain:
- The only supported data types are: int,Integer,long,Long,Date,Timestamp,LocalDateTime
- newVersion = oldVersion + 1 under integer type
- newVersion will be written back to entity
- Only updateById(id) and update(entity, wrapper) methods are supported
- Under the update(entity, wrapper) method, the wrapper cannot be reused!!!
- Register components and write Config files!
// Spring Boot mode@ Configuration@MapperScan("modify as needed") public class MybatisPlusConfig {/ * * * old version * / @ bean public optisticlockerinterceptor optisticlockerinceptor() {return new optisticlockerinceptor();} / * ** New version * / @ bean public mybatisplusinterceptor mybatisplusinterceptor() {mybatisplusinterceptor mybatisplusinterceptor = new mybatisplusinterceptor(); mybatisplusinterceptor.addinnerinterceptor (New optimizationlockerinnerinterceptor()); / / optimistic lock plug-in return mybatisPlusInterceptor;}}
- Test it and check the test results.
// Successfully test optimistic lock @ test public void testversionsuccess() {/ / 1. Query user information user user user = usermapper.selectbyid (1L); / / 2. Modify user information user.setname ("fan"); user.setage (24); / / 3. Update userMapper.updateById(user);}// Test lock update failed! @ test public void testversionfall() {/ / thread 1 user user1 = usermapper.selectbyid (1L); user1.setname ("fan111"); user1.setage (14); / / thread 2 simulates another thread to perform queue jumping operation, user user2 = usermapper.selectbyid (1L); user2.setname ("fan222"); user2.setage (24) ; userMapper. updateById(user2); // Spin lock to multiple attempts to submit! userMapper.updateById(user1); // If there is no optimistic lock, it will overwrite the value of the queue jumping thread}
Paging query
Pagination is used as much as ten times in the website!
-
The original uses limit for paging
-
pageHelper third party plug-in
-
MybatisPlus has a built-in paging plug-in
How to use?
- Add a paging plug-in to the configuration file
// Latest version @ bean public mybatisplusinterceptor mybatisplusinterceptor() {mybatisplusinterceptor interceptor = new mybatisplusinterceptor(); mybatisplusinterceptor.addinnerinterceptor (New optimizationlockerinnerinterceptor()); / / optimistic lock plug-in interceptor.addinnerinterceptor (New paginationinnerinnerinterceptor (dbtype. MySQL) );// Paging plugin return interceptor;}
- UserMapper.xml is equivalent to writing a common list query, and mybatis plus automatically pages for you
<mapper namespace="com.mingm.mapper.UserMapper"> <select id="selectPageVo" resultType="com.mingm.pojo.User"> SELECT * FROM user WHERE age=#{age} </select></mapper>
- Directly use the Page object to call paging!
@Test public void testPage(){ // Parameter 1: current page. / / parameter 2: page size. / / after using the paging plug-in, all paging operations become simple! Page<User> page = new Page<>(2,5); userMapper. selectPage(page,null); page. getRecords(). forEach(System.out::println); System. out. println(page.getTotal()); } @ Test public void selectuserpage() {/ / do not perform count sql optimization to solve the problem that MP cannot automatically optimize SQL. At this time, you need to query the count part yourself. / / page.setOptimizeCountSql(false); / / when total is less than 0 or set setSearchCount(false) The paging plug-in will not perform count query. / / key points!! The object returned by paging and the object passed in are the same page < user > userpage = new page < > (1,2); userMapper. selectPageVo(userPage,11);// Here 11 are the custom parameters in xml! userPage.getRecords().forEach(System.out::println); System.out.println(userPage.getTotal()); }
Delete operation
// Test delete @ testpublic void testdelete() {usermapper. Deletebyid (6L);}// Test batch delete @ testpublic void testdeletebatchid() {usermapper.deletebatchids (arrays. Aslist (1287326823914405893l, 1287326823914405894l));}// Delete @ testpublic void testdeletebymap() {HashMap < string, Object > map = new HashMap < > (); map.put ("name", "mingm"); usermapper.deletebymap (map);}
We will encounter some problems in our work: logical deletion!
Logical deletion
Physical delete: remove directly from the database
Logical deletion: it is not removed from the database, but made effective through a variable! deleted=0 --> deleted=1
Administrators can view deleted records! Prevent data loss! Similar to recycle bin!
Field type support Description:
- All data types are supported (integer, Boolean, localdatetime are recommended)
- If the database field uses datetime, the logical undeleted value and deleted value can be configured as string null, and the other value can be configured as a function to obtain the value, such as now()
Appendix:
- Logical deletion is a scheme to facilitate data recovery and protect the value of data itself, but it is actually deletion.
- If you need to check it frequently, you should not use logical deletion, but represent it in a state.
Test:
-
Add a deleted field in the database table
-
Add @ TableLogic annotation to the entity class field
@TableLogic // private Integer deleted;
- Configure com baomidou. mybatisplus. core. config. GlobalConfig$DbConfig
- application.yml
mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl mapper-locations: mybatis-plus/*.xml global-config: db-config:# logic-delete-field: deleted # Global logically deleted entity field name(since 3.3.0,Step 2 can be ignored after configuration) logic-delete-value: 1 # Logical deleted value(The default is 1) logic-not-delete-value: 0 # Logical undeleted value (default is 0)
- Just test!
The record is still in the database, but the value has changed!
Conditional constructor
Very important: wrapper
We can use it instead of writing some complex sql!
Test!
package com.mingm.springbootmybatisplus; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.mingm.mapper.UserMapper; import com.mingm.pojo.User; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.ArrayList; import java.util.List; import java.util.Map; @SpringBootTest public class WrapperTests { @Autowired private UserMapper userMapper; @Test void test1(){ //Query the user whose name is not empty and whose mailbox is not empty. The user's age is greater than or equal to 12 QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.isNotNull("name") .isNotNull("email") //email field is not empty .ge("age",12); //Greater than or equal to >= userMapper.selectList(wrapper).forEach(System.out::println); } @Test void test2(){ //Query name Jack QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.eq("name", "Jack"); //SELECT id,name,age,email,version,create_time,update_time,deleted FROM user WHERE deleted=0 AND (name = ?) User user = userMapper.selectOne(wrapper); System.out.println(user); } @Test void test3(){ //Query the number of users aged between 19 and 21, including! QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.between("age", 20, 21); //section //SELECT COUNT( * ) FROM user WHERE deleted=0 AND (age BETWEEN ? AND ?) // Integer count = userMapper.selectCount(wrapper); ArrayList<User> userList = (ArrayList<User>) userMapper.selectList(wrapper); //SELECT id,name,age,email,version,create_time,update_time,deleted FROM user WHERE deleted=0 AND (age BETWEEN ? AND ?) userList.forEach(System.out::println); System.out.println(userList.size()); } //Fuzzy query @Test void test4(){ //Query users aged between 19 and 30 QueryWrapper<User> wrapper = new QueryWrapper<>(); //left and right wrapper.notLike("name", "A") .likeRight("email", "t"); // Preparing: SELECT id,name,age,email,version,create_time,update_time,deleted FROM user // // WHERE deleted=0 AND (name NOT LIKE ? AND email LIKE ?) // ==> Parameters: %b%(String), t%(String) List<Map<String, Object>> maps = userMapper.selectMaps(wrapper); maps.forEach(System.out::println); } @Test void test5(){ QueryWrapper<User> wrapper = new QueryWrapper<>(); //id is found in the sub query wrapper.inSql("id", "select id from user where id < 3"); List<Object> objects = userMapper.selectObjs(wrapper); objects.forEach(System.out::println); } @Test void test6(){ QueryWrapper<User> wrapper = new QueryWrapper<>(); //Sort by id wrapper.orderByDesc("id"); List<User> users = userMapper.selectList(wrapper); users.forEach(System.out::println); } @Test void test7(){ QueryWrapper<User> wrapper = new QueryWrapper<>(); //id is found in the sub query wrapper.inSql("id", "select id from user where id < 5"); //SELECT id,name,age,email,version,create_time,update_time,deleted FROM user // // WHERE deleted=0 AND (id IN (select id from user where id < 5)) List<Object> objects = userMapper.selectObjs(wrapper); objects.forEach(System.out::println); } @Test void test8(){ QueryWrapper<User> wrapper = new QueryWrapper<>(); //Sort by id wrapper.orderByDesc("id"); List<User> users = userMapper.selectList(wrapper); users.forEach(System.out::println); } }
The rest of the test, you can go down and practice more!
Automatic code generator
Code generator | mybatis plus (Baidu. Com)
AutoGenerator is the code generator of mybatis plus. Through AutoGenerator, you can quickly generate the code of Entity, Mapper, Mapper XML, Service, Controller and other modules, which greatly improves the development efficiency.
- Presentation rendering:
- Crazy version configuration
public class Code { public static void main(String[] args) { //You need to build an automatic code generator object // Code generator AutoGenerator mpg = new AutoGenerator(); //Configuration policy //1. Global configuration GlobalConfig gc = new GlobalConfig(); String projectPath = System.getProperty("user.dir"); gc.setOutputDir(projectPath + "/src/main/java"); gc.setAuthor("ChanV"); gc.setOpen(false); gc.setFileOverride(false); //Overwrite gc.setServiceName("%sService"); //I prefix to Service gc.setIdType(IdType.ID_WORKER); gc.setDateType(DateType.ONLY_DATE); gc.setSwagger2(true); mpg.setGlobalConfig(gc); //2. Set data source DataSourceConfig dsc = new DataSourceConfig(); dsc.setUrl("jdbc:mysql://localhost:3306/mybatis-plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8"); dsc.setDriverName("com.mysql.cj.jdbc.Driver"); dsc.setUsername("root"); dsc.setPassword("root"); dsc.setDbType(DbType.MYSQL); mpg.setDataSource(dsc); //3. Package configuration PackageConfig pc = new PackageConfig(); pc.setModuleName("blog"); pc.setParent("com.chanv"); pc.setEntity("pojo"); pc.setMapper("mapper"); pc.setService("service"); pc.setController("controller"); mpg.setPackageInfo(pc); //4. Policy configuration StrategyConfig strategy = new StrategyConfig(); strategy.setInclude("user"); //Set the table name to map strategy.setNaming(NamingStrategy.underline_to_camel); strategy.setColumnNaming(NamingStrategy.underline_to_camel); strategy.setEntityLombokModel(true); //Automatic lombok strategy.setLogicDeleteFieldName("deleted"); //Auto fill configuration TableFill createTime = new TableFill("create_time", FieldFill.INSERT); TableFill updateTime = new TableFill("update_time", FieldFill.UPDATE); ArrayList<TableFill> tableFills = new ArrayList<>(); tableFills.add(createTime); tableFills.add(updateTime); strategy.setTableFillList(tableFills); //Optimistic lock strategy.setVersionFieldName("version"); strategy.setRestControllerStyle(true); strategy.setControllerMappingHyphenStyle(true); //localhost:8080/hello_id_2 mpg.setStrategy(strategy); mpg.execute(); //Execute code constructor } }