Mybatis plus quick start

1. Introduction

MyBatis plus (MP for short) is an enhancement tool for MyBatis. On the basis of MyBatis, it only makes enhancements and does not change. It is born to simplify development and improve efficiency.

1.1. Characteristics

  • 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

1.2. Support database

Any database that can use mybatis for CRUD and supports standard SQL. The specific support is as follows. If you don't check the pagination tutorial PR in the following list, your support.

  • mysql,oracle,db2,h2,hsql,sqlite,postgresql,sqlserver,Phoenix,Gauss ,clickhouse,Sybase,OceanBase,Firebird,cubrid,goldilocks,csiidb
  • Dameng database, virtual Valley database, NPC Jincang database, NTU general (Huaku) database, NTU general database, Shentong database, Hangao database

1.3. Frame structure

2. Get started quickly

2.1.pom.xml import MyBatis Plus dependency

<dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>Latest Version</version>
</dependency>

2.2. Configure data source in application.yml

# Configure data sources
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/wyl? useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8 
    username: root
    password: 123456

# Configure log and output more detailed log information
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

2.3. Create entity class according to database table

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

2.4. Create Mapper interface

//Inherit the basic interface BaseMapper on the corresponding mapper
@ResponseBody //Represents the persistence layer
public interface UserMapper extends BaseMapper<User> {
    //All CRUD operations have been written
    //You don't need a lot of configuration files like before
}

2.5. Test

The startup class needs to add @ mapperscan ("the package where the mapper is located"), otherwise the mapper bean cannot be loaded

@SpringBootTest
class MybatisPlusApplicationTests {
    //It inherits BaseMapper, and all methods come from the parent class
    //We can also write our own extension methods
    @Autowired
    private UserMapper userMapper;

    @Test
    void contextLoads() {
        //The parameter is a Wrapper and a condition constructor. We don't need null here
        //Query all users
        List<User> users = userMapper.selectList(null);
        users.forEach(System.out::println);
    }
}

Who wrote my sql and where did my methods go? In fact, they are all mybatis plus.

3. Configure log

All our sql is invisible now. We want to know how it is executed, so we must look at the log!

# Configuration log
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

4.CRUD

4.1.Service CRUD interface

4.1.1.Save

// Insert a record (select field, policy insert)
boolean save(T entity);
// Insert (batch)
boolean saveBatch(Collection<T> entityList);
// Insert (batch)
boolean saveBatch(Collection<T> entityList, int batchSize);

4.1.2.SaveOrUpdate

// Update record exists in TableId annotation. Insert a record
boolean saveOrUpdate(T entity);
// If the updateWrapper attempts to update, do you want to continue with the saveOrUpdate(T) method
boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper);
// Batch modify insert
boolean saveOrUpdateBatch(Collection<T> entityList);
// Batch modify insert
boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);

4.1.3.Remove

// Delete the record according to the entity condition
boolean remove(Wrapper<T> queryWrapper);
// Delete by ID
boolean removeById(Serializable id);
// Delete the record according to the columnMap condition
boolean removeByMap(Map<String, Object> columnMap);
// Delete (batch delete according to ID)
boolean removeByIds(Collection<? extends Serializable> idList);

4.1.4.Update

// According to the UpdateWrapper condition, sqlset needs to be set for updating records
boolean update(Wrapper<T> updateWrapper);
// Update the record according to the whereWrapper condition
boolean update(T updateEntity, Wrapper<T> whereWrapper);
// Select modify according to ID
boolean updateById(T entity);
// Batch update by ID
boolean updateBatchById(Collection<T> entityList);
// Batch update by ID
boolean updateBatchById(Collection<T> entityList, int batchSize);

4.1.5.Get

// Query by ID
T getById(Serializable id);
// Query a record according to the Wrapper. If there are multiple result sets, exceptions will be thrown. Take one at random and add the limiting condition wrapper.last("LIMIT 1")
T getOne(Wrapper<T> queryWrapper);
// Query a record according to the Wrapper
T getOne(Wrapper<T> queryWrapper, boolean throwEx);
// Query a record according to the Wrapper
Map<String, Object> getMap(Wrapper<T> queryWrapper);
// Query a record according to the Wrapper
<V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);

4.1.6.List

// Query all
List<T> list();
// Query list
List<T> list(Wrapper<T> queryWrapper);
// Query (batch query by ID)
Collection<T> listByIds(Collection<? extends Serializable> idList);
// Query (based on columnMap criteria)
Collection<T> listByMap(Map<String, Object> columnMap);
// Query all lists
List<Map<String, Object>> listMaps();
// Query list
List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper);
// Query all records
List<Object> listObjs();
// Query all records
<V> List<V> listObjs(Function<? super Object, V> mapper);
// Query all records according to Wrapper conditions
List<Object> listObjs(Wrapper<T> queryWrapper);
// Query all records according to Wrapper conditions
<V> List<V> listObjs(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);

4.1.7.Page

// Unconditional paging query
IPage<T> page(IPage<T> page);
// Conditional paging query
IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper);
// Unconditional paging query
IPage<Map<String, Object>> pageMaps(IPage<T> page);
// Conditional paging query
IPage<Map<String, Object>> pageMaps(IPage<T> page, Wrapper<T> queryWrapper);

4.1.8.Count

// Total records queried
int count();
// Query the total number of records according to Wrapper conditions
int count(Wrapper<T> queryWrapper);

4.1.9.Chain

query

// Chain query general
QueryChainWrapper<T> query();
// Chained query lambda. Note: Kotlin is not supported
LambdaQueryChainWrapper<T> lambdaQuery(); 

// Example:
query().eq("column", value).one();
lambdaQuery().eq(Entity::getId, value).list();

update

// Chain change normal
UpdateChainWrapper<T> update();
// Chain change lambda. Note: Kotlin is not supported 
LambdaUpdateChainWrapper<T> lambdaUpdate();

// Example:
update().eq("column", value).remove();
lambdaUpdate().eq(Entity::getId, value).update(entity);

4.2 mapper crud interface

4.2.1.Insert

// Insert a record
int insert(T entity);

4.2.2.Delete

// Delete the record according to the entity condition
int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);
// Delete (batch delete according to ID)
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
// Delete by ID
int deleteById(Serializable id);
// Delete the record according to the columnMap condition
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);

4.2.3.Update

// Update the record according to the whereWrapper condition
int update(@Param(Constants.ENTITY) T updateEntity, @Param(Constants.WRAPPER) Wrapper<T> whereWrapper);
// Modify according to ID
int updateById(@Param(Constants.ENTITY) T entity);

4.2.4.Select

// Query by ID
T selectById(Serializable id);
// Query a record according to the entity condition
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

// Query (batch query by ID)
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
// Query all records according to the entity condition
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// Query (based on columnMap criteria)
List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
// Query all records according to Wrapper conditions
List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// Query all records according to Wrapper conditions. Note: only the value of the first field is returned
List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

// Query all records (and turn the page) according to the entity condition
IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// Query all records (and turn pages) according to Wrapper conditions
IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// Query the total number of records according to Wrapper conditions
Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

case

List<User> userList1 = user.selectList(
        new EntityWrapper<User>().eq("name", "Wang Yanling")
);

paging

// Query 10 user records named 'wyl' in pages
List<User> userList = user.selectPage(
        new Page<User>(1, 10),
        new EntityWrapper<User>().eq("name", "wyl")
).getRecords();

combination

// Query 10 user records whose name is' wyl ', gender is male, and age is between 18 and 50
List<User> userList = userMapper.selectPage(
        new Page<User>(1, 10),
        new EntityWrapper<User>().eq("name", "wyl")
                .eq("sex", 0)
                .between("age", "18", "50")
);

4.3.mapper layer options

AlwaysUpdateSomeColumnById

int alwaysUpdateSomeColumnById(T entity);

insertBatchSomeColumn

int insertBatchSomeColumn(List<T> entityList);

logicDeleteByIdWithFill

int logicDeleteByIdWithFill(T entity);

4.4. Conditional constructor

Very important: wapper

We can write some complex SQL and use it instead!

1. Test one, remember to view the output SQL for analysis

@Test
void contextLoads() {
    //Query users whose name is not empty and whose mailbox is not empty, and whose age is greater than 12
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.isNotNull("name")
            .isNotNull("email")
            .ge("age", 12);
    userMapper.selectList(wrapper).forEach(System.out::println); //Compare it with the map we just learned
}

2. In test 2, remember to view the output SQL for analysis

@Test
void test2(){
    //Query name Chanv
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.eq("name", "Chanv");
    User user = userMapper.selectOne(wrapper);
    System.out.println(user);
}

3. Test three

@Test
void test3(){
    //Query users aged 19 to 30
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.between("age", 19, 30); //section
    Integer count = userMapper.selectCount(wrapper);
    System.out.println(count);
}

4. Test 4, remember to view the output SQL for analysis

//Fuzzy query
@Test
void test4(){
    //Query users aged 19 to 30
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    //left and right
    wrapper.notLike("name", "b")
            .likeRight("email", "t");
    List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);
    maps.forEach(System.out::println);
}

5. Test five

@Test
void test5(){
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    //id is found in the sub query
    wrapper.inSql("id", "select id from user where id < 3");
    List<Object> objects = userMapper.selectObjs(wrapper);
    objects.forEach(System.out::println);
}

6. Test six

@Test
void test6(){
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    //Sort by id
    wrapper.orderByDesc("id");
    List<User> users = userMapper.selectList(wrapper);
    users.forEach(System.out::println);
}

5. Code generator

dao, pojo, service and controller are all written by myself!

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.

5.1. Import dependency

<!-- Code generator dependency -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.3.1.tmp</version>
</dependency>
<!-- The generator needs to generate various components according to the template, so the template also needs to be imported -->
<!-- velocity It is the default template. In addition to it, common templates include: Freemarker,Beetl -->
<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity</artifactId>
    <version>1.7</version>
</dependency>

5.2. Start class. Any main or @ Test method is OK

package com.wyl.mybatisplus;
import com.baomidou.mybatisplus.annotation.DbType;
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.rules.NamingStrategy;
public class Code {
    public static void main(String[] args) {
        //You need to build an automatic code generator object
        // Code generator 
        AutoGenerator mpg = new AutoGenerator();
        //Configuration policy

        //1. Global configuration
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir(projectPath + "/src/main/java");
        gc.setAuthor("ChanV");
        gc.setOpen(false);
        gc.setFileOverride(false);  //Overwrite
        gc.setServiceName("%sService"); //I prefix to Service
        gc.setIdType(IdType.ID_WORKER);
        gc.setDateType(DateType.ONLY_DATE);
        gc.setSwagger2(true);
        mpg.setGlobalConfig(gc);

        //2. Set data source
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/mybatis-plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("root");
        dsc.setDbType(DbType.MYSQL);
        mpg.setDataSource(dsc);

        //3. Package configuration
        PackageConfig pc = new PackageConfig();
        pc.setModuleName("blog");
        pc.setParent("com.chanv");
        pc.setEntity("pojo");
        pc.setMapper("mapper");
        pc.setService("service");
        pc.setController("controller");
        mpg.setPackageInfo(pc);

        //4. Policy configuration
        StrategyConfig strategy = new StrategyConfig();
        strategy.setInclude("user");    //Set the table name to map
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setEntityLombokModel(true);    //Automatic lombok
        strategy.setLogicDeleteFieldName("deleted");
        //Auto fill configuration
        TableFill createTime = new TableFill("create_time", FieldFill.INSERT);
        TableFill updateTime = new TableFill("update_time", FieldFill.UPDATE);
        ArrayList<TableFill> tableFills = new ArrayList<>();
        tableFills.add(createTime);
        tableFills.add(updateTime);
        strategy.setTableFillList(tableFills);
        //Optimistic lock
        strategy.setVersionFieldName("version");
        strategy.setRestControllerStyle(true);
        strategy.setControllerMappingHyphenStyle(true);     //localhost:8080/hello_id_2
        mpg.setStrategy(strategy);

        mpg.execute();  //Execute code constructor
    }
}

The above two steps can complete the function of generating code!

Start scan on class

@SpringBootApplication  // Startup class
@MapperScan(value = {"com.wyl.mybatisplus.generator.mapper"})  // Scan mapper
public class MybatisplusApplication {
    public static void main(String[] args) {
        SpringApplication.run(MybatisplusApplication.class, args);
    }
}

Scan on test class

@SpringBootTest
@MapperScan(value = {"com.wyl.mybatisplus.generator.mapper"})  // @Mapperscan ("package location of mapper")
class UserServiceTest {

    @Autowired
    private UserMapper mapper;

    @Test
    public void test(){
        mapper.selectList(null).forEach(System.out::println);
    }
}

5.3. Test MVC

5.3.1. Back end: controller.java

package com.wyl.mybatisplus.generator.controller;
import com.wyl.mybatisplus.generator.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping("/generator/user")
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/success")
    public ModelAndView index(){
        ModelAndView mav = new ModelAndView();
        mav.setViewName("success");
        mav.addObject("list",userService.list());
        return mav;
    }
}

5.3.2. Front end: html

Remember to put it under templates

index.html

<h1>Index Page...</h1>
<a href="/generator/user/success">Display data</a>

succuss.html

<!DOCTYPE html>
<html lang="en">
<!-- to configure thymeleaf Template label Library -->
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Success Page</title>
</head>
<body>

<h1>Success Page...</h1>

<table border="1" cellspacing="0" cellpadding="1">
    <tr>
        <th>number</th>
        <th>user name</th>
        <th>Age</th>
    </tr>
    <tr th:each="user:${list}">
        <td th:text="${user.id}"></td>
        <td th:text="${user.userName}"></td>
        <td th:text="${user.userAge}"></td>
    </tr>
</table>

</body>
</html>

The thymeleaf template engine is used in the front end, and application.yml needs to be configured

spring:
# View resolution
  thymeleaf:
    prefix: classpath:/templates/
    suffix: .html

6.MybatisX rapid development plug-in

MybatisX is a rapid development plug-in based on IDEA, which is born for efficiency.

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

8. Optimistic lock

During the interview, we are often asked about optimistic lock and pessimistic lock! This is actually very simple!

Atomic reference!

Optimistic lock: as the name suggests, he is very optimistic. He always thinks there will be no problem. No matter what he does, he doesn't lock it! If there is a problem, update the value test again!

Pessimistic lock: as the name suggests, he is very pessimistic. He always has problems with his tasks. No matter what he does, he will lock it! Do it again!

Here we mainly explain the optimistic lock mechanism!

Optimistic lock implementation method:

  • Take out the record and get the current version
  • When updating, bring this version
  • When updating, set version = new version 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 = "wyl", version = version + 1
where id = 2 and version = 1

-- B The thread finishes first. At this time version = 2,Will cause A Modification failed!
update user set name = "wjm", version = version + 1
where id = 2 and version = 1

Test MP's optimistic lock plug-in

1. Add version field to the database!

//Successful test!
@Test
public void testOptimisticLocker(){
    //1. Query user information
    User user = userMapper.selectById(1330080433207046145L);
    //2. Modify user information
    user.setName("ChanV");
    user.setEmail("1277077741@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(5L);
    user.setName("ChanV111");
    user.setEmail("1277077741@qq.com");
    //Simulate another thread to perform queue jumping
    User user2 = userMapper.selectById(5L);
    user2.setName("ChanV222");
    user2.setEmail("1277077741@qq.com");
    userMapper.updateById(user2);

    //Spin lock multiple attempts to commit
    userMapper.updateById(user);    //If there is no optimistic lock, the value of the queue thread will be overwritten
}

9. Global policy configuration:

Through the above small case, we can find that the entity class needs to add the @ TableName annotation to specify the database table name and the growth strategy of id through the @ TableId annotation. It doesn't matter if there are fewer entity classes. It's troublesome if there are more entity classes. Therefore, the global policy can be configured in the spring-dao.xml file.

<!-- 5,mybatisplus Global policy configuration for -->
<bean id="globalConfiguration" class="com.baomidou.mybatisplus.entity.GlobalConfiguration">
        <!-- 2.3 After version, the default value of hump naming is true,Therefore, it can not be configured -->
        <!--<property name="dbColumnUnderline" value="true"/>-->
        <!-- Global primary key auto increment policy, 0 indicates auto -->
        <property name="idType" value="0"/>
        <!-- Global table prefix configuration -->
        <property name="tablePrefix" value="tb_"/>
</bean>

The configuration here is useless. The configuration needs to be injected into sqlSessionFactory before it takes effect. As follows:

<!-- 3,to configure mybatisplus of sqlSessionFactory -->
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="typeAliasesPackage" value="com.zhu.mybatisplus.entity"/>
        <!-- Inject global configuration -->
        <property name="globalConfig" ref="globalConfiguration"/>
</bean>

In this way, the @ TableName annotation and @ TableId annotation in the entity class can be removed.

Posted on Thu, 11 Nov 2021 02:49:33 -0500 by hesyar