MybatisPlus
MyBatis-Plus (opens new window) (MP) is a MyBatis (opens new window) On the basis of MyBatis, the enhancement tool is only enhanced without change. It is born to simplify development and improve efficiency.
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 primary key problem
- 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
Frame structure
Quick start
Quick start Quick start | mybatis plus (Baidu. Com)
step
1. Create database
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(Optimistic lock),daleted(Logical deletion),gmt_craete,gmt_modified
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');
2. Initialize the project, create a new springboot project, and import dependencies
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.0</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
Note: we use mybatis -Plus can save us a lot of code. Try not to import mybatis and mybatis plus at the same time!
3. Connect to the database
#mysql5 spring.datasource.username=root spring.datasource.password=123456 spring.datasource.url=jdbc:mysql://localhost:3306/mybatisplus?userSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
4. POJO Dao (configure mapper. XML) - service controller
package com.example.mybatisplsh01.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor public class User { private int id; private String name; private int age; private String email; }
mapper layer
package com.example.mybatisplsh01.dao; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.example.mybatisplsh01.pojo.User; import org.apache.ibatis.annotations.Mapper; import org.springframework.stereotype.Repository; //Just implement the corresponding interface on the corresponding mapper //Represents Dao layer @Repository public interface UserMapper extends BaseMapper<User> { //All CRUD are written }
main program
package com.example.mybatisplsh01; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; //Scan dao files @MapperScan("com.example.mybatisplsh01.dao") @SpringBootApplication public class MybatisPlsh01Application { public static void main(String[] args) { SpringApplication.run(MybatisPlsh01Application.class, args); } }
Note: we need to scan all interfaces under our mapper package on the main startup class
test
package com.example.mybatisplsh01; import com.example.mybatisplsh01.dao.UserMapper; import com.example.mybatisplsh01.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.List; @SpringBootTest class MybatisPlsh01ApplicationTests { @Autowired private UserMapper userMapper; @Test void contextLoads() { //All methods come from the parent class, and we can also write extension methods //The parameter here is a mapper and a condition constructor. We don't need it here, but write it as null List<User> userList = userMapper.selectList(null); for (User user : userList) { System.out.println(user); } } }
Configuration log
All our sqld is invisible now. We want to know how it is executed, so we must look at the log!
## Configuration log ##Default console output 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
CRUD extension
Insert operation
Insert insert
@Test void insert(){ User user = new User(); user.setAge(3); user.setName("xiaoqi"); user.setEmail("123456"); int insert = userMapper.insert(user); System.out.println(insert); System.out.println(user); }
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 (five bits are the data center and five 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
The self incrementing id is set in java, and the database needs to be synchronized, otherwise an error will be reported
problem
If there is a row with id 0 in the database, you need to set the id of this row to a number greater than 0, and then click auto increment.
Other codes
public enum IdType { /** * Database ID self increment * This type is invalid if ID self increment is set in the database */ AUTO(0), /** * This type is not set with primary key (in the annotation, it is equal to follow the global, and the global value is equal to INPUT) */ NONE(1), /** * User input ID * This type can be filled by registering the auto fill plug-in */ INPUT(2), /* The following three types are automatically filled only when the inserted object ID is empty. */ /** * Assign ID (the primary key type is number or string), * Default implementation class {@ link com. Baomidou. Mybatisplus. Core. Increment. Defaultidentifier generator} (snowflake algorithm) * * @since 3.3.0 */ ASSIGN_ID(3), /** * Assign UUID (the primary key type is string) * Default implementation class {@ link com. Baomidou. Mybatisplus. Core. Increment. Defaultidentifiergenerator} (UUID. Replace ("-", "")) */ ASSIGN_UUID(4);
update operation
Although it is byid, the updated is user
@Test void update(){ //Automatic splicing dynamic sql User user = new User(); user.setAge(3); user.setId(6); user.setName("xx"); user.setEmail("123456"); //A parameter is an object int i = userMapper.updateById(user); System.out.println(i); }
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)
1. Add a new field in the table: create_ time update_ time
2. To test the insertion method again, we need to synchronize in the entity class
private Date createTime; private Date updateTime;
Method 2: code level
1. Delete the default value of the database
2. Add annotation on the field attribute of entity class
//Field fill content @TableField(fill = FieldFill.INSERT) private Date createrTime; @TableField(fill = FieldFill.UPDATE) private Date updateTime;
3. Write the processor to process this annotation
package com.example.mybatisplsh01.handler; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.reflection.MetaObject; import org.springframework.stereotype.Component; import java.util.Date; @Component //Add components to IOC container @Slf4j public class MyMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { //Population policy at insertion log.info("start instert fill"); this.setFieldValByName("createTime",new Date(), metaObject); this.setFieldValByName("updateTime",new Date(), metaObject); } @Override public void updateFill(MetaObject metaObject) { //Population policy when updating log.info("start update"); this.setFieldValByName("updateTime",new Date(),metaObject); } }
4. Test insertion and update
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!
Optimistic lock implementation method:
- When fetching records, get the current version
- When updating, bring this version
- When updating, set version = newVersion where version = oldVersion
- If the version is incorrect, the update fails
Test the optimistic lock plug-in of mybatisplus
1. Add the version field to the database and set the default value to 1
2. Add field in entity class
@Version //Optimistic lock annotation private Integer version;
3. Register components
package com.example.mybatisplsh01.config; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.annotation.EnableTransactionManagement; //Mybatis plus is managed here //Scan dao files for components to take effect @MapperScan("com.example.mybatisplsh01.dao") @EnableTransactionManagement //Transaction start @Configuration //Configuration class public class MybatisPlusConfig { //Register optimistic lock plug-in @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return interceptor; } }
4. Test
@Test //Test optimistic lock void testOpt(){ User user = userMapper.selectById(1); //Modify user information user.setName("xxxx"); user.setEmail("78952"); userMapper.updateById(user); }
Failure example, simulate thread queue jumping
@Test //Test optimistic lock void testOpt(){ //Thread 1 User user = userMapper.selectById(1); //Modify user information user.setName("xxxx"); user.setEmail("78952"); //Simulate another thread to perform queue jumping User user2 = userMapper.selectById(1); //Modify user information user2.setName("xxxx2"); user2.setEmail("78952"); userMapper.updateById(user2); //Spin lock operation can be used userMapper.updateById(user); //If there is no optimistic lock, the value of the insert thread is overwritten }
Query operation
@Test //Test query void testselect(){ User user = userMapper.selectById(1); System.out.println(user); }
Batch query, using array tool class
@Test //Test query void testselect(){ List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3)); users.forEach(System.out::println); }
Conditional query, using map
@Test //Condition query void testSelectmap(){ HashMap<String, Object> map = new HashMap<>(); //Customize the conditions to query map.put("name","xx"); List<User> users = userMapper.selectByMap(map); users.forEach(System.out::println); }
Paging query
Paged websites are frequently used
- The original uses limit for paging
- pageHelper third party plug-in
- MybatisPlus has a built-in paging plug-in
How to use?
1. Configure interceptor
/** * The new paging plug-in follows the rules of mybatis. MybatisConfiguration#useDeprecatedExecutor = false is required * Avoid cache problems (this attribute will be removed after the old plug-in is removed) */ interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
2. Test direct use
@Test void limitpage(){ //Parameter 1, current page, parameter 2, page size Page<User> page = new Page<>(2,5); userMapper.selectPage(page,null); page.getRecords().forEach(System.out::println); }
Delete operation
Basic delete operation
@Test void delete(){ int deleteById = userMapper.deleteById(9); System.out.println(deleteById); } @Test void deletebatch(){ userMapper.deleteBatchIds(Arrays.asList(6,7)); } @Test void deleteMap(){ HashMap<String,Object> map = new HashMap(); map.put("name","xxxx2"); userMapper.deleteByMap(map); }
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 to prevent data loss, similar to the recycle bin
Test:
1. Add a deleted field in the data table
2. Add attribute in entity class
@TableLogic //Logical deletion private Integer deleted;
3. Configuration
# Global logically deleted entity field name (since 3.3.0, it can be ignored after configuration, and step 2 is not configured) # Logical deleted value (default = 1) # Logical undeleted value (0 by default) mybatis-plus.global-config.db-config.logic-delete-field=flag mybatis-plus.global-config.db-config.logic-delete-value=1 mybatis-plus.global-config.db-config.logic-not-delete-value= 0
The test performed for delete is an update operation, not a delete operation
In the database, the field deleted becomes 1, but the data is not deleted
Test query, the result is not found
The logically deleted fields will be automatically filtered during query
Performance analysis plug-in
In our usual development, we will encounter some slow sql. Solution: testing, druid monitoring
Function: performance analysis interceptor, used to output each SQL statement and its execution time
MyBatisPlus also provides a performance analysis plug-in. If it exceeds this time, it will stop running!
Conditional constructor
Very important: wrapper
We can use it instead of writing some complex sql!
test
package com.example.mybatisplsh01; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.example.mybatisplsh01.dao.UserMapper; import com.example.mybatisplsh01.pojo.User; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import javax.management.Query; @SpringBootTest public class WrapperTest { @Autowired private UserMapper userMapper; @Test void contextLoads(){ //Query parameters whose name and mailbox are not empty, and whose age is greater than or equal to 12 QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper .isNotNull("name") .isNotNull("email") .ge("age",12); userMapper.selectList(wrapper).forEach(System.out::println); } }
@Test void test2(){ //The query name is a specific name QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.eq("name","Tom"); System.out.println(userMapper.selectOne(wrapper)); }
@Test void test3(){ QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.between("age",20,30); Long aLong = userMapper.selectCount(wrapper); System.out.println(aLong); }
@Test //Fuzzy query void test4(){ QueryWrapper<User> wrapper = new QueryWrapper<>(); //Left and right% e% wrapper.notLike("name","e") //Right represents% on the right .likeRight("email","t"); List<Map<String, Object>> maps = userMapper.selectMaps(wrapper); maps.forEach(System.out::println); }
@Test void test5(){ QueryWrapper<User> wrapper = new QueryWrapper<>(); //Linked table query //You can find the id in this select statement wrapper.inSql("id","select id from user where id > 4"); 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); }
official: Conditional constructor | mybatis plus (baomidou. Com)
Automatic code generator
dao, pojo, service and controller are all written by myself!
AutoGenerator is the code generator of mybatis plus, which can quickly generate Entity
The code of Mapper, Mapper XML, Service, Controller and other modules greatly improves the development efficiency.
You only need to change the entity class name, package name and database configuration
import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.generator.AutoGenerator; import com.baomidou.mybatisplus.generator.config.DataSourceConfig; import com.baomidou.mybatisplus.generator.config.GlobalConfig; import com.baomidou.mybatisplus.generator.config.PackageConfig; import com.baomidou.mybatisplus.generator.config.StrategyConfig; import com.baomidou.mybatisplus.generator.config.po.TableFill; import com.baomidou.mybatisplus.generator.config.rules.DateType; import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; import java.util.ArrayList; // Automatic code generator public class KuangCode { public static void main(String[] args) { // You need to build an automatic code generator object 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("Madness theory"); 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/kuang_community? useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8"); dsc.setDriverName("com.mysql.cj.jdbc.Driver"); dsc.setUsername("root"); dsc.setPassword("123456"); dsc.setDbType(DbType.MYSQL); mpg.setDataSource(dsc); //3. Package configuration PackageConfig pc = new PackageConfig(); //You only need to change the entity class name, package name and database configuration pc.setModuleName("blog"); pc.setParent("com.kuang"); pc.setEntity("entity"); pc.setMapper("mapper"); pc.setService("service"); pc.setController("controller"); mpg.setPackageInfo(pc); //4. Policy configuration StrategyConfig strategy = new StrategyConfig(); strategy.setInclude("blog_tags","course","links","sys_settings","user_record"," user_say"); // 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 gmtCreate = new TableFill("gmt_create", FieldFill.INSERT); TableFill gmtModified = new TableFill("gmt_modified", FieldFill.INSERT_UPDATE); ArrayList<TableFill> tableFills = new ArrayList<>(); tableFills.add(gmtCreate); tableFills.add(gmtModified); strategy.setTableFillList(tableFills); // Optimistic lock strategy.setVersionFieldName("version"); strategy.setRestControllerStyle(true); strategy.setControllerMappingHyphenStyle(true); // localhost:8080/hello_id_2 mpg.setStrategy(strategy); mpg.execute(); //implement } }