Crazy God says notes - MyBatisPlus quick start 24

Posted by jdc44 on Fri, 04 Mar 2022 17:44:51 +0100

MyBatisPlus quick start

Required basis:

  • MyBatis
  • Spring
  • SpringMVC
  • What is it?

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

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
  1. Create a database mybatis_plus

  1. 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!

  1. Create an empty Spring Boot project (the project will be demonstrated with H2 as the default database)

  1. 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!
  1. 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
  1. 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:

  1. Who wrote sql for us mybatis-plus
  2. 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:

/** 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
  }
}
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:
  1. @ TableId(type = IdType.AUTO) on entity class field

  1. Database fields must be self increasing!

  1. 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)

  1. Add a field to the table: create_time,update_time

  1. 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;
  1. View results

Method 2: code level

  1. Delete the default value of the database and update it!

  1. 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;
  1. 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);
    }
}

  1. 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

  1. Add the version field to the database

  1. Entity class plus corresponding field
    @Version // Optimistic lock Version annotation
    private Integer version;
  1. 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();
    }
}
  1. 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!

  1. Paging the original limit
  2. pageHelper third party plug-in
  3. MP also has a built-in paging plug-in!

Specific use:

  1. 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();
    }

}
  1. 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:

  1. Add a deleted field in the data table

  1. Add attribute in entity class
    @TableLogic // Logical deletion
    private Integer deleted;
  1. 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!
  1. 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
  1. 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

🎉 It's over 🎉

Topics: Java Spring Boot