2020年11月4日星期三

12、SpringBoot整合mybatis plus

概述

官网上对mybatis plus的简介:

mybatis plus是一款mybtais的增强工具,在mybatis的基础上只做增强不做改变,为简化开发,提高效率而生。

正如官方所说,在保证最小代码入侵的前提下,MP(mybatis plus)给mybatis使用者带来了更好的体验。

官网文档:https://baomidou.com/guide/

推荐视频:https://www.bilibili.com/video/BV1yA411t782?p=1

如何去学习?

建议结合视频和官网文档。

最节省时间的方式:直接看文档,按照文档写demo,写完了快速过一遍视频(倍速、快进),毕竟有些技巧文档可能并没有说明。

最全面的学习方式:完整的看一遍视频,看完每小节后,去官方文档查找对应内容,重点看视频没有提到的。

现在着急用:直接点击跳转到本文的 完整测试代码 ,简单看一下代码,应该可以直接复制粘贴使用。

依赖

官网特别提醒,如果引入了mybatis plus的依赖,就不要引入mybatis以及mybatis-spring-starter之类的依赖了,防止发生版本冲突。

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

如果你想要使用自动生成功能,需要导入(模板引擎依赖根据个人需要导入)

  <dependency>   <groupId>com.baomidou</groupId>   <artifactId>mybatis-plus-generator</artifactId>   <version>3.4.0</version>  </dependency>  <dependency>   <groupId>org.apache.velocity</groupId>   <artifactId>velocity-engine-core</artifactId>   <version>2.2</version>  </dependency>

注解

加载实体类的注解比较多,大多用于java实体类成员变量与数据库字段之间的映射,相当于传统mybatis

实体类

以User实体类为例,说明注解的作用。

@Data// @TableName(value = "user")public class User { @TableId(type = IdType.AUTO) private Long id; @TableField(value = "name") private String name; private Integer age; @TableField(fill = FieldFill.INSERT) private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime; @Version private Integer version; private StatusEnum status; @TableLogic private Integer deleted; private Integer departmentId;}

@TableName

是实体类名与数据库表名之间的映射。

MP默认是按照类名来映射的,例如类名为User,表为user,那就不需要加这个注解,可以自动映射。

如果类名与表名不一致,就必须用这个注解标注了。

@TableId

加载主键上

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

它有几种配置策略,可以按需取用

 #数据库自增 AUTO(0),   #mybatis plus 设置主键,雪花算法 NONE(1),   #开发者,手动复制 INPUT(2),  #mybatis分配id,可以使用Long、Integer、String ASSIGN_ID(3),  #分配一个uuid,必须是String ASSIGN_UUID(4),

@TableFiled

加在普通成员变量上

如果变量名与字段不一致,可以用value属性标注,可以实现变量和表字段的映射。

而fill可以实现自动填充。

 @TableField(fill = FieldFill.INSERT) private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime;

例如项目中,可能每张表都有createTime和updateTime,如果每次插入数据,都手动输入可能有点繁琐。所以可以使用fill属性。

FiledFill.INSERT表示在插入的时候填充。

FiledFill.INSERT_UPDATE表示插入、删除的时候都需要填充。

当然,我们还需要添加一个处理类

@Componentpublic class MyMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) {  this.setFieldValByName("createTime", new Date(), metaObject);  this.setFieldValByName("updateTime", new Date(), metaObject); } @Override public void updateFill(MetaObject metaObject) {  this.setFieldValByName("updateTime", new Date(), metaObject); }}

 

@TableFiled还有其他的一些属性

 String value() default ""; #该字段数据库中没有,不需要映射 boolean exist() default true; String condition() default ""; String update() default ""; FieldStrategy insertStrategy() default FieldStrategy.DEFAULT; FieldStrategy updateStrategy() default FieldStrategy.DEFAULT; FieldStrategy whereStrategy() default FieldStrategy.DEFAULT; #自动填充 FieldFill fill() default FieldFill.DEFAULT; #查询的时候,查询结果忽略该字段 boolean select() default true; boolean keepGlobalFormat() default false; JdbcType jdbcType() default JdbcType.UNDEFINED; Class<? extends TypeHandler> typeHandler() default UnknownTypeHandler.class; boolean javaType() default false; String numericScale() default "";

@Version

乐观锁的实现方式(取自官网):当要更新一条记录的时候,希望这条记录没有被别人更新:

  • 取出记录时,获取当前version
  • 更新时,带上这个version
  • 执行更新时, set version = newVersion where version = oldVersion
  • 如果version不对,就更新失败

mybatis plus中使用乐观锁,只需要添加一个注解@Version

 @Version private Integer version;

并配置拦截器

@Configurationpublic class MyBatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor(){  MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();  interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); // 乐观锁插件  return interceptor; }}

在执行更新操作的时候,会自动在sql后面加上 and version = xx

枚举

可以直接将java枚举类型映射为数据库字段

1、建需要映射的java枚举类

加注解

public enum StatusEnum { WORK(0,"上班"), REST(1,"休息"); StatusEnum(Integer code, String msg){  this.code = code;  this.msg = msg; } @EnumValue private Integer code; private String msg;}

2、实体类直接使用

名字也要映射,如果和数据库字段不一致,可以用@TableField指定

 private StatusEnum status;

3、配置文件中加上

mybatis-plus: type-enums-package: com.dayrain.nums

逻辑删除

什么时候用到逻辑删除?逻辑删除就是并非真正的删除某个数据,它依然存在数据库中,只是我在取用的时候,把他忽略。

具体的做法就是加一个字段,例如deleted,默认值为1 。

如果用户要删除这条数据,将它置为0。

每一次查询的时候,查询条件加上 and deleted = 1,这条数据表面上就完成了删除,但实际上还在数据库中,所以称为"逻辑删除"。

1、加注解

@TableLogicprivate Integer deleted;

2、配置文件

logic-not-delete-value表示正常状况的值,

logic-delete-value表示逻辑删除后的值,

这些是可以根据实际情况自行调整

mybatis-plus: global-config: db-config:  logic-not-delete-value: 1  logic-delete-value: 0

实战:

 @Test public void delete() {  userMapper.deleteById(1); }

控制台输出

==> Preparing: UPDATE user SET deleted=0 WHERE id=? AND deleted=1==> Parameters: 1(Integer)<== Updates: 0

可以看出,删除实际执行的update,逻辑上的删除

此时执行查询

 @Test void testSelect() {  List<User> users = userMapper.selectList(null);  users.forEach(System.out::println); }

控制台输出

发现查询的时候,会自动带上 deleted=1

JDBC Connection [HikariProxyConnection@1188871851 wrapping com.mysql.cj.jdbc.ConnectionImpl@36e43829] will not be managed by Spring==> Preparing: SELECT id,name,age,create_time,update_time,version,status,deleted FROM user WHERE deleted=1==> Parameters: <==  Total: 0

 增删改查

查询

@SpringBootTestpublic class UserTest { @Autowired UserMapper userMapper; @Test public void selectTest() {  //1、不加条件,查询所有  // userMapper.selectList(null);  //2、参数查  // SELECT id,name,age,create_time,update_time,version,status,deleted FROM user WHERE deleted=1 AND (name = ?)  // QueryWrapper<User> wrapper = new QueryWrapper();  // wrapper.eq("name","小李");  // List<User> users = userMapper.selectList(wrapper);  // users.forEach(System.out::println);  //3、多个参数  // SELECT id,name,age,create_time,update_time,version,status,deleted FROM user WHERE deleted=1 AND (name = ? AND age = ?)  // QueryWrapper<User> wrapper = new QueryWrapper();  // Map<String, Object>map = new HashMap<>();  // map.put("name", "小李");  // map.put("age", 22);  // wrapper.allEq(map);  // List<User> users = userMapper.selectList(wrapper);  //也可以直接用selectByMap直接传map  // users.forEach(System.out::println);  //4、比较,  // QueryWrapper<User> wrapper = new QueryWrapper();  //gt表示大于,同理  //ge(大于等于),ne(不等于),lt(小于),le(小于等于)  // wrapper.gt("age",22);  // List<User> users = userMapper.selectList(wrapper);  // users.forEach(System.out::println);  //5、模糊查询  //复习一下, mysql中  //'%小',表示以'小'结尾,'小%'表示以小开头。'%小%'表示中间有小  // QueryWrapper<User> wrapper = new QueryWrapper();  // wrapper.like("name","小");  '%小%'  // wrapper.likeLeft("name","小"); '%小'  // wrapper.likeRight("name","小"); '小%'  // List<User> users = userMapper.selectList(wrapper);  // users.forEach(System.out::println);  //6、复合查询  // SELECT id,name,age,create_time,update_time,version,status,deleted FROM user WHERE deleted=1 AND (id IN (select id from user where id < 10))  // QueryWrapper<User> wrapper = new QueryWrapper();  // wrapper.inSql("id", "select id from user where id < 10");  // List<User> users = userMapper.selectList(wrapper);  // users.forEach(System.out::println);  //7、排序  //desc asc  //SELECT id,name,age,create_time,update_time,version,status,deleted FROM user WHERE deleted=1 ORDER BY age DESC  // QueryWrapper<User> wrapper = new QueryWrapper();  // wrapper.orderByDesc("age");  // List<User> users = userMapper.selectList(wrapper);  // users.forEach(System.out::println);  //8、将结果封装成map对象  // QueryWrapper<User> wrapper = new QueryWrapper();  // wrapper.orderByDesc("age");  // List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);  // maps.forEach(System.out::println);  //9、分页  // Page<User> page = new Page<>(1, 2);  // Page<User> result = userMapper.selectPage(page, null);  // System.out.println(result);  // 结果  // System.out.println(result.getRecords());  /**   * ==> Preparing: SELECT COUNT(1) FROM user WHERE deleted = 1   * ==> Parameters:   * <== Columns: COUNT(1)   * <==  Row: 1   * <==  Total: 1   * ==> Preparing: SELECT id,name,age,create_time,update_time,version,status,deleted FROM user WHERE deleted=1 LIMIT ?   * ==> Parameters: 2(Long)   * <== Columns: id, name, age, create_time, update_time, version, status, deleted   * <==  Row: 1, 小李, 1, 2020-11-03 17:03:18, 2020-11-03 17:31:46, 2, 1, 1   * <==  Total: 1   */  //10、连表查询  //自定mapper  List<UserDetail> userDetails = userMapper.selectUserDetail(1L);  userDetails.forEach(System.out::println); }}

删除

@SpringBootTestpublic class UserTest { @Autowired UserMapper userMapper; @Test public void delete() {  //1、根据id删除  // userMapper.deleteById(1);  //2、根据id集合删除  // userMapper.deleteBatchIds(Arrays.asList(1,2));  //3、条件删除  QueryWrapper wrapper = new QueryWrapper();  wrapper.eq("age", 14);  userMapper.delete(wrapper); }}

更新

 @Test void updateUser() {  // User user = new User();  // user.setAge(1);  // user.setName("小李");  // user.setId(1323545415801319427L);  // userMapper.updateById(user);  // System.out.println(userMapper.selectById(user.getId()));  //条件更新,相当于updateSelective,如果为null,则不更新  //把年龄为25岁的人,名字都改成小明。  User user = new User();  user.setName("小明");  QueryWrapper queryWrapper = new QueryWrapper();  queryWrapper.eq("age", 25);  userMapper.update(user, queryWrapper); }

添加

 @Test void saveUser() {  User user = new User();  user.setAge(22);  user.setName("小李");  userMapper.insert(user); }

连表查询

MP对mybatis本身的代码没有入侵,如果你喜欢,mybatis依然可以像以前那么用。

当MP提供的内置sql不能满足需求时,比如连表查询等,我们可以自己写sql,实现较为复杂的功能。

注解版

例如现在有员工表、部门表,员工有部门id,需求是查出员工对应的部门名。

做一个简单的连表查询即可,但问题是如何去接收结果?我们这里的做法是定义一个对象来映射结果集,如果字段名和我们的成员变量名不一致,sql中可以采用"别名"的方式。

public interface UserMapper extends BaseMapper<User> { //原生注解 @Select("select user.name name, d.name departmentName from department d , user where user.id = #{id}") List<UserDetail>selectUserDetail(Long id);}

UserDetail类

@Datapublic class UserDetail { private String name; private String departmentName;}

**其实这里的@Select注解就和mybatis没有关系了,是原生mybatis提供的。包括下面的

传统的

 

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.dayrain.mapper.UserMapper"> <select id="selectAll" resultType="com.dayrain.entity.User">  select * from user; </select></mapper>

为了让Springboot扫描到这个文件,需要在配置文件中加上路径

默认的路径就是  classpath*:/mapper/**/*.

所以

 mapper-locations: classpath*:/mapper/**/*.

代码自动生成

启动类:

public class Main { public static void main(String[] args) {  //创建generator对象  AutoGenerator autoGenerator = new AutoGenerator();  //数据源  DataSourceConfig dataSourceConfig = new DataSourceConfig();  dataSourceConfig.setDbType(DbType.MYSQL);  dataSourceConfig.setUrl("jdbc:mysql://ip:3306/test?useUnicode=true&characterEncoding=UTF-8");  dataSourceConfig.setUsername("root");  dataSourceConfig.setPassword("root");  dataSourceConfig.setDriverName("com.mysql.cj.jdbc.Driver");  autoGenerator.setDataSource(dataSourceConfig);  //全局设置  GlobalConfig globalConfig = new GlobalConfig();  globalConfig.setOutputDir(System.getProperty("user.dir") + "/src/main/java");  globalConfig.setOpen(false);  globalConfig.setAuthor("dayrain");  //如果不设置,默认都是以I开头的service  globalConfig.setServiceName("%sService");  autoGenerator.setGlobalConfig(globalConfig);  //包信息
//路径根据个人需要配置
//该配置运行结果:会在com.dayrain包下生成一个generator文件夹,里面有entity、service等包。 PackageConfig packageConfig = new PackageConfig(); packageConfig.setParent("com.dayrain"); packageConfig.setModuleName("generator"); packageConfig.setController("controller"); packageConfig.setService("service"); packageConfig.setMapper("mapper"); autoGenerator.setPackageInfo(packageConfig); //配置策略 StrategyConfig strategyConfig = new StrategyConfig(); strategyConfig.setEntityLombokModel(true); strategyConfig.setNaming(NamingStrategy.underline_to_camel); strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel); autoGenerator.setStrategy(strategyConfig); autoGenerator.execute(); }}

添加自动生成依赖

 <dependency>   <groupId>com.baomidou</groupId>   <artifactId>mybatis-plus-generator</artifactId>   <version>3.4.0</version>  </dependency>  <dependency>   <groupId>org.apache.velocity</groupId>   <artifactId>velocity-engine-core</artifactId>   <version>2.2</version>  </dependency>

 

完整测试代码

目录结构

config

@Configurationpublic class MyBatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor(){  MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();  interceptor.addInnerInterceptor(new PaginationInnerInterceptor());  interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); // 乐观锁插件  return interceptor; }}

entity

用户

@Data// @TableName(value = "user")public class User { @TableId(type = IdType.AUTO) private Long id; @TableField(value = "name") private String name; private Integer age; @TableField(fill = FieldFill.INSERT) private Date createTime; @TableField(fill = FieldFill.INSERT_UPDATE) private Date updateTime; @Version private Integer version; private StatusEnum status; @TableLogic private Integer deleted; private Integer departmentId;}

部门

@Datapublic class Department { @TableId(type = IdType.AUTO) private Integer id; private String name;}

用户详细信息

@Datapublic class UserDetail { private String name; private String departmentName;}

handler

@Componentpublic class MyMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) {  this.setFieldValByName("createTime", new Date(), metaObject);  this.setFieldValByName("updateTime", new Date(), metaObject); } @Override public void updateFill(MetaObject metaObject) {  this.setFieldValByName("updateTime", new Date(), metaObject); }}

mapper

public interface UserMapper extends BaseMapper<User> { //原生注解 @Select("select user.name name, d.name departmentName from department d , user where user.id = #{id}") List<UserDetail>selectUserDetail(Long id); List<User>selectAll();}

enums

public enum StatusEnum { WORK(0,"上班"), REST(1,"休息"); StatusEnum(Integer code, String msg){  this.code = code;  this.msg = msg; } @EnumValue private Integer code; private String msg;}

启动类

@SpringBootApplication@MapperScan("com.dayrain.mapper")public class MybatisLearnApplication { public static void main(String[] args) {  SpringApplication.run(MybatisLearnApplication.class, args); }}

配置文件

spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://ip:3306/test?useUnicode=true&characterEncoding=UTF-8 username: root password: rootmybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl type-enums-package: com.dayrain.nums global-config: db-config:  logic-not-delete-value: 1  logic-delete-value: 0 mapper-locations: classpath*:/mapper/**/*.

位于 resouces/mapper下

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.dayrain.mapper.UserMapper"> <select id="selectAll" resultType="com.dayrain.entity.User">  select * from user; </select></mapper>

 sql

DROP TABLE IF EXISTS `user`;CREATE TABLE `user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, `age` int(11) NULL DEFAULT NULL, `create_time` datetime(0) NULL DEFAULT NULL, `update_time` datetime(0) NULL DEFAULT NULL, `version` int(11) NULL DEFAULT 1, `status` tinyint(4) NULL DEFAULT NULL, `deleted` tinyint(4) NULL DEFAULT 1, `department_id` int(11) NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE) ENGINE = InnoDB AUTO_INCREMENT = 1323545415801319428 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;SET FOREIGN_KEY_CHECKS = 1;

 

DROP TABLE IF EXISTS `department`;CREATE TABLE `department` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

 

如有错误,恳请批评指正!

原文转载:http://www.shaoqun.com/a/488098.html

hts:https://www.ikjzd.com/w/525

moss:https://www.ikjzd.com/w/1653

olive:https://www.ikjzd.com/w/2025


概述官网上对mybatisplus的简介:mybatisplus是一款mybtais的增强工具,在mybatis的基础上只做增强不做改变,为简化开发,提高效率而生。正如官方所说,在保证最小代码入侵的前提下,MP(mybatisplus)给mybatis使用者带来了更好的体验。官网文档:https://baomidou.com/guide/推荐视频:https://www.bilibili.com/
巨鲸:https://www.ikjzd.com/w/1986
hemingway:https://www.ikjzd.com/w/2344
珠海海泉湾有什么好玩的?有什么项目?:http://tour.shaoqun.com/a/41659.html
在珠海旅游可以去哪里?:http://tour.shaoqun.com/a/4087.html
广州番禺有什么又好玩又免费的地方?广州番禺免费景点:http://tour.shaoqun.com/a/3811.html