Official website: MyBatis-Plus (baomidou.com)
=Introduction
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.
vision
Our vision is to be the best partner of MyBatis, just like Contra 1P and 2P in, and the efficiency is doubled.
# 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 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
Address: Quick start | mybatis plus (Baidu. Com)
step
- Create database mybatis_plus
- Create user table and insert data
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) )ENGINE=INNODB DEFAULT CHARSET=utf8 ; -- In real development, version(Optimistic lock),deleted(Logical deletion),gmt_create(Creation time),gmt_modifed((modification time) INSERT INTO USER (id, NAME, age, email) VALUES (1, 'Jone', 18, '[email protected]'), (2, 'Jack', 20, '[email protected]'), (3, 'Tom', 28, '[email protected]'), (4, 'Sandy', 21, '[email protected]'), (5, 'Billie', 24, '[email protected]');
-
Write project, initialize project! Initialize with SpringBoot!
-
Import dependency
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- mybatisPlus rely on--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.0.5</version> </dependency>
Note: do not import mybatis and mybatisplus at the same time. There may be version conflicts;
-
Connect to the database. This step is the same as mybatis
#The mysql8 driver is different from mysql5. You need to add a time zone spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8 spring.datasource.password=onmyown123 spring.datasource.username=root spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
6. The traditional method is POJO Dao (connect to the database and configure mapper. XML) - service controller
6. After using mybatis plus
-
pojo
@Data @AllArgsConstructor @NoArgsConstructor @ToString public class User { private Long id ; private String name ; private int age ; private String email; };
-
mapper
//Inherit the basic class BaseMapper on the corresponding mapper @Mapper public interface UserMapper extends BaseMapper<User> { //Once Dao is configured, just call the parent method directly }
-
test
@SpringBootTest class MybatisPlusApplicationTests { @Autowired private UserMapper userMapper; @Test void contextLoads() { List<User> users = userMapper.selectList(null); users.forEach(System.out::println); } }
The sql and method mybatisPlus have been written for us
The above entry case clearly shows the advantages of built-in mapper and powerful CRUD operation
Configuration logOur current sql and methods are not visible. We want to know how it is executed, so we need to look at the log!
#Configuration log mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImplCRUD extension
Insert operation
Insert insert
@Test void testInsert(){ User user = new User(); user.setName("Bao Bao Feng"); user.setAge(100); user.setEmail("[email protected]"); int insert = userMapper.insert(user); System.out.println(insert); System.out.println(user); }
Test:
(note here that the id parameter type of the User entity class is Long(long is not a wrapper class, and MybatisPlus will automatically fill in 0))
We found that the id was automatically filled with a long number
The default value of the id inserted in the database is the global unique id
Primary key generation strategy
Generation of unique id of distributed system: Distributed system unique ID generation scheme Summary - nick hao - blog Park (cnblogs.com)
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.
Primary key auto increment
We need to configure primary key auto increment
-
The id of the database table type is changed to auto increment
-
Add @ TableId(type = IdType.AUTO) to the entity class id
-
Test the insertion again
After this setting, there is no need to set the primary key id, and mybatisPlus can also help us increase the id automatically
Other source code explanations
public enum IdType { AUTO, //Database id self increment NONE, //No primary key set INPUT, //Manual input ID_WORKER, //The default globally unique id is a numeric representation UUID, //Globally unique id uuid ID_WORKER_STR;//The default globally unique id string representation private int key; private IdType(int key) { /* compiled code */ } public int getKey() { /* compiled code */ } }
update operation
update
@Test void testUpdate(){ User user = new User(); user.setId(6L); user.setName("Bao Bao Feng"); user.setAge(100); int i = userMapper.updateById(user); }
Test results:
Automatically complete dynamic sql settings!
Auto fill
The creation time and modification time are all completed automatically. We don't want to update them manually
Alibaba Development Manual: all database tables: gtm_create,gtm_modified almost all tables should be configured! And need automation!
Database level (database is not allowed to be modified during work)
- Add create in the user table_ time,update_ Time field
-
Test the modification method again, and synchronize the entity class first
@Test void testUpdate(){ User user = new User(); user.setId(6L); user.setName("Ruan Feng"); user.setAge(80); int i = userMapper.updateById(user); System.out.println(i); }
Then update_time is automatically updated after modifying the code execution!
Code level
- Create the database first_ time,update_ Remove the default value of time, and then update_time update
- Add the annotation @ tablefile on the field of the entity class
//Add padding to fields @TableField(fill = FieldFill.INSERT) private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime;
- Edit the processor to process these annotations~
@Slf4j @Component public class MyMetaObjectHandler implements MetaObjectHandler { //Auto fill for insert operation @Override public void insertFill(MetaObject metaObject) { //default MetaObjectHandler setFieldValByName(String fieldName, Object fieldVal, MetaObject metaObject) log.info("start insertFill"); this.setFieldValByName("createTime",new Date(),metaObject); this.setFieldValByName("updateTime",new Date(),metaObject); } //Automatic population of update operations @Override public void updateFill(MetaObject metaObject) { log.info("start updateFill"); this.setFieldValByName("updateTime",new Date(),metaObject); } }
-
Test insertion
-
Test update, observe time!
Optimistic lock
Optimistic lock: no matter what you do, there will be no problem. No matter what you do, you won't lock it. Wait until there is a problem, and then update the value test
Pessimistic lock: everything will have problems. No matter what you do, you will lock it,
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
Optimistic lock understanding:
-- Suppose there are two threads -- A: update user set name="abc",set version=2 where id=2 and version=1; -- B: update user set name="abc",set version=2 where id=2 and version=1; -- hypothesis B Thread preemptive completion, version=2,that A The thread will fail
Optimistic lock implementation
-
Add the version field to the database and set the default value to 1
-
Add version field to entity class
@Version private Integer version;
-
Register components
//Scan Mapper package @MapperScan("com.yfm.mapper") @EnableTransactionManagement @Configuration public class MybatisPlusConfig { //Register optimistic lock plug-in @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor() { return new OptimisticLockerInterceptor(); } }
-
Test it!
//Test optimistic lock successful @Test void testOptimisticLocker(){ User user = userMapper.selectById(1L); user.setName("Tongtian book"); userMapper.updateById(user); } //Failed to test optimistic lock, multithreading @Test void testOptimisticLocker2(){ //Thread A User user2 = userMapper.selectById(1L); user2.setName("Tongtian Book 22"); //Thread B, queue jump update User user3 = userMapper.selectById(1L); user3.setName("Tongtian Book 33"); userMapper.updateById(user3); //When userMapper.updateById(user3); After execution, version=3, and the queried version=2, so the following will fail userMapper.updateById(user2); }
5. Test results
==> Preparing: UPDATE user SET name=?, age=?, email=?, version=?, create_time=?, update_time=? WHERE id=? AND version=? ==> Parameters: Tongtian Book 33(String), 18(Integer), [email protected](String), 3(Integer), 2021-11-21 17:04:33.0(Timestamp), 2021-11-22 09:22:57.102(Timestamp), 1(Long), 2(Integer) <== Updates: 1 Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@7afb9c93] Creating a new SqlSession SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@f4f843f] was not registered for synchronization because synchronization is not active 2021-11-22 09:22:57.116 INFO 17600 --- [ main] com.yfm.handler.MyMetaObjectHandler : start updateFill JDBC Connection [HikariProxyConnection@2069378030 wrapping com.mysql.cj.jdbc.ConnectionImpl@6eed46e9] will not be managed by Spring ==> Preparing: UPDATE user SET name=?, age=?, email=?, version=?, create_time=?, update_time=? WHERE id=? AND version=? ==> Parameters: Tongtian Book 22(String), 18(Integer), [email protected](String), 3(Integer), 2021-11-21 17:04:33.0(Timestamp), 2021-11-22 09:22:57.116(Timestamp), 1(Long), 2(Integer) <== Updates: 0
Query operation
@Test void testSelectById(){ User user = userMapper.selectById("1L"); System.out.println(user); } //Test batch query @Test void testSelectByBatchIds(){ List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 6)); users.forEach(System.out::println); } //Test Map multi condition query @Test void testSelectByMap(){ HashMap<String, Object> map = new HashMap<String,Object>(); map.put("name","Rootless"); map.put("age",70); List<User> users = userMapper.selectByMap(map); users.forEach(System.out::println); }
Paging query
Pagination is used a lot on websites
- Original limit
- PageHelper third party paging plug-in-
- MP also has a built-in paging plug-in!
How to use
-
Configure interceptor components
@Bean public PaginationInterceptor paginationInterceptor() { PaginationInterceptor paginationInterceptor = new PaginationInterceptor(); // After setting the requested page to be larger than the maximum page, true will be called back to the home page, false will continue to request, and the default is false // paginationInterceptor.setOverflow(false); // Set the maximum number of single page restrictions, 500 by default, - 1 unlimited // paginationInterceptor.setLimit(500); // Turn on the join optimization of count, only for some left join s //paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true)); return paginationInterceptor; }
-
Directly use the Page object!
//Test paging @Test void testPage(){ //Parameter 1: current page //Parameter 2: size per page Page<User> userPage = new Page<>(1,5); userMapper.selectPage(userPage,null); //Printout userPage.getRecords().forEach(System.out::println); //Tip: the total number of records in the table will be queried before paging }
Delete operation
Basic delete operations:
deleteById deleteBatchIds deleteByMap
Logical deletion
Physical delete: delete directly from the database
Logical deletion: it is not removed from the database, but invalidated by a variable! deleted=0 => deleted=1
Administrators can view deleted records! Preventing data loss is equivalent to the function of recycle bin. If you really want to delete, delete the data through deleted=1
Test:
1. Add the deleted field to the database
2. Add the deleted attribute in the entity class
@TableLogic //Logical deletion private Integer deleted;
3. Logically delete configuration and register components
//There is no need to inject since MP3.1.1 //Logical delete component @Bean public ISqlInjector sqlInjector(){ return new LogicSqlInjector(); }
#Configure logical deletion mybatis-plus.global-config.db-config.logic-delete-value=1 mybatis-plus.global-config.db-config.logic-not-delete-value=0
4. Test delete
@Test void testDelete(){ int i = userMapper.deleteById(1462326539445100546L); System.out.println(i); }
Result: the delete operation is changed to the update operation, and the deleted field is modified!
At this time, you can't find it again. The deleted=0 condition will be added during query!
Performance analysis plug-in
In our usual development, we will encounter some slow sql.
MP also provides a performance analysis plug-in. If it exceeds this time, it will stop
Steps:
1. Import components
//This component has been removed in the new version @Bean @Profile({"dev","test"}) //Set the dev test environment on to ensure our efficiency public PerformanceInterceptor performanceInterceptor(){ PerformanceInterceptor performanceInterceptor=new PerformanceInterceptor(); performanceInterceptor.setMaxTime(100);//ms sets the maximum time for sql execution. If it exceeds, it will not be executed performanceInterceptor.setFormat(true); //sql format print return performanceInterceptor; }
Note that add the development environment settings in application.properties
#Set as development environment spring.profiles.active=dev
2. Test use!
Conditional constructor
Very important: Wrapper
We can write some complex sql and use it instead!
Test:
Test 1:
@Test void contextLoads() { //Note that you should add generics. You can't add conditions in a chain without adding generics! QueryWrapper<User> wrapper = new QueryWrapper<>(); //Test and query records whose age is greater than 30, email address and name are not empty wrapper .isNotNull("email") .isNotNull("name") //ge: greater than or equal to. .ge("age",30); //Compare it with the Map we just learned userMapper.selectList(wrapper).forEach(System.out::println); }
Test 2:
@Test void test2() { //Query records with age equal to 18 QueryWrapper<User> wrapper = new QueryWrapper<>(); //Note that you should add generics. You can't add conditions in a chain without adding generics! wrapper .eq("age",18); User user = userMapper.selectOne(wrapper);//Query a data. Multiple results appear. Use List or Map System.out.println(user); }
Test 3:
@Test void test3() { //Query the number of results aged between 18 and 50 QueryWrapper<User> wrapper = new QueryWrapper<>(); //Note that you should add generics. You can't add conditions in a chain without adding generics! wrapper .between("age",18,50); Integer count = userMapper.selectCount(wrapper); System.out.println(count); }
Test 4: remember to view the output SQL for analysis
//Fuzzy query @Test void test4() { //Note that you should add generics. You can't add conditions in a chain without adding generics! QueryWrapper<User> wrapper = new QueryWrapper<>(); //Right and left of like here mean '%' in right or left wrapper.likeRight("email","t") //Query mailbox records starting with t .notLike("name","t"); //Name does not contain t List<Map<String, Object>> maps = userMapper.selectMaps(wrapper); maps.forEach(System.out::println); }
-- Execute SQL: SELECT id, name, age, email, version, deleted, create_time, update_time FROM user WHERE deleted=0 AND email LIKE 't%' AND name NOT LIKE '%t%'
Test 5:
//Subquery @Test void test5() { //Note that you should add generics. You can't add conditions in a chain without adding generics! 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); }
result:
Execute SQL: SELECT id, name, age, email, version, deleted, create_time, update_time FROM user WHERE deleted=0 AND id IN ( select id from user where id>3 )
Test 6:
//sort QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.orderByAsc("age");
There are still a lot of content about conditional constructors, such as grouping and so on, as after-school exercises
Code generator
Mapper, POJO, service and controller all write for you!
Use the following code:
Import the dependency of MybatisPlus, and the crazy God video uses version 3.0.5
In addition to the first time, you should also import template engine dependencies
<!-- mybatisPlus rely on--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.0.5</version> </dependency> <!-- MybatisPlus Default template engine dependency--> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-engine-core</artifactId> <version>2.3</version> </dependency>
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(); //D:\java\idea\IntelliJ IDEA 2020.1\IDEACode\mybatisPlus\mybatis_plus String projectPath = System.getProperty("user.dir");//Represents the current path of the project gc.setOutputDir(projectPath+"/src/main/java"); gc.setAuthor("yfm"); gc.setOpen(false);//Open Explorer gc.setFileOverride(false); //Overwrite original code gc.setServiceName("%sService"); //I prefix to Service gc.setIdType(IdType.ID_WORKER); gc.setDateType(DateType.ONLY_DATE); gc.setSwagger2(true); mpg.setGlobalConfig(gc); //2. Data source configuration DataSourceConfig dsc = new DataSourceConfig(); dsc.setUrl("jdbc:mysql://localhost:3306/db1?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8"); dsc.setUsername("root"); dsc.setPassword("onmyown123"); dsc.setDriverName("com.mysql.cj.jdbc.Driver"); dsc.setDbType(DbType.MYSQL); mpg.setDataSource(dsc); //3. Package settings PackageConfig pc= new PackageConfig(); pc.setModuleName("blog");//Grinding block life pc.setParent("com.parent");//The result of the package name is com.parent.blog pc.setEntity("entity");//Entity class package name pc.setMapper("mapper"); pc.setService("service"); pc.setController("controller"); mpg.setPackageInfo(pc); //4. Policy configuration, logical deletion, optimistic lock, etc 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 //Logical deletion strategy.setLogicDeleteFieldName("deleted"); //Optimistic lock strategy.setVersionFieldName("version"); //Auto fill TableFill createTime =new TableFill("createTime", FieldFill.INSERT); TableFill updateTime =new TableFill("updateTime", FieldFill.INSERT_UPDATE); ArrayList<TableFill> tableFills = new ArrayList<>(); tableFills.add(createTime); tableFills.add(updateTime); strategy.setTableFillList(tableFills); strategy.setRestControllerStyle(true); strategy.setControllerMappingHyphenStyle(true); //localhost:8080/hello_id_2 mpg.setStrategy(strategy); mpg.execute();//implement }