MybatisPlus

953 阅读7分钟

MyBatisPlius

0、创建表

DROP TABLE IF EXISTS user;

CREATE TABLE user
(
    id BIGINT(20) NOT NULL COMMENT '主键ID',
    name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
    age INT(11) NULL DEFAULT NULL COMMENT '年龄',
    email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
    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');

1、导入依赖

    <!--数据库驱动-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.23</version> <!-- 使用适当的版本号 -->
</dependency>
<!--lombok-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>
<!--mybatis-plus-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.5.1</version>
</dependency>
  • 我们使用mybaits-plus可以节省我们大量的代码,尽量不要同时导入mybatismybaits-plus

2、连接数据库

spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=PRC&characterEncoding=UTF8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

3、做一个小测试

  • pojo:com.yap.mybatis_plus.pojo
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Long id;
    private String name;
    private Integer age;
    private String email;
}
  • mapper:com.yap.mybatis_plus.mapper
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yap.mybatis_plus.pojo.User;
import org.springframework.stereotype.Repository;

/**
 * @author yap
 * @version 1.0
 */
//在对应的mapper 上面继承基本的类 BaseMapper
@Repository//代表持久层
public interface UserMapper extends BaseMapper<User> {
}
  • 启动类
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
//扫描mapper 文件夹
@MapperScan("com.yap.mybatis_plus.mapper")
@SpringBootApplication
public class MybatisPlusApplication {

    public static void main(String[] args) {
        SpringApplication.run(MybatisPlusApplication.class, args);
    }

}

4、测试类中

  • 引入
//继承了BaseMapper,所以的方法都来自父类
//我们也可以编写自己的扩展方法
@Autowired
private UserMapper userMapper;

@Test
void selectList() {
    //参数是一个Wrapper,条件构造器,
    //查询全部用户
    List<User> users =  userMapper.selectList(null);
    users.forEach(System.out::println);
}
  • 输出结果
    User(id=1, name=Jone, age=18, email=<test1@baomidou.com>)
    User(id=2, name=Jack, age=20, email=<test2@baomidou.com>)
    User(id=3, name=Tom, age=28, email=<test3@baomidou.com>)
    User(id=4, name=Sandy, age=21, email=<test4@baomidou.com>)
    User(id=5, name=Billie, age=24, email=<test5@baomidou.com>)

5、配置日志

  • 现在是看不到sql的,配置控制台输出日志
    mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
  • 输出结果
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3289079a] was not registered for synchronization because synchronization is not active
JDBC Connection [HikariProxyConnection@324701295 wrapping com.mysql.cj.jdbc.ConnectionImpl@6419a0e1] will not be managed by Spring
==>  Preparing: SELECT id,name,age,email FROM user 
==> Parameters: 
<==    Columns: id, name, age, email
<==        Row: 1, Jone, 18, test1@baomidou.com
<==        Row: 2, Jack, 20, test2@baomidou.com
<==        Row: 3, Tom, 28, test3@baomidou.com
<==        Row: 4, Sandy, 21, test4@baomidou.com
<==        Row: 5, Billie, 24, test5@baomidou.com
<==      Total: 5
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3289079a]

6、CRUD拓展

6.1 测试插入(insert)

  • insert:使用的是雪花算法生成唯一Id
  • 雪花算法:snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是: 使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID ),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个ID),最后还有一个符号位,永远是0。可以保证全球唯一!
  • 主键自增
    • 1、 :实体类字段上@TableId(type = IdType.ID_WORKER)//唯一
      • AUTO:数据库id自增
      • NONE:未设置主键
      • INPUT:手动输入
      • ID_WORKER:默认的全局唯一ID
      • UUID:全局唯一id uuid
      • ID_WORKER_STR:ID_WORKER的字符串表示法
    • 2、 :数据库字段一定要是自增否则会报错!
@Test
void insert() {
    User user = new User();
    user.setName("abc");
    user.setAge(3);
    user.setEmail("456");
    int result = userMapper.insert(user);//自动生成id
    System.out.println(result);
    System.out.println(user);
}

image.png

6.2 测试更新(update)

  • updateById:参数是一个对象
@Test
void updateById() {
    User user = new User();
    //通过条件自动拼接sql
    user.setId(1667764916303597570L);
    user.setName("123");
    user.setAge(1);
    user.setEmail("456");
    //注意updateById的参数是个对象
    int result = userMapper.updateById(user);
    System.out.println(result);
    System.out.println(user);
}

image.png

6.3 自动填充

  • 创建时间、修改时间!这些个操作一遍都是自动化完成的,我们不希望手动更新!
  • 阿里巴巴开发手册:所有数据库表:创建时间(gmt_create)、修改时间:(gmt_modified)几乎所有表都要配置上!而且需要自动化!

方式:代码级别

  • 1:在表中新建字段 create_time,update_time

image.png

  • 2:同步实体类User
//字段添加填充内容
@TableField(fill = FieldFill.INSERT)//代表添加的时候执行
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)//代表添加修改的时候执行
private Date updateTime;
  • 3:创建com.yap.mybatis_plus.handler.MyMetaObjectHandler
  • 自定义实现类
package com.yap.mybatis_plus.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.util.Date;

@Slf4j
@Component//不要忘记把处理器加到ioc容器中
public class MyMetaObjectHandler implements MetaObjectHandler {

    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("start insert fill ....");
        //插入的时候添加和修改都会存入时间
        this.strictInsertFill(metaObject, "createTime", Date.class,  new Date()); 
        this.strictInsertFill(metaObject, "updateTime", Date.class,  new Date()); 

    }

    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("start update fill ....");
        //更新的时候会修改时间
        this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date()); 
     }
}

6.4 乐观锁

乐观锁:顾名思义十分乐观,它总会认为不会出现问题,无论干什么都不上锁!如果出现问题,再次更新值测试

悲观锁:顾名思义十分悲观,它总是认为总是出现问题,无论干什么都会上锁!再去操作!

  • 乐观锁实现方式:
    • 取出记录时,获取当前 version
    • 更新时,带上这个 version
    • 执行更新时, set version = newVersion where version = oldVersion
    • 如果 version 不对,就更新失败
乐观锁:1、先查询 获得版本号 version = 1
-- A
update user set name = "abc",version = version + 1
where id  = 2 and version = 1

-- B 线程抢先完成,这个时候version = 2,会导致A修改失败!
update user set name = "abc",version = version + 1
where id  = 2 and version = 1

测试一下MP乐观锁插件

  • 1、:给数据库增加version字段,默认值为1int类型,长度10
  • 2、:实体类增加字段
@Version//乐观锁version注解
private Integer version;
  • 说明:

    • 支持的数据类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
    • 整数类型下 newVersion = oldVersion + 1
    • newVersion 会回写到 entity 中
    • 仅支持 updateById(id) 与 update(entity, wrapper) 方法
    • 在 update(entity, wrapper) 方法下, wrapper 不能复用!!!
  • 3、:配置类com.yap.mybatis_plus.Config

package com.yap.mybatis_plus.Config;

import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

// Spring Boot 方式
@Configuration
public class MybatisPlusConfig {
 
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return mybatisPlusInterceptor;
    }
}
  • 4、:测试一下!

//测试乐观锁成功
@Test
public void testVersion() {
    //1、查询用户信息
    User user = userMapper.selectById(1L);
    //2、修改用户信息
    user.setName("abc");
    user.setEmail("2314526");
    //3、执行更新操作
    int i = userMapper.updateById(user);
    System.out.println(i);
}

//测试乐观锁失败
//开启version最后数据库显示的结果是abc222
//未开启version最后数据库显示的结果是abc111
@Test
public void testVersion2() {
    //线程1
    User user = userMapper.selectById(1L);
    user.setName("abc111");
    user.setEmail("2314526");

    //模拟另外一个线程执行了插队操作
    User user2 = userMapper.selectById(1L);
    user2.setName("abc222");
    user2.setEmail("2314526");
    userMapper.updateById(user2);
    //自旋锁来多次尝试提交
    userMapper.updateById(user);//如果没有乐观锁就会覆盖插队线程的值
}

6.5 普通查询

  • selectById:通过id查询数据
  • selectBatchIds:通过集合查询数据
  • selectByMap:通过条件查询数据
@Test
public void selectById() {
    //通过id 查询一条数据
    User user = userMapper.selectById(1);
    System.out.println(user);
    //SELECT id,name,age,email,version,create_time,update_time FROM user WHERE id=?
}

@Test
public void selectBatchIds() {
    //查询多条数据
    List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
    users.forEach(System.out::println);
    //SELECT id,name,age,email,version,create_time,update_time FROM user WHERE id IN ( ? , ? , ? )
}

@Test
public void selectByMap() {
    //条件查询
    HashMap<String, Object> map = new HashMap<>();
    map.put("name","abc");
    List<User> users = userMapper.selectByMap(map);
    users.forEach(System.out::println);
    //SELECT id,name,age,email,version,create_time,update_time FROM user WHERE name = ?
}

6.6 分页查询

  • 简单的单表分页查询
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
@Test
public void selectPage() {
    Page<User> page = new Page<>(2,5);
     userMapper.selectPage(page, null);
    page.getRecords().forEach(System.out::println);
    //SELECT id,name,age,email,version,create_time,update_time FROM user LIMIT ?,?
}

6.7 普通删除

  • deleteById:通过id删除
  • deleteBatchIds:删除多条数据
  • deleteByMap:条件删除数据
@Test
public void deleteById() {
    //删除一条数据
    userMapper.deleteById(1);
    //DELETE FROM user WHERE id=?
}

@Test
public void deleteBatchIds() {
    //删除多条数据
    userMapper.deleteBatchIds(Arrays.asList(4, 2, 3));
    //DELETE FROM user WHERE id IN ( ? , ? , ? )
}

@Test
public void deleteByMap() {
    //条件删除
    HashMap<String, Object> map = new HashMap<>();
    map.put("id", "1667848352129540097");
    userMapper.deleteByMap(map);
    //DELETE FROM user WHERE id = ?
}

6.8 逻辑删除

  • 配置之后查询的时候也会默认查询为0的
  • 数据库增加字段:deletedint类型,长度1,默认0
  • 实体类增加属性
@TableLogic
private Integer deleted;
#配置逻辑删除
mybatis-plus.global-config.db-config.logic-delete-field=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0
  • 测试
@Test
public void deleteById() {
    //删除一条数据
    userMapper.deleteById(5);
}

6.9 条件构造器

  • allEqallEq({id:1,name:"老王",age:null}, false)--->id = 1 and name = '老王'
  • eqeq("name", "老王")--->name = '老王'
  • nene("name", "老王")--->name <> '老王'
  • gtgt("age", 18)--->age > 18
  • gege("age", 18)--->age >= 18
  • ltlt("age", 18)--->age < 18
  • lele("age", 18)--->age <= 18
  • betweenbetween("age", 18, 30)--->age between 18 and 30
  • notBetweennotBetween("age", 18, 30)--->age not between 18 and 30
  • likelike("name", "王")--->name like '%王%'
  • notLikenotLike("name", "王")--->name not like '%王%'
  • likeLeftlikeLeft("name", "王")--->name like '%王'
  • likeRightlikeRight("name", "王")--->name like '王%'
  • notLikeLeftnotLikeLeft("name", "王")--->name not like '%王'
  • notLikeRightnotLikeRight("name", "王")--->name not like '王%'
  • isNullisNull("name")--->name is null
  • isNotNullisNotNull("name")--->name is not null
  • inin("age", 1, 2, 3)--->age in (1,2,3)
  • notInnotIn("age", 1, 2, 3)--->age not in (1,2,3)
  • inSql
    • inSql("age", "1,2,3,4,5,6")--->age in (1,2,3,4,5,6)
    • inSql("id", "select id from table where id < 3")--->id in (select id from table where id < 3)
  • notInSql
    • notInSql("age", "1,2,3,4,5,6")--->age not in (1,2,3,4,5,6)
    •  notInSql("id", "select id from table where id < 3")--->id not in (select id from table where id < 3)
  • groupBygroupBy("id", "name")--->group by id,name
  • orderByAscorderByAsc("id", "name")--->order by id ASC,name ASC
  • orderByDescorderByDesc("id", "name")--->order by id DESC,name DESC
  • orderByorderBy(true, true, "id", "name")--->order by id ASC,name ASC
  • having
    • having("sum(age) > 10")--->having sum(age) > 10
    • having("sum(age) > {0}", 11)--->having sum(age) > 11
  • funcfunc(i -> if(true) {i.eq("id", 1)} else {i.ne("id", 1)})
  • or
    • eq("id",1).or().eq("name","老王")--->id = 1 or name = '老王'
    • or(i -> i.eq("name", "李白").ne("status", "活着"))--->or (name = '李白' and status <> '活着')
  • andand(i -> i.eq("name", "李白").ne("status", "活着"))--->and (name = '李白' and status <> '活着')
  • nestednested(i -> i.eq("name", "李白").ne("status", "活着"))--->(name = '李白' and status <> '活着')
  • apply
    • apply("id = 1")--->id = 1
    • apply("date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")--->date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")
    • apply("date_format(dateColumn,'%Y-%m-%d') = {0}", "2008-08-08")--->date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")
  • lastlast("limit 1")
  • existsexists("select id from table where age = 1")--->exists (select id from table where age = 1)
  • notExistsnotExists("select id from table where age = 1")--->not exists (select id from table where age = 1)

7、代码生成器

  • 用的依赖版本
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.3.1</version>
</dependency>
<!-- MyBatis-Plus 代码生成器依赖 -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.3.1</version>
</dependency>

<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity-engine-core</artifactId>
    <version>2.0</version>
</dependency>
  • 基础版本
package com.yap.mybatis_plus;

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.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;

public class CodeGenerator {

    public static void main(String[] args) {
        // 数据源配置
        DataSourceConfig dataSourceConfig = new DataSourceConfig();
        dataSourceConfig.setUrl("jdbc:mysql://"); // 数据库连接URL
        dataSourceConfig.setUsername("root"); // 数据库用户名
        dataSourceConfig.setPassword("r"); // 数据库密码
        dataSourceConfig.setDriverName("com.mysql.cj.jdbc.Driver"); // 数据库驱动类名

        // 全局配置
        GlobalConfig globalConfig = new GlobalConfig();
        globalConfig.setOutputDir(System.getProperty("user.dir") + "/mybatis_plus/src/main/java"); // 代码输出路径
        globalConfig.setAuthor("yap"); // 作者
        globalConfig.setOpen(false); // 生成后是否打开输出目录
        globalConfig.setFileOverride(true); // 是否覆盖已有文件
        globalConfig.setServiceName("%sService"); // Service接口命名方式
        globalConfig.setIdType(IdType.AUTO); // 主键生成策略
        globalConfig.setDateType(DateType.ONLY_DATE); // 日期类型

        // 包配置
        PackageConfig packageConfig = new PackageConfig();
        packageConfig.setParent("com.yap"); // 父包名
        packageConfig.setModuleName("users"); // 模块名
        packageConfig.setEntity("pojo"); // 实体类包名
        packageConfig.setMapper("mapper"); // Mapper接口包名
        packageConfig.setService("service"); // Service接口包名
        packageConfig.setServiceImpl("service.impl"); // Service实现类包名
        packageConfig.setController("controller"); // Controller包名
        packageConfig.setXml("mapper.xml"); // XML文件包名

        // 策略配置
        StrategyConfig strategyConfig = new StrategyConfig();
        strategyConfig.setInclude("users"); // 需要生成代码的表名
        strategyConfig.setEntityLombokModel(true); // 使用Lombok注解
        strategyConfig.setNaming(NamingStrategy.underline_to_camel); // 数据库表名映射到实体类的命名策略
        strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel); // 数据库字段名映射到实体类属性名的命名策略

        // 代码生成器配置
        AutoGenerator autoGenerator = new AutoGenerator();
        autoGenerator.setDataSource(dataSourceConfig);
        autoGenerator.setGlobalConfig(globalConfig);
        autoGenerator.setPackageInfo(packageConfig);
        autoGenerator.setStrategy(strategyConfig);

        // 执行生成代码
        autoGenerator.execute();
    }

}