MyBatisPlus quick start
Required basis:
- MyBatis
- Spring
- SpringMVC
- What is it?
- MyBatis is meant to simplify JDBC operations!
- Official website: https://baomidou.com/ , simplify MyBatis
1. Overview of mybatisplus
MyBatis-Plus (MP) is a MyBatis On the basis of MyBatis, the enhancement tool is only enhanced without change, and is born to simplify development and improve efficiency.
vision
- Is to be MyBatis's best partner, like Contra 1P and 2P in, and the efficiency is doubled by the combination of friends and friends.
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. There is also a powerful condition constructor to meet various use needs
- Support Lambda form call: through Lambda expression, it is convenient to write various query conditions without worrying 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
Frame structure
-
Summary: scan entity types, analyze database tables and fields, and directly inject sql into the container of mybatis without writing sql
(no need to write mapper.xml (Xiuer)), which simplifies the development of mybatis.
Code hosting
Related links
course
- Introduction to mybatis plus - video tutorial - muke.com
- Mybatis plus advanced - video tutorial - muke.com
- Mybatis Plus + springboot to achieve simple rights management - video tutorial - muke.com
principle
2. Quick start
- Tutorial address: https://mp.baomidou.com/guide/quick-start.html
- The following will illustrate the powerful functions of mybatis plus through a simple Demo. Before that, we assume that you have:
- Have Java development environment and corresponding IDE
- Familiar with Spring Boot
- Familiar with Maven
- Create a database mybatis_plus
- Operate the following statements to create a data table and insert data.
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) ); 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'); -- In real development, version(Le Guansuo) deleted(Logical deletion) gmt_create,gmt_modified
If we use mybatis plus to add, delete, modify and check the table from scratch, what do we need to do?
-
Import corresponding dependencies
-
Study how dependencies are configured
-
How to write the code
-
Improve and expand technical ability!
- Create an empty Spring Boot project (the project will be demonstrated with H2 as the default database)
- Import dependencies, including spring boot starter, spring boot starter test, mybatis plus boot starter and h2 dependencies:
<dependencies> <!-- Database driven --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- springboot --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- mybatis-plus Version is important 3.0.5 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.0.5</version> </dependency> <!-- h2 --> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> </dependencies>
- 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!
- Connect to the database! This step is the same as mybatis!
# mysql 5 drives different com mysql. jdbc. Driver # mysql 8 drives different com mysql. cj. jdbc. Driver. The configuration of time zone needs to be added serverTimezone=GMT%2B8 spring.datasource.username=root spring.datasource.password=root spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
- The traditional method is POJO Dao (connect to mybatis and configure the mapper.xml file) - service controller. After using mybatis plus:
- pojo
package com.github.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor public class User { private Long id; private String name; private String email; private Integer age; }
- mapper interface
package com.github.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.github.pojo.User; import org.springframework.stereotype.Repository; @Repository // Persistent layer public interface UserMapper extends BaseMapper<User> { // There is no need to add CRUD operations }
- Add the @ MapperScan annotation in the Spring Boot startup class to scan all interfaces under the Mapper package:
@SpringBootApplication @MapperScan("com.github.mapper") public class Mybatisplus01Application { public static void main(String[] args) { SpringApplication.run(Mybatisplus01Application.class, args); } }
- Test in test class:
@SpringBootTest class Mybatisplus01ApplicationTests { // It inherits BaseMapper, and all methods use their own parent class @Autowired private UserMapper userMapper; @Test void contextLoads() { // The parameter is a wrapper, provided that the constructor uses null first // Query all users List<User> users = userMapper.selectList(null); users.forEach(System.out::println); } }
Tip: 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:
To summarize:
- Through the above simple steps, we have realized the CRUD function of the User table, and we don't even need to write XML files!
- From the above steps, we can see that integrating mybatis plus is very simple. We only need to introduce the starter project and configure the mapper scanning path.
Thinking questions:
- Who wrote sql for us mybatis-plus
- Who wrote the method for us mybatis-plus
3. Configure log
- All sql is invisible now. If I want to know how it is executed, I must look at the log!
# Configuration log mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
- After configuring the log, we need to pay attention to the automatically generated SQL in later learning, and we will like mybatis plus!
4.CRUD
1. Insert operation
// Test insertion @Test public void insertTest(){ User user = new User(); user.setName("Wow ha ha"); user.setAge(22); user.setEmail("2589654784@qq.com"); // Auto generate id Integer insert = userMapper.insert(user); // Number of rows affected System.out.println(insert); // The id found in the log will be backfilled automatically System.out.println(user); }
- Output test:
- The default value of the id inserted in the database is the global unique id.
2. Primary key generation strategy
Default ID_WORKER globally unique id
- Generation of unique id of distributed system: View blog Garden
- There are many kinds of distributed id generation algorithms, and Twitter's SnowFlake is a classic one.
Snowflake algorithm:
- Snowflake is Twitter's open source distributed ID generation algorithm, and the result is a long ID. The result of ID generated by snowflake algorithm is a 64bit integer.
- 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!
Algorithm implementation:
- Algorithm implementation officially given by Twitter It is written in Scala. There is no analysis here. You can check it yourself.
/** Copyright 2010-2012 Twitter, Inc.*/ package com.twitter.service.snowflake import com.twitter.ostrich.stats.Stats import com.twitter.service.snowflake.gen._ import java.util.Random import com.twitter.logging.Logger /** * An object that generates IDs. * This is broken into a separate class in case * we ever want to support multiple worker threads * per process */ class IdWorker(val workerId: Long, val datacenterId: Long, private val reporter: Reporter, var sequence: Long = 0L) extends Snowflake.Iface { private[this] def genCounter(agent: String) = { Stats.incr("ids_generated") Stats.incr("ids_generated_%s".format(agent)) } private[this] val exceptionCounter = Stats.getCounter("exceptions") private[this] val log = Logger.get private[this] val rand = new Random val twepoch = 1288834974657L private[this] val workerIdBits = 5L private[this] val datacenterIdBits = 5L private[this] val maxWorkerId = -1L ^ (-1L << workerIdBits) private[this] val maxDatacenterId = -1L ^ (-1L << datacenterIdBits) private[this] val sequenceBits = 12L private[this] val workerIdShift = sequenceBits private[this] val datacenterIdShift = sequenceBits + workerIdBits private[this] val timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits private[this] val sequenceMask = -1L ^ (-1L << sequenceBits) private[this] var lastTimestamp = -1L // sanity check for workerId if (workerId > maxWorkerId || workerId < 0) { exceptionCounter.incr(1) throw new IllegalArgumentException("worker Id can't be greater than %d or less than 0".format(maxWorkerId)) } if (datacenterId > maxDatacenterId || datacenterId < 0) { exceptionCounter.incr(1) throw new IllegalArgumentException("datacenter Id can't be greater than %d or less than 0".format(maxDatacenterId)) } log.info("worker starting. timestamp left shift %d, datacenter id bits %d, worker id bits %d, sequence bits %d, workerid %d", timestampLeftShift, datacenterIdBits, workerIdBits, sequenceBits, workerId) def get_id(useragent: String): Long = { if (!validUseragent(useragent)) { exceptionCounter.incr(1) throw new InvalidUserAgentError } val id = nextId() genCounter(useragent) reporter.report(new AuditLogEntry(id, useragent, rand.nextLong)) id } def get_worker_id(): Long = workerId def get_datacenter_id(): Long = datacenterId def get_timestamp() = System.currentTimeMillis protected[snowflake] def nextId(): Long = synchronized { var timestamp = timeGen() if (timestamp < lastTimestamp) { exceptionCounter.incr(1) log.error("clock is moving backwards. Rejecting requests until %d.", lastTimestamp); throw new InvalidSystemClock("Clock moved backwards. Refusing to generate id for %d milliseconds".format( lastTimestamp - timestamp)) } if (lastTimestamp == timestamp) { sequence = (sequence + 1) & sequenceMask if (sequence == 0) { timestamp = tilNextMillis(lastTimestamp) } } else { sequence = 0 } lastTimestamp = timestamp ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift) | (workerId << workerIdShift) | sequence } protected def tilNextMillis(lastTimestamp: Long): Long = { var timestamp = timeGen() while (timestamp <= lastTimestamp) { timestamp = timeGen() } timestamp } protected def timeGen(): Long = System.currentTimeMillis() val AgentParser = """([a-zA-Z][a-zA-Z\-0-9]*)""".r def validUseragent(useragent: String): Boolean = useragent match { case AgentParser(_) => true case _ => false } }
- Algorithm implementation in Java: Carried from rhinoceros keeper
public class SnowflakeIdWorker { /** Start time cut-off (use the time when your business system is online) */ private final long twepoch = 1575365018000L; /** Number of machine IDS */ private final long workerIdBits = 10L; /** The maximum machine id supported is 31 (this shift algorithm can quickly calculate the maximum decimal number represented by several binary numbers) */ private final long maxWorkerId = -1L ^ (-1L << workerIdBits); /** The number of bits the sequence occupies in the id */ private final long sequenceBits = 12L; /** The machine ID shifts 12 bits to the left */ private final long workerIdShift = sequenceBits; /** Shift the time cut to the left by 22 bits (10 + 12) */ private final long timestampLeftShift = sequenceBits + workerIdBits; /** The mask of the generated sequence is 4095 (0b111111 = 0xfff = 4095) */ private final long sequenceMask = -1L ^ (-1L << sequenceBits); /** Working machine ID(0~1024) */ private long workerId; /** Sequence in milliseconds (0 ~ 4095) */ private long sequence = 0L; /** Last generated ID */ private long lastTimestamp = -1L; //==============================Constructors===================================== /** * Constructor * @param workerId Work ID (0~1024) */ public SnowflakeIdWorker(long workerId) { if (workerId > maxWorkerId || workerId < 0) { throw new IllegalArgumentException(String.format("workerId can't be greater than %d or less than 0", maxWorkerId)); } this.workerId = workerId; } // ==============================Methods========================================== /** * Get the next ID (this method is thread safe) * @return SnowflakeId */ public synchronized long nextId() { long timestamp = timeGen(); //If the current time is less than the timestamp generated by the last ID, it indicates that the system clock has fallback, and an exception should be thrown at this time if (timestamp < lastTimestamp) { throw new RuntimeException( String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); } //If it is generated at the same time, the sequence within milliseconds is performed if (lastTimestamp == timestamp) { sequence = (sequence + 1) & sequenceMask; //Sequence overflow in milliseconds if (sequence == 0) { //Block to the next millisecond and get a new timestamp timestamp = tilNextMillis(lastTimestamp); } } //The timestamp changes and the sequence resets in milliseconds else { sequence = 0L; } //Last generated ID lastTimestamp = timestamp; //Shift and put together by or operation to form a 64 bit ID return ((timestamp - twepoch) << timestampLeftShift) // | (workerId << workerIdShift) // | sequence; } /** * Block to the next millisecond until a new timestamp is obtained * @param lastTimestamp Last generated ID * @return Current timestamp */ protected long tilNextMillis(long lastTimestamp) { long timestamp = timeGen(); while (timestamp <= lastTimestamp) { timestamp = timeGen(); } return timestamp; } /** * Returns the current time in milliseconds * @return Current time (MS) */ protected long timeGen() { return System.currentTimeMillis(); } }
Primary key auto increment
- You need to configure self increment of primary key:
- @ TableId(type = IdType.AUTO) on entity class field
- Database fields must be self increasing!
- Test insertion again!
Relevant source code explanation:
public enum IdType { AUTO(0), // Self increment of database id NONE(1), // No primary key set INPUT(2), // Manual input ID_WORKER(3), // Default globally unique id UUID(4), // Globally unique id uuid ID_WORKER_STR(5); // ID_WORKER string representation }
3. Update operation
// update operation @Test public void UpdateTest(){ User user = new User(); // Automatic splicing of SQL through conditions user.setId(7L); user.setName("KYDH,Open source navigation"); user.setAge(26); // Note: the parameter of updateById is an object! Integer i = userMapper.updateById(user); System.out.println(i); }
- All sql is automatically configured for us dynamically!
4. Auto fill
Creation time and change time! Generally, we don't want these updates to be completed manually.
Alibaba Development Manual: almost all tables should be configured with gmt_create,gmt_modified ! And it needs automation.
Method 1: database level (database level cannot be modified during work)
- Add a field to the table: create_time,update_time
- To test the insert or update method again, you need to synchronize it in the entity class!
private Date createTime; // Hump naming private Date updateTime;
- View results
Method 2: code level
- Delete the default value of the database and update it!
- Annotation needs to be added on the field attribute of entity class
@TableField(fill = FieldFill.INSERT) private Date createTime; // Hump naming @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime;
- Write a processor to process this annotation!
package com.github.handler; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.reflection.MetaObject; import org.springframework.stereotype.Component; /** * @author subeiLY * @create 2022-03-04 11:05 */ @Slf4j // journal @Component // Don't forget to add the processor to the IOC container! public class MyMetaObjectHandler implements MetaObjectHandler { // Population policy at insertion @Override public void insertFill(MetaObject metaObject) { log.info("start insert fill......"); // setFieldValByName(String fieldName, Object fieldVal, MetaObject metaObject) this.setFieldValByName("createTime",new Date(),metaObject); this.setFieldValByName("updateTime",new Date(),metaObject); } // Population policy when updating @Override public void updateFill(MetaObject metaObject) { log.info("start update fill......"); this.setFieldValByName("updateTime",new Date(),metaObject); } }
- Test insertion / update, observation time
5. Optimistic lock and pessimistic lock
Optimistic lock: as the name suggests, he is very optimistic. He always thinks that there will be no problem. No matter what he does, he doesn't lock it! If there is a problem, update the test value again.
Pessimistic lock: as the name suggests, he is very pessimistic. He always thinks that if there is a problem, he will lock it no matter what he does! Operate again!
Here is the main explanation: optimistic lock mechanism!
Optimistic lock implementation method:
- When the record is fetched, the current version is obtained
- When updating, bring this version
- When updating, set version = newVersion where version = oldVersion
- If the version is incorrect, the update fails
Optimistic lock: 1. Query first to obtain the version number version = 1 -- A update user set name = "Wow ha ha", version = version + 1 where id = 2 and version = 1 -- B The thread finishes first. At this time version = 2,Will lead to A Modification failed! update user set name = "Wow ha ha", version = version + 1 where id = 2 and version = 1
Test the mybatis plus optimistic lock plug-in
- Add the version field to the database
- Entity class plus corresponding field
@Version // Optimistic lock Version annotation private Integer version;
- Register components
package com.github.config; import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.transaction.annotation.EnableTransactionManagement; // Scan mapper folder @MapperScan("com.github.mapper") @EnableTransactionManagement @Configuration // Configuration class public class MyBatisPlusConfig { // Register optimistic lock plug-in @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor() { return new OptimisticLockerInterceptor(); } }
- Test:
// Test optimistic lock @Test public void testOptimisticLocker(){ // 1. Query user information User user = userMapper.selectById(1L); // 2. Modify user information user.setName("goldentop"); user.setEmail("2451367@qq.com"); // 3. Perform update operation userMapper.updateById(user); } // Failed to test optimistic lock! Multithreading @Test public void testOptimisticLocker2(){ // Thread 1 User user = userMapper.selectById(1L); user.setName("Kuafu 1"); user.setEmail("2451367@qq.com"); // Simulate another thread to perform queue jumping User user2 = userMapper.selectById(1L); user2.setName("Kuafu 2"); user2.setEmail("2451367@qq.com"); userMapper.updateById(user2); // Spin lock to multiple attempts to submit! userMapper.updateById(user); // If there is no optimistic lock, the value of queue jumping thread will be overwritten! }
6. Query operation
// Query test @Test public void SelectByIdTest(){ User user = userMapper.selectById(1L); System.out.println(user); } // Batch query @Test public void SelectByBatchIdTest(){ List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3)); users.forEach(System.out::println); } // Using map operations by one of the conditional queries @Test public void SelectByBatchIds(){ HashMap<String, Object> map = new HashMap<>(); // Custom query map.put("name","Wow ha ha"); map.put("age",22); List<User> users = userMapper.selectByMap(map); users.forEach(System.out::println); }
7. Paging query
Pagination is used as much as ten times in the website!
- Paging the original limit
- pageHelper third party plug-in
- MP also has a built-in paging plug-in!
Specific use:
- Just configure the interceptor component!
// Scan mapper folder @MapperScan("com.github.mapper") @EnableTransactionManagement @Configuration // Configuration class public class MyBatisPlusConfig { // Register optimistic lock plug-in @Bean public OptimisticLockerInterceptor optimisticLockerInterceptor() { return new OptimisticLockerInterceptor(); } // Paging plug-in @Bean public PaginationInterceptor paginationInterceptor(){ return new PaginationInterceptor(); } }
- You can use the Page object directly!
// Paging query @Test public void testPage(){ // Parameter 1 current: 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("PageCount ==>"+page.getTotal()); }
8. Delete
Delete record by id
// Basic delete operation @Test public void DeleteTest(){ userMapper.deleteById(14993819200198L); } // Batch delete by id @Test public void DeleteByIdTest(){ userMapper.deleteBatchIds(Arrays.asList(14993819200199L,6)); }
Delete by map
// Delete via Map @Test public void DeleteMapTest(){ HashMap<String, Object> map = new HashMap<>(); map.put("name","KYDH,Open source navigation"); userMapper.deleteByMap(map); }
9. Logical deletion
Physical deletion: directly remove 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! Prevent data loss, similar to recycle bin!
Test:
- Add a deleted field in the data table
- Add attribute in entity class
@TableLogic // Logical deletion private Integer deleted;
- to configure
// 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!
- Update, not delete! The record is still in the database, but the value has changed!
- All the above CRUD operations and their extensions must be proficient! It will greatly improve the efficiency of your work and writing projects!
5. Performance analysis plug-in
- In normal development, you will encounter some slow sql. Test! druid,
- Function: performance analysis interceptor, which is used to output each SQL statement and its execution time
- MP also provides performance analysis plug-in. If it exceeds this time, it will stop running!
- Import plug-in
// SQL execution efficiency plug-in @Bean @Profile({"dev","test"})// Set the dev test environment on to ensure our efficiency public PerformanceInterceptor performanceInterceptor() { PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor(); // ms sets the maximum time for sql execution. If it exceeds, it will not be executed performanceInterceptor.setMaxTime(100); // Format code performanceInterceptor.setFormat(true); return performanceInterceptor; }
- Note: configure the environment in SpringBoot as dev or test environment!
# Set up development environment spring.profiles.active=dev
- Test it!
@Test void contextLoads() { // The parameter is a wrapper, provided that the constructor uses null first // Query all users List<User> users = userMapper.selectList(null); users.forEach(System.out::println); }
- Using the performance analysis plug-in can help us improve efficiency!
6. Conditional constructor Wrapper
Test one
@Test void contextLoads2() { // Query users whose name is not empty and whose mailbox is 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); // Compare with map }
Test two
@Test void test2(){ // Query name Jack QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.eq("name","Jack"); User user = userMapper.selectOne(wrapper); // Query one data and multiple results appear. Use List or Map System.out.println(user); }
Test three
@Test void test3(){ // Query users aged between 20 and 30 QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.between("age",20,30); // section Integer count = userMapper.selectCount(wrapper); // Number of query results System.out.println(count); }
Test four
// Fuzzy query @Test void test4(){ // Query users aged between 20 and 30 QueryWrapper<User> wrapper = new QueryWrapper<>(); // Left and right t% wrapper .notLike("name","e") .likeRight("email","t"); List<Map<String, Object>> maps = userMapper.selectMaps(wrapper); maps.forEach(System.out::println); }
Test V ()
@Test public void testWrapper5() { //Fuzzy query // 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<5) QueryWrapper<User> wrapper = new QueryWrapper<>(); // Find out the sub id in the query wrapper.inSql("id","select id from user where id<5"); List<Object> objects = userMapper.selectObjs(wrapper); objects.forEach(System.out::println); }
Test six
@Test public void testWrapper6() { QueryWrapper<User> wrapper = new QueryWrapper<>(); // Sort by id in descending order wrapper.orderByDesc("id"); List<User> userList = userMapper.selectList(wrapper); userList.forEach(System.out::println); }
7. Automatic code generator
- 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.
Legacy test:
package com.github; import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.annotation.FieldFill; import com.baomidou.mybatisplus.annotation.IdType; 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 * @author subeiLY */ public class WskCode { public static void main(String[] args) { // Build a code generator object AutoGenerator mpg = new AutoGenerator(); // How to execute and configure policies // 1. Global configuration GlobalConfig gc = new GlobalConfig(); // Get current directory String projectPath = System.getProperty("user.dir"); // To which directory gc.setOutputDir(projectPath+"/src/main/java"); gc.setAuthor("github"); gc.setOpen(false); // Overwrite gc.setFileOverride(false); // I prefix to Service gc.setServiceName("%sService"); 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.setUsername("root"); dsc.setPassword("root"); dsc.setUrl("jdbc:mysql://localhost:3306/github?useSSL=false&serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8"); dsc.setDriverName("com.mysql.cj.jdbc.Driver"); dsc.setDbType(DbType.MYSQL); mpg.setDataSource(dsc); // 3. Package configuration PackageConfig pc = new PackageConfig(); pc.setModuleName("study"); pc.setParent("com.github"); pc.setEntity("pojo"); pc.setMapper("mapper"); pc.setService("service"); pc.setController("controller"); mpg.setPackageInfo(pc); // 4. Policy configuration StrategyConfig strategy = new StrategyConfig(); // Set the table name to be mapped. Just change it here strategy.setInclude("admin","Banyan","building","room"); strategy.setNaming(NamingStrategy.underline_to_camel); strategy.setColumnNaming(NamingStrategy.underline_to_camel); // Whether to use lombok to open annotation strategy.setEntityLombokModel(true); strategy.setLogicDeleteFieldName("deleted"); // Auto fill configuration TableFill gmtCreate = new TableFill("gmt_create", FieldFill.INSERT); TableFill gmtUpdate = new TableFill("gmt_update", FieldFill.INSERT_UPDATE); ArrayList<TableFill> tableFills = new ArrayList<>(); tableFills.add(gmtCreate); tableFills.add(gmtUpdate); strategy.setTableFillList(tableFills); // Optimistic lock configuration strategy.setVersionFieldName("version"); // Open hump naming strategy.setRestControllerStyle(true); // localhost:8080/hello_id_2 strategy.setControllerMappingHyphenStyle(true); mpg.setStrategy(strategy); mpg.execute(); // implement } }
New version test
- Import dependency
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.5.1</version> </dependency> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> <version>2.3.31</version> </dependency>
- java
package com.github; import com.baomidou.mybatisplus.generator.FastAutoGenerator; import com.baomidou.mybatisplus.generator.config.OutputFile; import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine; import java.util.Collections; // Code generator (new version) public class QuaryCode { public static void main(String[] args) { FastAutoGenerator.create("url", "username", "password") .globalConfig(builder -> { builder.author("subei") // Set author .enableSwagger() // Turn on swagger mode .fileOverride() // Overwrite generated files .outputDir("D://"); / / specify the output directory }) .packageConfig(builder -> { builder.parent("com.github.mybatisplus.samples.generator") // Set parent package name .moduleName("system") // Set parent package module name .pathInfo(Collections.singletonMap(OutputFile.mapperXml, "D://"); / / set mapperXml generation path }) .strategyConfig(builder -> { builder.addInclude("t_simple") // Set the table name to be generated .addTablePrefix("t_", "c_"); // Set filter table prefix }) .templateEngine(new FreemarkerTemplateEngine()) // Freemarker engine template is used. The default is Velocity engine template .execute(); } }
8. Multiple data sources
- It is applicable to a variety of scenarios: pure multi library, read-write separation, one master multi slave, mixed mode, etc.
- Let's simulate more than one other scenario library.
- Scenario Description:
- Create two libraries: mybatis_plus (the previous library remains unchanged) and mybatis_plus_1 (New);
- Mybatis_ Move the product table of the plus library to mybatis_plus_1 library, so that each library has a table;
- Obtain user data and commodity data respectively through a test case. If it is obtained, it indicates that the multi database simulation is successful.
Create database mybatis_ plus_ Table 1 and table product
CREATE DATABASE `mybatis_plus` /*!40100 DEFAULT CHARACTER SET utf8mb4 */; use `mybatis_plus`; CREATE TABLE `user` ( `id` bigint(20) NOT NULL COMMENT 'Primary key ID', `name` varchar(30) DEFAULT NULL COMMENT 'full name', `age` int(11) DEFAULT NULL COMMENT 'Age', `email` varchar(50) DEFAULT NULL COMMENT 'mailbox', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 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');
CREATE DATABASE `mybatis_plus_1` /*!40100 DEFAULT CHARACTER SET utf8mb4 */; use `mybatis_plus_1`; CREATE TABLE product ( id BIGINT(20) NOT NULL COMMENT 'Primary key ID', name VARCHAR(30) NULL DEFAULT NULL COMMENT 'Trade name', price INT(11) DEFAULT 0 COMMENT 'Price', version INT(11) DEFAULT 0 COMMENT 'Optimistic lock version number', PRIMARY KEY (id) ); # Add test data INSERT INTO product (id, NAME, price) VALUES (1, 'Alien notebook', 100); # Delete mybatis_plus library product table use mybatis_plus; DROP TABLE IF EXISTS product;
Create a new spring boot project and introduce dependencies:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.1</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-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>
Configure multiple data sources
spring: # Configure data source information datasource: dynamic: # Set the default data source or data source group. The default value is master primary: master # Strictly match the data source. The default is false True throws an exception when it does not match the specified data source, and false uses the default data source strict: false datasource: master: url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=false driver-class-name: com.mysql.cj.jdbc.Driver username: root password: root slave_1: url: jdbc:mysql://localhost:3306/mybatis_plus_1?characterEncoding=utf-8&useSSL=false driver-class-name: com.mysql.cj.jdbc.Driver username: root password: root
Create entity class
@Data // lombok annotation @TableName("user") public class User { @TableField private Long id; private String name; private Integer age; private String email; }
@Data public class Product { private Integer id; private String name; private Integer price; private Integer version; }
Create mapper
@Repository public interface UserMapper extends BaseMapper<User> { }
@Repository public interface ProductMapper extends BaseMapper<Product> { }
Startup class
@SpringBootApplication @MapperScan("com.github.mapper") public class MybatisPlusDatasourceApplication { public static void main(String[] args) { SpringApplication.run(MybatisPlusDatasourceApplication.class, args); } }
Create user service
public interface UserService extends IService<User> { }
@DS("master") //Specify the data source to operate on @Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { }
Create product service
public interface ProductService extends IService<Product> { }
@DS("slave_1") @Service public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements ProductService { }
test
@SpringBootTest class MybatisPlusDatasourceApplicationTests { @Autowired private UserService userService; @Autowired private ProductService productService; @Test public void testDynamicDataSource(){ System.out.println(userService.getById(1L)); System.out.println(productService.getById(1L)); } }
9.MyBatisX plug-in
Mybatis plus provides us with powerful mapper and service templates, which can greatly improve the development efficiency. However, in the real development process, mybatis plus can not solve all problems for us. For example, for some complex SQL and multi table joint query, we need to write our own code and SQL statements. How can we quickly solve this problem? At this time, we can use MyBatisX plug-in, MyBatisX, a rapid development plug-in based on IDEA, for efficiency.
- MyBatisX plug-in usage: https://baomidou.com/pages/ba5b24
Installation method: open IDEA, enter file - > Settings - > plugins - > Browse repositories, enter mybatisx to search and install.
Function:
-
XML jump
-
Generate code (you need to configure the Database data source in idea first)
-
Reset template
JPA tips
-
Generate new
-
Generate query
-
Generate modification
-
Generate delete