Springboot simply uses mybatisplus

content

what is mybatisplus

characteristic

Directory Structure

First import the dependencies:

Create the database:

Connect to the database

Create entity class: (using the lombok plugin)

mapper interface

Then we need to scan all the interfaces under our mapper package on the main startup class

then test in the test class

query operation

 update operation

summary:

Primary key generation strategy

autofill

optimistic locking

Paging query

Tombstone

Conditional constructor


what is mybatisplus

mybatisplus is an  enhancement tool for MyBatis (opens new window) . It only enhances and does not change on the basis of MyBatis. It is born to simplify development and improve efficiency.

characteristic

  • Non-invasive : only make enhancements without changing, introducing it will not affect existing projects, smooth as silk
  • Low loss : Basic CURD is automatically injected at startup, performance is basically lossless, and direct object-oriented operation
  • Powerful CRUD operations : built-in general mapper and general service, most CRUD operations on a single table can be realized with only a small amount of configuration, and more powerful conditional constructors to meet various usage needs
  • Support Lambda form invocation : Through Lambda expressions, you can easily write various query conditions, and you don't need to worry about writing wrong fields.
  • Supports automatic primary key generation : supports up to 4 primary key strategies (including a distributed unique ID generator - Sequence), which can be freely configured to perfectly solve the primary key problem
  • Support ActiveRecord mode : Support ActiveRecord form call, entity class can perform powerful CRUD operations only by inheriting Model class
  • Support custom global general operations : support global general method injection ( Write once, use anywhere )
  • Built-in code generator : Use code or Maven plugin to quickly generate Mapper , Model , Service , Controller layer code, support template engine, and more custom configurations waiting 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 plugin 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, which can quickly identify slow queries
  • Built-in global interception plug-in : provides intelligent analysis and blocking of delete and update operations on the entire table, and can also customize interception rules to prevent misoperation

Directory Structure

First import the dependencies:


        <!-- 数据库驱动 --> <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
       <version>5.1.47</version>
       </dependency>
        <!-- lombok --> <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
              </dependency>

        <!-- mybatis-plus --> <!-- mybatis-plus 是自己开发,并非官方的! -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3</version>
        </dependency>

Create the database:

create database mybatisplus;
use mybatisplus;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` bigint(20) NOT NULL COMMENT '主键ID',
  `name` varchar(30) CHARACTER SET gbk COLLATE gbk_chinese_ci NULL DEFAULT NULL COMMENT '姓名',
  `age` int(11) NULL DEFAULT NULL COMMENT '年龄',
  `email` varchar(50) CHARACTER SET gbk COLLATE gbk_chinese_ci NULL DEFAULT NULL COMMENT '邮箱',
  `version` int(25) NULL DEFAULT 1,
  `deleted` int(10) NULL DEFAULT 1,
  `create_time` datetime NULL DEFAULT NULL,
  `update_time` datetime NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = gbk COLLATE = gbk_chinese_ci ROW_FORMAT = Compact;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, 'test232', 18, '[email protected]', 4, 0, NULL, '2021-07-26 23:06:43');
INSERT INTO `user` VALUES (2, 'Jack', 20, '[email protected]', 1, 0, NULL, NULL);
INSERT INTO `user` VALUES (3, 'Tom', 28, '[email protected]', 1, 0, NULL, NULL);
INSERT INTO `user` VALUES (4, 'Sandy', 21, '[email protected]', 1, 0, NULL, NULL);
INSERT INTO `user` VALUES (5, 'Billie', 24, '[email protected]', 1, 0, NULL, NULL);
INSERT INTO `user` VALUES (55, '小h黑3', 29, '@12910000', 1, 0, NULL, '2021-07-26 17:18:21');
INSERT INTO `user` VALUES (1419574597568741377, '小明2', 10, '@1291759046', 1, 0, NULL, NULL);
INSERT INTO `user` VALUES (1419574597568741378, '小明3', 10, '@1291759046', 1, 0, NULL, NULL);
INSERT INTO `user` VALUES (1419578836433018881, '小明3', 10, '@1291759046', 1, 0, NULL, NULL);
INSERT INTO `user` VALUES (1419582624418045954, '小明5', 10, '@1291759046', 1, 1, NULL, NULL);



-- 真实开发中,version(乐观锁)、deleted(逻辑删除)、gmt_create、gmt_modified

Connect to the database

# mysql 5 驱动不同 com.mysql.jdbc.Driver
# mysql 8 驱动不同com.mysql.cj.jdbc.Driver、需要增加时区的配置 serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=123
spring.datasource.url=jdbc:mysql://localhost:3306/mybatisplus? useSSL=true&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

# 配置日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

Create entity class: (using the lombok plugin)

package com.chen.pojo;

import com.baomidou.mybatisplus.annotation.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    //主键注解
    //对应数据库中的主键(uuid,自增id,雪花算法,redis,zookeeper)
//    AUTO	数据库ID自增
//    NONE	无状态,该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT)
//    INPUT	insert前自行set主键值
//    ASSIGN_ID	分配ID(主键类型为Number(Long和Integer)或String)(since 3.3.0),使用接口IdentifierGenerator的方法nextId(默认实现类为DefaultIdentifierGenerator雪花算法)
//    ASSIGN_UUID	分配UUID,主键类型为String(since 3.3.0),使用接口IdentifierGenerator的方法nextUUID(默认default方法)
    @TableId(type = IdType.ASSIGN_ID)
    private Long id;
    private String name;
    private Integer age;
    private String email;

    //字段注解(非主键)
    // 字段添加填充内容
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    @TableField(fill = FieldFill.UPDATE)
    private Date updateTime;
    @Version
    //乐观锁Version注解
    private Integer version;

    @TableLogic
    //逻辑删除
    private Integer deleted;
}

mapper interface

package com.chen.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.chen.pojo.User;
import org.springframework.stereotype.Repository;

// 在对应的Mapper上面继承基本的类 BaseMapper
@Repository // 代表持久层
public interface UserMapper extends BaseMapper<User> {

// 所有的CRUD操作都已经编写完成了
// 不需要像以前的配置一大堆文件了!

}

Then we need to scan all the interfaces under our mapper package on the main startup class

@MapperScan("com.chen.mapper")

then test in the test class

query operation

  @Autowired
    private UserMapper userMapper;

    @Test
    public void testSelect() {
        System.out.println(("----- selectAll method test ------"));
        // 参数是一个 Wrapper ,条件构造器,这里我们先不用 null
        // 查询全部用户
        List<User> userList = userMapper.selectList(null);
        userList.forEach(System.out::println);

    }

output result

   //测试查询
    @Test
    public void testQuery(){
        User user=userMapper.selectById(1L);
        System.out.println(user);
    }

    // 测试批量查询!
     @Test
    public void testSelectByBatchId(){
     List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
     users.forEach(System.out::println);
    }
     // 按条件查询之一使用map操作
    @Test
    public void testSelectByBatchIds(){
        HashMap<String, Object> map = new HashMap<>();
        // 自定义要查询
        map.put("name","test222");
        map.put("age",18);
        List<User> users = userMapper.selectByMap(map);
        users.forEach(System.out::println);
    }

insert operation

 //测试插入
    @Test
    public  void insertTest(){
        User user=new User();
        user.setAge(10);
        user.setEmail("@1291759046");
        user.setName("小明8");
        //user.setId(55l);
        userMapper.insert(user);
    }

 update operation

 //测试更新
    @Test
    public void testUpdate(){
        User user=new User();
        user.setId(55L);
        user.setAge(29);
        user.setEmail("@12910000");
        user.setName("小h黑555");
        userMapper.updateById(user);

    }

Here is the update based on id

summary:

Through the above few simple steps, we have realized the CRUD function of the User table, and we don't even need to write an XML file!

From the above steps, we can see that the integration MyBatis-Plusis very simple, we only need to introduce the starter project and configure the mapper scan path.

But the power of MyBatis-Plus is much more than these functions. Want to learn more about the powerful functions of MyBatis-Plus? Then keep looking down

Primary key generation strategy

Observe the above insertion test, you can find that the primary key id is not inserted, but a unique id is generated, this is because the allocation id is set

  @TableId(type = IdType.ASSIGN_ID)
    private Long id;

If the id is set to @TableId(type = IdType.AUTO)
    private Long id;

In the database, set the database id field to the auto-increment type

autofill

Creation time, modification time! These operations are all done automatically, we don't want to update manually!
code level
Entity class field attributes need to add annotations
 //字段注解(非主键)
    // 字段添加填充内容
    @TableField(fill = FieldFill.INSERT)
    private Date createTime;
    @TableField(fill = FieldFill.UPDATE)
    private Date updateTime;
Write a processor to handle this annotation!
Create  the MyMetaObjectHandler class
package com.chen.handler;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.Date;

@Slf4j
@Component
// 一定不要忘记把处理器加到IOC容器中!
public class MyMetaObjectHandler implements MetaObjectHandler {
    // 插入时的填充策略
    @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);
//        this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐使用)
//        // 或者
//        this.strictInsertFill(metaObject, "createTime", () -> LocalDateTime.now(), LocalDateTime.class); // 起始版本 3.3.3(推荐)
//        // 或者
//        this.fillStrategy(metaObject, "createTime", LocalDateTime.now()); // 也可以使用(3.3.0 该方法有bug)
    }

    // 更新时的填充策略
    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("start update fill.....");
        this.setFieldValByName("updateTime", new Date(), metaObject);
    }
}

Test adding and changing data, time is automatically generated

optimistic locking

When a record is to be updated, it is hoped that the record has not been updated by others.
Implementation of optimistic locking:

  • When fetching a record, get the current version
  • When updating, bring this version
  • When performing an update, set version = newVersion where version = oldVersion
  • If the version is wrong, the update fails
This type of version since the segment has been created in the database
Entity class plus corresponding fields
   @Version
    //乐观锁Version注解
    private Integer version;

Register components

Create configuration class MyBatisPlusConfig class


import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.injector.ISqlInjector;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize;
import com.chen.pojo.User;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
// 扫描我们的 mapper 文件夹
@MapperScan("com.chen.pojo")
@EnableTransactionManagement
@Configuration
// 配置类
public class MyBatisPlusConfig
{
    // 注册乐观锁插件

    /**
     * 旧版
     */
//    @Bean
//    public OptimisticLockerInterceptor optimisticLockerInterceptor() {
//        return new OptimisticLockerInterceptor();
//    }

    /**
     * 新版
     */
    // 注册乐观锁插件
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return mybatisPlusInterceptor;
    }




}

carry out testing

 //测试乐观锁成功·
    @Test
    public void testOptimisticloker(){
        //1.查询用户信息
        User user=userMapper.selectById(1);
        //2.修改用户信息
        user.setName("哈哈哈哈");
        user.setEmail("[email protected]");
        //执行更新操作
        userMapper.updateById(user);
    }
 // 测试乐观锁失败!多线程下
    @Test
    public void testOptimisticLocker2(){
        // 线程 1
        User user = userMapper.selectById(1L);
        user.setName("test1111111");
        user.setEmail("[email protected]");
        // 模拟另外一个线程执行了插队操作
        User user2 = userMapper.selectById(1L);
        user2.setName("test000000");
        user2.setEmail("[email protected]");
        userMapper.updateById(user2);
        // 自旋锁来多次尝试提交!
        userMapper.updateById(user);
        // 如果没有乐观锁就会覆盖插队线程的值!
        }

Here, it will be found that the user update of user1 fails, the user update of user2 succeeds, and the version value in the database is increased by one, because the user update of user1 fails because of the addition of one, which is optimistic locking

Paging query

1 , the original limit for paging
2. pageHelper third - party plugin
3. MP actually has a built-in paging plug-in !
Configure the interceptor component

Add in MyBatisPlusConfig

package com.chen.config;


import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.injector.ISqlInjector;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize;
import com.chen.pojo.User;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
// 扫描我们的 mapper 文件夹
@MapperScan("com.chen.pojo")
@EnableTransactionManagement
@Configuration
// 配置类
public class MyBatisPlusConfig
{
    


    //分页插件
    // 最新版
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor2() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }

}

test

 // 测试分页查询
    @Test
    public void testPage(){
        // 参数一:当前页
        // 参数二:页面大小
        // 使用了分页插件之后,所有的分页操作也变得简单的!
        Page<User> page = new Page<>(2,5);
        userMapper.selectPage(page,null);
        page.getRecords().forEach(System.out::println);
        System.out.println(page.getTotal());
    }

Tombstone

  • Tombstone is a scheme to facilitate data recovery and protect the value of the data itself, etc., but it is actually deletion.
  • If you need to look up frequently, you should not use tombstone, but represent it as a state.
physical deletion
: Remove directly from the database
Tombstone
: It is not removed from the database, but is invalidated by a variable! deleted = 0 => deleted = 1
Administrators can view deleted records! Prevent data loss, similar to Recycle Bin!
Add a deleted field to the data table , where the database has been added
  @TableLogic
    //逻辑删除
    private Integer deleted;
Place application.yml
# 配置逻辑删除
mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: flag  # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)

test:

// 测试删除
    @Test
    public void testDeleteById(){
        userMapper.deleteById(1420632853468803073L);
    }
    // 通过id批量删除
    @Test
    public void testDeleteBatchId(){
        userMapper.deleteBatchIds(Arrays.asList(1240620674645544961L,1240620674645544962L));
    }
    // 通过map删除
    @Test
    public void testDeleteMap() {
        HashMap<String, Object> map = new HashMap<>();
        map.put("name", "小明6");
        userMapper.deleteByMap(map);
    }

It is found that the logical deletion is successful, the data is not actually deleted, but the deleted state is changed

Conditional constructor

  1. wrapper is heavy
  2. Transmitting wrappers can be analogous to receiving values ​​with map for your controller (developing momentarily, maintaining crematorium)
  3. The correct RPC call posture is to write a DTO for transmission, and the callee performs the corresponding operation according to the DTO
  4. We refuse to accept any issue or even pr related to RPC transport Wrapper error reporting
We can use it instead of writing some complex sql !
test:
Create the WrapperTest class
package com.chen;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.chen.mapper.UserMapper;
import com.chen.pojo.User;
import org.apache.ibatis.annotations.Param;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;
import java.util.Map;

@SpringBootTest
public class WrapperTest {

    @Autowired
    private UserMapper userMapper;
    @Test
   public void contextLoads() {
        // 查询name不为空的用户,并且邮箱不为空的用户,年龄大于等于12
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.isNotNull("name")
                .isNotNull("email")
                .ge("age",12);
        userMapper.selectList(wrapper).forEach(System.out::println);

        // 和我们刚才学习 的map对比一下
        }

    @Test
   public void test2(){
        // 查询名字
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("name","小h黑3");
        User user = userMapper.selectOne(wrapper);
        // 查询一个数据,出现多个结果使用List 或者 Map
        System.out.println(user);
        //
        }

    @Test void test3(){
        // 查询年龄在 20 ~ 30 岁之间的用户
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.between("age",20,30);
        // 区间
        Integer count = userMapper.selectCount(wrapper);
       userMapper.selectList(wrapper).forEach(System.out::println);
       // List<User> list= userMapper.selectList(wrapper);
      //  list.forEach(System.out::println);
        // 查询结果数
        System.out.println(count);
        }


    // 模糊查询
    @Test void test4(){
        // 查询年龄在 20 ~ 30 岁之间的用户
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        // 左和右 t%
        wrapper .notLike("name","j") .likeRight("email","t");
        List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);
        maps.forEach(System.out::println);
    }

    // 模糊查询
    @Test void test5(){
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        // id 在子查询中查出来
        wrapper.inSql("id","select id from user where id<3");
        List<Object> objects = userMapper.selectObjs(wrapper);
        objects.forEach(System.out::println);
    }

    //测试六
    @Test void test6(){
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        // 通过id进行排序
       // wrapper.orderByAsc("id");//升序
        wrapper.orderByDesc("id");//降序
        List<User> users = userMapper.selectList(wrapper);
        users.forEach(System.out::println);
    }

    //测试7
    @Test void test7(){
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.between("age",24,29);
        User user = new User();
        user.setAge(30);
        int n=userMapper.update(user,wrapper);



    }


}

Guess you like

Origin blog.csdn.net/qq_44716544/article/details/119192652