当前位置: 首页 > news >正文

安亭网站建设wordpress搜索不能用

安亭网站建设,wordpress搜索不能用,网站在线留言的用途,网页游戏排行榜2013前十名MyBatis-Plus 从入门到精通 前言快速入门创建一个SpringBoot项目导入依赖配置数据库创建一个实体类创建一个mapper接口在SpringBoot启动类上配置mapper接口的扫描路径在数据库中创建表编写一个SpringBoot测试类 核心功能注解CRUD接口Mapper CRUD接口Service CRUD 接口条件构造器… MyBatis-Plus 从入门到精通 前言快速入门创建一个SpringBoot项目导入依赖配置数据库创建一个实体类创建一个mapper接口在SpringBoot启动类上配置mapper接口的扫描路径在数据库中创建表编写一个SpringBoot测试类 核心功能注解CRUD接口Mapper CRUD接口Service CRUD 接口条件构造器使用示例Condition实体对象作为条件 allEq方法lambda条件构造器 更新操作反思删除操作自定义SQL原生mybatismybatis-plus分页查询AR模式主键策略小结 配置基本配置进阶配置 代码生成器高级功能逻辑删除自动填充乐观锁插件性能分析插件多租户SQL解析器动态表名SQL解析器总结 前言 mybatis-plus是一款Mybatis增强工具用于简化开发提高效率。下文使用缩写mp来简化表示mybatis-plus本文主要介绍mp搭配SpringBoot的使用。 注本文使用的mp版本是当前最新的3.4.2其它版本的差异请自行查阅文档 官方网站baomidou.com/ 快速入门 创建一个SpringBoot项目 导入依赖 !-- pom.xml -- ?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersionparentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion2.3.4.RELEASE/versionrelativePath/ !-- lookup parent from repository --/parentgroupIdcom.example/groupIdartifactIdmybatis-plus/artifactIdversion0.0.1-SNAPSHOT/versionnamemybatis-plus/namepropertiesjava.version1.8/java.version/propertiesdependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-configuration-processor/artifactId/dependencydependencygroupIdcom.baomidou/groupIdartifactIdmybatis-plus-boot-starter/artifactIdversion3.4.2/version/dependencydependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactIdscoperuntime/scope/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactId/dependency/dependenciesbuildpluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactId/plugin/plugins/build /project 配置数据库 # application.yml spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/yogurt?serverTimezoneAsia/Shanghaiusername: rootpassword: rootmybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #开启SQL语句打印 创建一个实体类 package com.example.mp.po; import lombok.Data; import java.time.LocalDateTime; Data public class User {private Long id;private String name;private Integer age;private String email;private Long managerId;private LocalDateTime createTime; }创建一个mapper接口 package com.example.mp.mappers; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.example.mp.po.User; public interface UserMapper extends BaseMapperUser { }在SpringBoot启动类上配置mapper接口的扫描路径 package com.example.mp; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; SpringBootApplication MapperScan(com.example.mp.mappers) public class MybatisPlusApplication {public static void main(String[] args) {SpringApplication.run(MybatisPlusApplication.class, args);} }在数据库中创建表 DROP TABLE IF EXISTS user; CREATE TABLE user ( id BIGINT(20) PRIMARY KEY NOT NULL COMMENT 主键, name VARCHAR(30) DEFAULT NULL COMMENT 姓名, age INT(11) DEFAULT NULL COMMENT 年龄, email VARCHAR(50) DEFAULT NULL COMMENT 邮箱, manager_id BIGINT(20) DEFAULT NULL COMMENT 直属上级id, create_time DATETIME DEFAULT NULL COMMENT 创建时间, CONSTRAINT manager_fk FOREIGN KEY(manager_id) REFERENCES user (id) ) ENGINEINNODB CHARSETUTF8;INSERT INTO user (id, name, age ,email, manager_id, create_time) VALUES (1, 大BOSS, 40, bossbaomidou.com, NULL, 2021-03-22 09:48:00), (2, 李经理, 40, bossbaomidou.com, 1, 2021-01-22 09:48:00), (3, 黄主管, 40, bossbaomidou.com, 2, 2021-01-22 09:48:00), (4, 吴组长, 40, bossbaomidou.com, 2, 2021-02-22 09:48:00), (5, 小菜, 40, bossbaomidou.com, 2, 2021-02-22 09:48:00)编写一个SpringBoot测试类 package com.example.mp; import com.example.mp.mappers.UserMapper; import com.example.mp.po.User; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.List; import static org.junit.Assert.*; RunWith(SpringRunner.class) SpringBootTest public class SampleTest {Autowiredprivate UserMapper mapper;Testpublic void testSelect() {ListUser list mapper.selectList(null);assertEquals(5, list.size());list.forEach(System.out::println);} }准备工作完成 数据库情况如下 项目目录如下 运行测试类 可以看到针对单表的基本CRUD操作只需要创建好实体类并创建一个继承自BaseMapper的接口即可可谓非常简洁。并且我们注意到User类中的managerIdcreateTime属性自动和数据库表中的manager_idcreate_time对应了起来这是因为mp自动做了数据库下划线命名到Java类的驼峰命名之间的转化。 核心功能 注解 mp一共提供了8个注解这些注解是用在Java的实体类上面的。 TableName注解在类上指定类和数据库表的映射关系。实体类的类名转成小写后和数据库表名相同时可以不指定该注解。TableId注解在实体类的某一字段上表示这个字段对应数据库表的主键。当主键名为id时表中列名为id实体类中字段名为id无需使用该注解显式指定主键mp会自动关联。若类的字段名和表的列名不一致可用value属性指定表的列名。另这个注解有个重要的属性type用于指定主键策略TableField注解在某一字段上指定Java实体类的字段和数据库表的列的映射关系。这个注解有如下几个应用场景。1、排除非表字段若Java实体类中某个字段不对应表中的任何列它只是用于保存一些额外的或组装后的数据则可以设置exist属性为false这样在对实体对象进行插入时会忽略这个字段。排除非表字段也可以通过其他方式完成如使用static或transient关键字但个人觉得不是很合理不做赘述2、字段验证策略通过insertStrategyupdateStrategywhereStrategy属性进行配置可以控制在实体对象进行插入更新或作为WHERE条件时对象中的字段要如何组装到SQL语句中。3、字段填充策略通过fill属性指定字段为空时会进行自动填充Version乐观锁注解EnumValue注解在枚举字段上TableLogic逻辑删除KeySequence序列主键策略oracleInterceptorIgnore插件过滤规则CRUD接口 mp封装了一些最基础的CRUD方法只需要直接继承mp提供的接口无需编写任何SQL即可食用。mp提供了两套接口分别是Mapper CRUD接口和Service CRUD接口。并且mp还提供了条件构造器Wrapper可以方便地组装SQL语句中的WHERE条件 Mapper CRUD接口 只需定义好实体类然后创建一个接口继承mp提供的BaseMapper即可食用。mp会在mybatis启动时自动解析实体类和表的映射关系并注入带有通用CRUD方法的mapper。BaseMapper里提供的方法部分列举如下 insert(T entity) 插入一条记录 deleteById(Serializable id) 根据主键id删除一条记录 delete(WrapperT wrapper) 根据条件构造器wrapper进行删除 selectById(Serializable id) 根据主键id进行查找 selectBatchIds(Collection idList) 根据主键id进行批量查找 selectByMap(MapString,Object map) 根据map中指定的列名和列值进行等值匹配查找 selectMaps(WrapperT wrapper) 根据 wrapper 条件查询记录将查询结果封装为一个MapMap的key为结果的列value为值 selectList(WrapperT wrapper) 根据条件构造器wrapper进行查询 update(T entity, WrapperT wrapper) 根据条件构造器wrapper进行更新 updateById(T entity) ...下面讲解几个比较特别的方法 selectMaps BaseMapper接口还提供了一个selectMaps方法这个方法会将查询结果封装为一个MapMap的key为结果的列value为值 该方法的使用场景如下 1、只查部分列 当某个表的列特别多而SELECT的时候只需要选取个别列查询出的结果也没必要封装成Java实体类对象时只查部分列时封装成实体后实体对象中的很多属性会是null则可以用selectMaps获取到指定的列后再自行进行处理即可 比如 Testpublic void test3() {QueryWrapperUser wrapper new QueryWrapper();wrapper.select(id,name,email).likeRight(name,黄);ListMapString, Object maps userMapper.selectMaps(wrapper);maps.forEach(System.out::println);} 2、进行数据统计 比如 // 按照直属上级进行分组查询每组的平均年龄最大年龄最小年龄 /** select avg(age) avg_age ,min(age) min_age, max(age) max_age from user group by manager_id having sum(age) 500; **/Test public void test3() {QueryWrapperUser wrapper new QueryWrapper();wrapper.select(manager_id, avg(age) avg_age, min(age) min_age, max(age) max_age).groupBy(manager_id).having(sum(age) {0}, 500);ListMapString, Object maps userMapper.selectMaps(wrapper);maps.forEach(System.out::println); }selectObjs 只会返回第一个字段第一列的值其他字段会被舍弃 比如 Testpublic void test3() {QueryWrapperUser wrapper new QueryWrapper();wrapper.select(id, name).like(name, 黄);ListObject objects userMapper.selectObjs(wrapper);objects.forEach(System.out::println);}得到的结果只封装了第一列的id selectCount 查询满足条件的总数注意使用这个方法不能调用QueryWrapper的select方法设置要查询的列了。这个方法会自动添加select count(1) 比如 Testpublic void test3() {QueryWrapperUser wrapper new QueryWrapper();wrapper.like(name, 黄);Integer count userMapper.selectCount(wrapper);System.out.println(count);} Service CRUD 接口 另外一套CRUD是Service层的只需要编写一个接口继承IService并创建一个接口实现类即可食用。这个接口提供的CRUD方法和Mapper接口提供的功能大同小异比较明显的区别在于IService支持了更多的批量化操作如saveBatchsaveOrUpdateBatch等方法。 食用示例如下 1、首先新建一个接口继承IService package com.example.mp.service;import com.baomidou.mybatisplus.extension.service.IService; import com.example.mp.po.User;public interface UserService extends IServiceUser { }2、 创建这个接口的实现类并继承ServiceImpl最后打上Service注解注册到Spring容器中即可食用 package com.example.mp.service.impl;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.example.mp.mappers.UserMapper; import com.example.mp.po.User; import com.example.mp.service.UserService; import org.springframework.stereotype.Service;Service public class UserServiceImpl extends ServiceImplUserMapper, User implements UserService { } 3、测试代码 package com.example.mp;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.example.mp.po.User; import com.example.mp.service.UserService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; RunWith(SpringRunner.class) SpringBootTest public class ServiceTest {Autowiredprivate UserService userService;Testpublic void testGetOne() {LambdaQueryWrapperUser wrapper Wrappers.UserlambdaQuery();wrapper.gt(User::getAge, 28);User one userService.getOne(wrapper, false); // 第二参数指定为false,使得在查到了多行记录时,不抛出异常,而返回第一条记录System.out.println(one);} }另IService也支持链式调用代码写起来非常简洁查询示例如下 Testpublic void testChain() {ListUser list userService.lambdaQuery().gt(User::getAge, 39).likeRight(User::getName, 王).list();list.forEach(System.out::println);}更新示例如下 Testpublic void testChain() {userService.lambdaUpdate().gt(User::getAge, 39).likeRight(User::getName, 王).set(User::getEmail, w39baomidou.com).update();}删除示例如下 Testpublic void testChain() {userService.lambdaUpdate().like(User::getName, 青蛙).remove();}条件构造器 mp让我觉得极其方便的一点在于其提供了强大的条件构造器Wrapper可以非常方便的构造WHERE条件。条件构造器主要涉及到3个类AbstractWrapper。QueryWrapperUpdateWrapper它们的类关系如下 在AbstractWrapper中提供了非常多的方法用于构建WHERE条件而QueryWrapper针对SELECT语句提供了select()方法可自定义需要查询的列而UpdateWrapper针对UPDATE语句提供了set()方法用于构造set语句。条件构造器也支持lambda表达式写起来非常舒爽。 下面对AbstractWrapper中用于构建SQL语句中的WHERE条件的方法进行部分列举 eqequals等于 allEqall equals全等于 nenot equals不等于 gtgreater than 大于 gegreater than or equals大于等于≥ ltless than小于 leless than or equals小于等于≤ between相当于SQL中的BETWEEN notBetween like模糊匹配。like(name,黄)相当于SQL的name like %黄% likeRight模糊匹配右半边。likeRight(name,黄)相当于SQL的name like 黄% likeLeft模糊匹配左半边。likeLeft(name,黄)相当于SQL的name like %黄 notLikenotLike(name,黄)相当于SQL的name not like %黄% isNull isNotNull in andSQL连接符AND orSQL连接符OR apply用于拼接SQL该方法可用于数据库函数并可以动态传参 .......使用示例 下面通过一些具体的案例来练习条件构造器的使用。使用前文创建的user表 // 案例先展示需要完成的SQL语句后展示Wrapper的写法// 1. 名字中包含佳且年龄小于25 // SELECT * FROM user WHERE name like %佳% AND age 25 QueryWrapperUser wrapper new QueryWrapper(); wrapper.like(name, 佳).lt(age, 25); ListUser users userMapper.selectList(wrapper); // 下面展示SQL时仅展示WHERE条件展示代码时, 仅展示Wrapper构建部分// 2. 姓名为黄姓且年龄大于等于20小于等于40且email字段不为空 // name like 黄% AND age BETWEEN 20 AND 40 AND email is not null wrapper.likeRight(name,黄).between(age, 20, 40).isNotNull(email);// 3. 姓名为黄姓或者年龄大于等于40按照年龄降序排列年龄相同则按照id升序排列 // name like 黄% OR age 40 order by age desc, id asc wrapper.likeRight(name,黄).or().ge(age,40).orderByDesc(age).orderByAsc(id);// 4.创建日期为2021年3月22日并且直属上级的名字为李姓 // date_format(create_time,%Y-%m-%d) 2021-03-22 AND manager_id IN (SELECT id FROM user WHERE name like 李%) wrapper.apply(date_format(create_time, %Y-%m-%d) {0}, 2021-03-22) // 建议采用{index}这种方式动态传参, 可防止SQL注入.inSql(manager_id, SELECT id FROM user WHERE name like 李%); // 上面的apply, 也可以直接使用下面这种方式做字符串拼接但当这个日期是一个外部参数时这种方式有SQL注入的风险 wrapper.apply(date_format(create_time, %Y-%m-%d) 2021-03-22);// 5. 名字为王姓并且年龄小于40或者邮箱不为空 // name like 王% AND (age 40 OR email is not null) wrapper.likeRight(name, 王).and(q - q.lt(age, 40).or().isNotNull(email));// 6. 名字为王姓或者年龄小于40并且年龄大于20并且邮箱不为空 // name like 王% OR (age 40 AND age 20 AND email is not null) wrapper.likeRight(name, 王).or(q - q.lt(age,40).gt(age,20).isNotNull(email));// 7. (年龄小于40或者邮箱不为空) 并且名字为王姓 // (age 40 OR email is not null) AND name like 王% wrapper.nested(q - q.lt(age, 40).or().isNotNull(email)).likeRight(name, 王);// 8. 年龄为30313435 // age IN (30,31,34,35) wrapper.in(age, Arrays.asList(30,31,34,35)); // 或 wrapper.inSql(age,30,31,34,35);// 9. 年龄为30313435, 返回满足条件的第一条记录 // age IN (30,31,34,35) LIMIT 1 wrapper.in(age, Arrays.asList(30,31,34,35)).last(LIMIT 1);// 10. 只选出id, name 列 (QueryWrapper 特有) // SELECT id, name FROM user; wrapper.select(id, name);// 11. 选出id, name, age, email, 等同于排除 manager_id 和 create_time // 当列特别多, 而只需要排除个别列时, 采用上面的方式可能需要写很多个列, 可以采用重载的select方法指定需要排除的列 wrapper.select(User.class, info - {String columnName info.getColumn();return !create_time.equals(columnName) !manager_id.equals(columnName);}); Condition 条件构造器的诸多方法中均可以指定一个boolean类型的参数condition用来决定该条件是否加入最后生成的WHERE语句中比如 String name 黄; // 假设name变量是一个外部传入的参数 QueryWrapperUser wrapper new QueryWrapper(); wrapper.like(StringUtils.hasText(name), name, name); // 仅当 StringUtils.hasText(name) 为 true 时, 会拼接这个like语句到WHERE中 // 其实就是对下面代码的简化 if (StringUtils.hasText(name)) {wrapper.like(name, name); } 实体对象作为条件 调用构造函数创建一个Wrapper对象时可以传入一个实体对象。后续使用这个Wrapper时会以实体对象中的非空属性构建WHERE条件默认构建等值匹配的WHERE条件这个行为可以通过实体类里各个字段上的TableField注解中的condition属性进行改变 示例如下 Testpublic void test3() {User user new User();user.setName(黄主管);user.setAge(28);QueryWrapperUser wrapper new QueryWrapper(user);ListUser users userMapper.selectList(wrapper);users.forEach(System.out::println);}若希望针对某些属性改变等值匹配的行为则可以在实体类中用TableField注解进行配置示例如下 package com.example.mp.po; import com.baomidou.mybatisplus.annotation.SqlCondition; import com.baomidou.mybatisplus.annotation.TableField; import lombok.Data; import java.time.LocalDateTime; Data public class User {private Long id;TableField(condition SqlCondition.LIKE) // 配置该字段使用like进行拼接private String name;private Integer age;private String email;private Long managerId;private LocalDateTime createTime; }运行下面的测试代码 Testpublic void test3() {User user new User();user.setName(黄);QueryWrapperUser wrapper new QueryWrapper(user);ListUser users userMapper.selectList(wrapper);users.forEach(System.out::println);}TableField中配置的condition属性实则是一个字符串SqlCondition类中预定义了一些字符串以供选择 package com.baomidou.mybatisplus.annotation;public class SqlCondition {//下面的字符串中, %s 是占位符, 第一个 %s 是列名, 第二个 %s 是列的值public static final String EQUAL %s#{%s};public static final String NOT_EQUAL %slt;gt;#{%s};public static final String LIKE %s LIKE CONCAT(%%,#{%s},%%);public static final String LIKE_LEFT %s LIKE CONCAT(%%,#{%s});public static final String LIKE_RIGHT %s LIKE CONCAT(#{%s},%%); }SqlCondition中提供的配置比较有限当我们需要或等拼接方式则需要自己定义。比如 package com.example.mp.po; import com.baomidou.mybatisplus.annotation.SqlCondition; import com.baomidou.mybatisplus.annotation.TableField; import lombok.Data; import java.time.LocalDateTime; Data public class User {private Long id;TableField(condition SqlCondition.LIKE)private String name;TableField(condition %s gt; #{%s}) // 这里相当于大于, 其中 gt; 是字符实体private Integer age;private String email;private Long managerId;private LocalDateTime createTime; }测试如下 Testpublic void test3() {User user new User();user.setName(黄);user.setAge(30);QueryWrapperUser wrapper new QueryWrapper(user);ListUser users userMapper.selectList(wrapper);users.forEach(System.out::println);}allEq方法 allEq方法传入一个map用来做等值匹配 Testpublic void test3() {QueryWrapperUser wrapper new QueryWrapper();MapString, Object param new HashMap();param.put(age, 40);param.put(name, 黄飞飞);wrapper.allEq(param);ListUser users userMapper.selectList(wrapper);users.forEach(System.out::println);}当allEq方法传入的Map中有value为null的元素时默认会设置为is null Testpublic void test3() {QueryWrapperUser wrapper new QueryWrapper();MapString, Object param new HashMap();param.put(age, 40);param.put(name, null);wrapper.allEq(param);ListUser users userMapper.selectList(wrapper);users.forEach(System.out::println);}若想忽略map中value为null的元素可以在调用allEq时设置参数boolean null2IsNull为false Testpublic void test3() {QueryWrapperUser wrapper new QueryWrapper();MapString, Object param new HashMap();param.put(age, 40);param.put(name, null);wrapper.allEq(param, false);ListUser users userMapper.selectList(wrapper);users.forEach(System.out::println);}若想要在执行allEq时过滤掉Map中的某些元素可以调用allEq的重载方法allEq(BiPredicateR, V filter, MapR, V params) Testpublic void test3() {QueryWrapperUser wrapper new QueryWrapper();MapString, Object param new HashMap();param.put(age, 40);param.put(name, 黄飞飞);wrapper.allEq((k,v) - !name.equals(k), param); // 过滤掉map中key为name的元素ListUser users userMapper.selectList(wrapper);users.forEach(System.out::println);}lambda条件构造器 lambda条件构造器支持lambda表达式可以不必像普通条件构造器一样以字符串形式指定列名它可以直接以实体类的方法引用来指定列。示例如下 Testpublic void testLambda() {LambdaQueryWrapperUser wrapper new LambdaQueryWrapper();wrapper.like(User::getName, 黄).lt(User::getAge, 30);ListUser users userMapper.selectList(wrapper);users.forEach(System.out::println);}像普通的条件构造器列名是用字符串的形式指定无法在编译期进行列名合法性的检查这就不如lambda条件构造器来的优雅。 另外还有个链式lambda条件构造器使用示例如下 Testpublic void testLambda() {LambdaQueryChainWrapperUser chainWrapper new LambdaQueryChainWrapper(userMapper);ListUser users chainWrapper.like(User::getName, 黄).gt(User::getAge, 30).list();users.forEach(System.out::println);}更新操作 上面介绍的都是查询操作,现在来讲更新和删除操作。 BaseMapper中提供了2个更新方法 1、updateById(T entity) 根据入参entity的id主键进行更新对于entity中非空的属性会出现在UPDATE语句的SET后面即entity中非空的属性会被更新到数据库示例如下 RunWith(SpringRunner.class) SpringBootTest public class UpdateTest {Autowiredprivate UserMapper userMapper;Testpublic void testUpdate() {User user new User();user.setId(2L);user.setAge(18);userMapper.updateById(user);} }2、update(T entity, Wrapper wrapper) 根据实体entity和条件构造器wrapper进行更新示例如下 Testpublic void testUpdate2() {User user new User();user.setName(王三蛋);LambdaUpdateWrapperUser wrapper new LambdaUpdateWrapper();wrapper.between(User::getAge, 26,31).likeRight(User::getName,吴);userMapper.update(user, wrapper);}额外演示一下把实体对象传入Wrapper即用实体对象构造WHERE条件的案例 Testpublic void testUpdate3() {User whereUser new User();whereUser.setAge(40);whereUser.setName(王);LambdaUpdateWrapperUser wrapper new LambdaUpdateWrapper(whereUser);User user new User();user.setEmail(sharebaomidou.com);user.setManagerId(10L);userMapper.update(user, wrapper);}注意到我们的User类中对name属性和age属性进行了如下的设置 Data public class User {private Long id;TableField(condition SqlCondition.LIKE)private String name;TableField(condition %s gt; #{%s})private Integer age;private String email;private Long managerId;private LocalDateTime createTime; }再额外演示一下链式lambda条件构造器的使用 Testpublic void testUpdate5() {LambdaUpdateChainWrapperUser wrapper new LambdaUpdateChainWrapper(userMapper);wrapper.likeRight(User::getEmail, share).like(User::getName, 飞飞).set(User::getEmail, ffbaomidou.com).update();}反思 由于BaseMapper提供的2个更新方法都是传入一个实体对象去执行更新这在需要更新的列比较多时还好若想要更新的只有那么一列或者两列则创建一个实体对象就显得有点麻烦。针对这种情况UpdateWrapper提供有set方法可以手动拼接SQL中的SET语句此时可以不必传入实体对象示例如下 Testpublic void testUpdate4() {LambdaUpdateWrapperUser wrapper new LambdaUpdateWrapper();wrapper.likeRight(User::getEmail, share).set(User::getManagerId, 9L);userMapper.update(null, wrapper);}删除操作 BaseMapper一共提供了如下几个用于删除的方法 deleteById 根据主键id进行删除 deleteBatchIds 根据主键id进行批量删除 deleteByMap 根据Map进行删除Map中的key为列名value为值根据列和值进行等值匹配 delete(WrapperT wrapper) 根据条件构造器Wrapper进行删除与前面查询和更新的操作大同小异不做赘述 自定义SQL 当mp提供的方法还不能满足需求时则可以自定义SQL。 原生mybatis 示例如下 1、注解方式 package com.example.mp.mappers;import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.example.mp.po.User; import org.apache.ibatis.annotations.Select;import java.util.List;/*** Author yogurtzzz* Date 2021/3/18 11:21**/ public interface UserMapper extends BaseMapperUser {Select(select * from user)ListUser selectRaw(); }2、xml方式 ?xml version1.0 encodingUTF-8? !DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd mapper namespacecom.example.mp.mappers.UserMapperselect idselectRaw resultTypecom.example.mp.po.UserSELECT * FROM user/select /mapperpackage com.example.mp.mappers;import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.example.mp.po.User; import org.apache.ibatis.annotations.Select; import java.util.List;public interface UserMapper extends BaseMapperUser {ListUser selectRaw(); }使用xml时若xml文件与mapper接口文件不在同一目录下则需要在application.yml中配置mapper.xml的存放路径 mybatis-plus:mapper-locations: /mappers/*若有多个地方存放mapper则用数组形式进行配置 mybatis-plus:mapper-locations: - /mappers/*- /com/example/mp/*测试代码如下 Test public void testCustomRawSql() {ListUser users userMapper.selectRaw();users.forEach(System.out::println); }mybatis-plus 也可以使用mp提供的Wrapper条件构造器来自定义SQL 示例如下 1、注解方式 package com.example.mp.mappers; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.toolkit.Constants; import com.example.mp.po.User; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import java.util.List;public interface UserMapper extends BaseMapperUser {// SQL中不写WHERE关键字且固定使用${ew.customSqlSegment}Select(select * from user ${ew.customSqlSegment})ListUser findAll(Param(Constants.WRAPPER)WrapperUser wrapper); }2、xml方式 package com.example.mp.mappers; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.example.mp.po.User; import java.util.List;public interface UserMapper extends BaseMapperUser {ListUser findAll(WrapperUser wrapper); }!-- UserMapper.xml -- ?xml version1.0 encodingUTF-8? !DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//EN http://mybatis.org/dtd/mybatis-3-mapper.dtd mapper namespacecom.example.mp.mappers.UserMapperselect idfindAll resultTypecom.example.mp.po.UserSELECT * FROM user ${ew.customSqlSegment}/select /mapper分页查询 BaseMapper中提供了2个方法进行分页查询分别是selectPage和selectMapsPage前者会将查询的结果封装成Java实体对象后者会封装成MapString,Object。分页查询的食用示例如下 1、创建mp的分页拦截器注册到Spring容器中 package com.example.mp.config; import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;Configuration public class MybatisPlusConfig {/** 新版mp **/Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));return interceptor;}/** 旧版mp 用 PaginationInterceptor **/ }2、执行分页查询 Test public void testPage() {LambdaQueryWrapperUser wrapper new LambdaQueryWrapper();wrapper.ge(User::getAge, 28);// 设置分页信息, 查第3页, 每页2条数据PageUser page new Page(3, 2);// 执行分页查询PageUser userPage userMapper.selectPage(page, wrapper);System.out.println(总记录数 userPage.getTotal());System.out.println(总页数 userPage.getPages());System.out.println(当前页码 userPage.getCurrent());// 获取分页查询结果ListUser records userPage.getRecords();records.forEach(System.out::println); }3、其他 注意到分页查询总共发出了2次SQL一次查总记录数一次查具体数据。若希望不查总记录数仅查分页结果。可以通过Page的重载构造函数指定isSearchCount为false即可 public Page(long current, long size, boolean isSearchCount)在实际开发中可能遇到多表联查的场景此时BaseMapper中提供的单表分页查询的方法无法满足需求需要自定义SQL示例如下使用单表查询的SQL进行演示实际进行多表联查时修改SQL语句即可 1、在mapper接口中定义一个函数接收一个Page对象为参数并编写自定义SQL // 这里采用纯注解方式。当然若SQL比较复杂建议还是采用XML的方式 Select(SELECT * FROM user ${ew.customSqlSegment}) PageUser selectUserPage(PageUser page, Param(Constants.WRAPPER) WrapperUser wrapper);2、执行查询 Test public void testPage2() {LambdaQueryWrapperUser wrapper new LambdaQueryWrapper();wrapper.ge(User::getAge, 28).likeRight(User::getName, 王);PageUser page new Page(3,2);PageUser userPage userMapper.selectUserPage(page, wrapper);System.out.println(总记录数 userPage.getTotal());System.out.println(总页数 userPage.getPages());userPage.getRecords().forEach(System.out::println); }AR模式 ActiveRecord模式通过操作实体对象直接操作数据库表。与ORM有点类似。 示例如下 1、让实体类User继承自Model package com.example.mp.po;import com.baomidou.mybatisplus.annotation.SqlCondition; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.extension.activerecord.Model; import lombok.Data; import lombok.EqualsAndHashCode; import java.time.LocalDateTime;EqualsAndHashCode(callSuper false) Data public class User extends ModelUser {private Long id;TableField(condition SqlCondition.LIKE)private String name;TableField(condition %s gt; #{%s})private Integer age;private String email;private Long managerId;private LocalDateTime createTime; }2、直接调用实体对象上的方法 Test public void insertAr() {User user new User();user.setId(15L);user.setName(我是AR猪);user.setAge(1);user.setEmail(arbaomidou.com);user.setManagerId(1L);boolean success user.insert(); // 插入System.out.println(success); }其他示例 // 查询 Test public void selectAr() {User user new User();user.setId(15L);User result user.selectById();System.out.println(result); } // 更新 Test public void updateAr() {User user new User();user.setId(15L);user.setName(王全蛋);user.updateById(); } //删除 Test public void deleteAr() {User user new User();user.setId(15L);user.deleteById(); }主键策略 在定义实体类时用TableId指定主键而其type属性可以指定主键策略。 mp支持多种主键策略默认的策略是基于雪花算法的自增id。全部主键策略定义在了枚举类IdType中IdType有如下的取值 AUTO数据库ID自增依赖于数据库。在插入操作生成SQL语句时不会插入主键这一列NONE未设置主键类型。若在代码中没有手动设置主键则会根据主键的全局策略自动生成默认的主键全局策略是基于雪花算法的自增IDINPUT需要手动设置主键若不设置。插入操作生成SQL语句时主键这一列的值会是null。oracle的序列主键需要使用这种方式ASSIGN_ID当没有手动设置主键即实体类中的主键属性为空时才会自动填充使用雪花算法ASSIGN_UUID当实体类的主键属性为空时才会自动填充使用UUID....还有几种是已过时的就不再列举可以针对每个实体类使用TableId注解指定该实体类的主键策略这可以理解为局部策略。若希望对所有的实体类都采用同一种主键策略挨个在每个实体类上进行配置则太麻烦了此时可以用主键的全局策略。只需要在application.yml进行配置即可。比如配置了全局采用自增主键策略 # application.yml mybatis-plus:global-config:db-config:id-type: auto下面对不同主键策略的行为进行演示 1、AUTO 在User上对id属性加上注解然后将MYSQL的user表修改其主键为自增。 EqualsAndHashCode(callSuper false) Data public class User extends ModelUser {TableId(type IdType.AUTO)private Long id;TableField(condition SqlCondition.LIKE)private String name;TableField(condition %s gt; #{%s})private Integer age;private String email;private Long managerId;private LocalDateTime createTime; }测试 Test public void testAuto() {User user new User();user.setName(我是青蛙呱呱);user.setAge(99);user.setEmail(frogbaomidou.com);user.setCreateTime(LocalDateTime.now());userMapper.insert(user);System.out.println(user.getId()); }可以看到代码中没有设置主键ID发出的SQL语句中也没有设置主键ID并且插入结束后主键ID会被写回到实体对象。 2、NONE 在MYSQL的user表中去掉主键自增。然后修改User类若不配置TableId注解默认主键策略也是NONE TableId(type IdType.NONE) private Long id;插入时若实体类的主键ID有值则使用之若主键ID为空则使用主键全局策略来生成一个ID。 3、其余的策略类似不赘述 小结 AUTO依赖于数据库的自增主键插入时实体对象无需设置主键插入成功后主键会被写回实体对象。 INPUT完全依赖于用户输入。实体对象中主键ID是什么插入到数据库时就设置什么。若有值便设置值若为null则设置null其余的几个策略都是在实体对象中主键ID为空时才会自动生成。 NONE会跟随全局策略ASSIGN_ID采用雪花算法ASSIGN_UUID采用UUID 全局配置在application.yml中进行即可针对单个实体类的局部配置使用TableId即可。对于某个实体类若它有局部主键策略则采用之否则跟随全局策略。 配置 mybatis plus有许多可配置项可在application.yml中进行配置如上面的全局主键策略。下面列举部分配置项 基本配置 configLocation若有单独的mybatis配置用这个注解指定mybatis的配置文件mybatis的全局配置文件 mapperLocationsmybatis mapper所对应的xml文件的位置 typeAliasesPackagemybatis的别名包扫描路径 .....进阶配置 1、mapUnderscoreToCamelCase是否开启自动驼峰命名规则映射。默认开启 2、dbTpe数据库类型。一般不用配会根据数据库连接url自动识别 3、fieldStrategy已过时字段验证策略。该配置项在最新版的mp文档中已经找不到了被细分成了insertStrategyupdateStrategyselectStrategy。默认值是NOT_NULL即对于实体对象中非空的字段才会组装到最终的SQL语句中。 有如下几种可选配置 这个配置项可在application.yml中进行全局配置也可以在某一实体类中对某一字段用TableField注解进行局部配置 这个字段验证策略有什么用呢在UPDATE操作中能够体现出来若用一个User对象执行UPDATE操作我们希望只对User对象中非空的属性更新到数据库中其他属性不做更新则NOT_NULL可以满足需求。而若updateStrategy配置为IGNORED则不会进行非空判断会将实体对象中的全部属性如实组装到SQL中这样执行UPDATE时可能就将一些不想更新的字段设置为了NULL。 IGNORED忽略校验。即不做校验。实体对象中的全部字段无论值是什么都如实地被组装到SQL语句中为NULL的字段在SQL语句中就组装为NULL。 NOT_NULL非NULL校验。只会将非NULL的字段组装到SQL语句中 NOT_EMPTY非空校验。当有字段是字符串类型时只组装非空字符串对其他类型的字段等同于NOT_NULL NEVER不加入SQL。所有字段不加入到SQL语句4、tablePrefix添加表名前缀 比如 mybatis-plus:global-config:db-config:table-prefix: xx_然后将MYSQL中的表做一下修改。但Java实体类保持不变仍然为User。 测试 Testpublic void test3() {QueryWrapperUser wrapper new QueryWrapper();wrapper.like(name, 黄);Integer count userMapper.selectCount(wrapper);System.out.println(count);}可以看到拼接出来的SQL在表名前面添加了前缀 完整的配置可以参考mp的官网 https://baomidou.com/config/#mapperlocations 代码生成器 mp提供一个生成器可快速生成Entity实体类Mapper接口ServiceController等全套代码。 示例如下 public class GeneratorTest {Testpublic void generate() {AutoGenerator generator new AutoGenerator();// 全局配置GlobalConfig config new GlobalConfig();String projectPath System.getProperty(user.dir);// 设置输出到的目录config.setOutputDir(projectPath /src/main/java);config.setAuthor(yogurt);// 生成结束后是否打开文件夹config.setOpen(false);// 全局配置添加到 generator 上generator.setGlobalConfig(config);// 数据源配置DataSourceConfig dataSourceConfig new DataSourceConfig();dataSourceConfig.setUrl(jdbc:mysql://localhost:3306/yogurt?serverTimezoneAsia/Shanghai);dataSourceConfig.setDriverName(com.mysql.cj.jdbc.Driver);dataSourceConfig.setUsername(root);dataSourceConfig.setPassword(root);// 数据源配置添加到 generatorgenerator.setDataSource(dataSourceConfig);// 包配置, 生成的代码放在哪个包下PackageConfig packageConfig new PackageConfig();packageConfig.setParent(com.example.mp.generator);// 包配置添加到 generatorgenerator.setPackageInfo(packageConfig);// 策略配置StrategyConfig strategyConfig new StrategyConfig();// 下划线驼峰命名转换strategyConfig.setNaming(NamingStrategy.underline_to_camel);strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel);// 开启lombokstrategyConfig.setEntityLombokModel(true);// 开启RestControllerstrategyConfig.setRestControllerStyle(true);generator.setStrategy(strategyConfig);generator.setTemplateEngine(new FreemarkerTemplateEngine());// 开始生成generator.execute();} }运行后可以看到生成了如下图所示的全套代码 高级功能 高级功能的演示需要用到一张新的表user2 DROP TABLE IF EXISTS user2; CREATE TABLE user2 ( id BIGINT(20) PRIMARY KEY NOT NULL COMMENT 主键id, name VARCHAR(30) DEFAULT NULL COMMENT 姓名, age INT(11) DEFAULT NULL COMMENT 年龄, email VARCHAR(50) DEFAULT NULL COMMENT 邮箱, manager_id BIGINT(20) DEFAULT NULL COMMENT 直属上级id, create_time DATETIME DEFAULT NULL COMMENT 创建时间, update_time DATETIME DEFAULT NULL COMMENT 修改时间, version INT(11) DEFAULT 1 COMMENT 版本, deleted INT(1) DEFAULT 0 COMMENT 逻辑删除标识,0-未删除,1-已删除, CONSTRAINT manager_fk FOREIGN KEY(manager_id) REFERENCES user2(id) ) ENGINE INNODB CHARSETUTF8;INSERT INTO user2(id, name, age, email, manager_id, create_time) VALUES (1, 老板, 40 ,bossbaomidou.com ,NULL, 2021-03-28 13:12:40), (2, 王狗蛋, 40 ,gdbaomidou.com ,1, 2021-03-28 13:12:40), (3, 王鸡蛋, 40 ,jdbaomidou.com ,2, 2021-03-28 13:12:40), (4, 王鸭蛋, 40 ,ydbaomidou.com ,2, 2021-03-28 13:12:40), (5, 王猪蛋, 40 ,zdbaomidou.com ,2, 2021-03-28 13:12:40), (6, 王软蛋, 40 ,rdbaomidou.com ,2, 2021-03-28 13:12:40), (7, 王铁蛋, 40 ,tdbaomidou.com ,2, 2021-03-28 13:12:40)并创建对应的实体类User2 package com.example.mp.po; import lombok.Data; import java.time.LocalDateTime; Data public class User2 {private Long id;private String name;private Integer age;private String email;private Long managerId;private LocalDateTime createTime;private LocalDateTime updateTime;private Integer version;private Integer deleted; }以及Mapper接口 package com.example.mp.mappers; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.example.mp.po.User2; public interface User2Mapper extends BaseMapperUser2 { }逻辑删除 首先为什么要有逻辑删除呢直接删掉不行吗当然可以但日后若想要恢复或者需要查看这些数据就做不到了。逻辑删除是为了方便数据恢复和保护数据本身价值的一种方案。 日常中我们在电脑中删除一个文件后也仅仅是把该文件放入了回收站日后若有需要还能进行查看或恢复。当我们确定不再需要某个文件可以将其从回收站中彻底删除。这也是类似的道理。 mp提供的逻辑删除实现起来非常简单 只需要在application.yml中进行逻辑删除的相关配置即可 mybatis-plus:global-config:db-config:logic-delete-field: deleted # 全局逻辑删除的实体字段名logic-delete-value: 1 # 逻辑已删除值(默认为1)logic-not-delete-value: 0 # 逻辑未删除值(默认为0)# 若逻辑已删除和未删除的值和默认值一样则可以不配置这2项测试代码 package com.example.mp; import com.example.mp.mappers.User2Mapper; import com.example.mp.po.User2; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.List; RunWith(SpringRunner.class) SpringBootTest public class LogicDeleteTest {Autowiredprivate User2Mapper mapper;Testpublic void testLogicDel() {int i mapper.deleteById(6);System.out.println(rowAffected i);} }可以看到发出的SQL不再是DELETE而是UPDATE 此时我们再执行一次SELECT Test public void testSelect() {ListUser2 users mapper.selectList(null); }可以看到发出的SQL语句会自动在WHERE后面拼接逻辑未删除的条件。查询出来的结果中没有了id为6的王软蛋。 若想要SELECT的列不包括逻辑删除的那一列则可以在实体类中通过TableField进行配置 TableField(select false) private Integer deleted;可以看到下图的执行结果中SELECT中已经不包含deleted这一列了 前面在application.yml中做的配置是全局的。通常来说对于多个表我们也会统一逻辑删除字段的名称统一逻辑已删除和未删除的值所以全局配置即可。当然若要对某些表进行单独配置在实体类的对应字段上使用TableLogic即可 TableLogic(value 0, delval 1) private Integer deleted;小结 开启mp的逻辑删除后会对SQL产生如下的影响 INSERT语句没有影响 SELECT语句追加WHERE条件过滤掉已删除的数据 UPDATE语句追加WHERE条件防止更新到已删除的数据 DELETE语句转变为UPDATE语句**注意上述的影响只针对mp自动注入的SQL生效。**如果是自己手动添加的自定义SQL则不会生效。比如 public interface User2Mapper extends BaseMapperUser2 {Select(select * from user2)ListUser2 selectRaw(); }调用这个selectRaw则mp的逻辑删除不会生效。 另逻辑删除可在application.yml中进行全局配置也可在实体类中用TableLogic进行局部配置。 自动填充 表中常常会有“新增时间”“修改时间”“操作人” 等字段。比较原始的方式是每次插入或更新时手动进行设置。mp可以通过配置对某些字段进行自动填充食用示例如下 1、在实体类中的某些字段上通过TableField设置自动填充 public class User2 {private Long id;private String name;private Integer age;private String email;private Long managerId;TableField(fill FieldFill.INSERT) // 插入时自动填充private LocalDateTime createTime;TableField(fill FieldFill.UPDATE) // 更新时自动填充private LocalDateTime updateTime;private Integer version;private Integer deleted; }2、实现自动填充处理器 package com.example.mp.component; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import org.apache.ibatis.reflection.MetaObject; import org.springframework.stereotype.Component; import java.time.LocalDateTime;Component //需要注册到Spring容器中 public class MyMetaObjectHandler implements MetaObjectHandler {Overridepublic void insertFill(MetaObject metaObject) {// 插入时自动填充// 注意第二个参数要填写实体类中的字段名称而不是表的列名称strictFillStrategy(metaObject, createTime, LocalDateTime::now);}Overridepublic void updateFill(MetaObject metaObject) {// 更新时自动填充strictFillStrategy(metaObject, updateTime, LocalDateTime::now);} }测试 Test public void test() {User2 user new User2();user.setId(8L);user.setName(王一蛋);user.setAge(29);user.setEmail(ydbaomidou.com);user.setManagerId(2L);mapper.insert(user); }到表里可以看到对createTime进行了自动填充 注意自动填充仅在该字段为空时会生效若该字段不为空则直接使用已有的值。如下 Test public void test() {User2 user new User2();user.setId(8L);user.setName(王一蛋);user.setAge(29);user.setEmail(ydbaomidou.com);user.setManagerId(2L);user.setCreateTime(LocalDateTime.of(2000,1,1,8,0,0));mapper.insert(user); }更新时的自动填充测试如下 Test public void test() {User2 user new User2();user.setId(8L);user.setName(王一蛋);user.setAge(99);mapper.updateById(user); }乐观锁插件 当出现并发操作时需要确保各个用户对数据的操作不产生冲突此时需要一种并发控制手段。悲观锁的方法是在对数据库的一条记录进行修改时先直接加锁数据库的锁机制锁定这条数据然后再进行操作而乐观锁正如其名它先假设不存在冲突情况而在实际进行数据操作时再检查是否冲突。乐观锁的一种通常实现是版本号在MySQL中也有名为MVCC的基于版本号的并发事务控制。 在读多写少的场景下乐观锁比较适用能够减少加锁操作导致的性能开销提高系统吞吐量。 在写多读少的场景下悲观锁比较使用否则会因为乐观锁不断失败重试反而导致性能下降。 乐观锁的实现如下 取出记录时获取当前version 更新时带上这个version 执行更新时 set version newVersion where version oldVersion 如果oldVersion与数据库中的version不一致就更新失败这种思想和CASCompare And Swap非常相似。 乐观锁的实现步骤如下 1、配置乐观锁插件 package com.example.mp.config;import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;Configuration public class MybatisPlusConfig {/** 3.4.0以后的mp版本推荐用如下的配置方式 **/Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());return interceptor;}/** 旧版mp可以采用如下方式。注意新旧版本中新版的类名称带有Inner, 旧版的不带, 不要配错了 **//*Beanpublic OptimisticLockerInterceptor opLocker() {return new OptimisticLockerInterceptor();}*/ }2、在实体类中表示版本的字段上添加注解Version Data public class User2 {private Long id;private String name;private Integer age;private String email;private Long managerId;private LocalDateTime createTime;private LocalDateTime updateTime;Versionprivate Integer version;private Integer deleted; } 测试代码 Test public void testOpLocker() {int version 1; // 假设这个version是先前查询时获得的User2 user new User2();user.setId(8L);user.setEmail(versionbaomidou.com);user.setVersion(version);int i mapper.updateById(user); }当UPDATE返回了1表示影响行数为1则更新成功。反之由于WHERE后面的version与数据库中的不一致匹配不到任何记录则影响行数为0表示更新失败。更新成功后新的version会被封装回实体对象中。 实体类中version字段类型只支持intlongDateTimestampLocalDateTime 注意乐观锁插件仅支持updateById(id)与update(entity, wrapper)方法 注意如果使用wrapper则wrapper不能复用示例如下 Test public void testOpLocker() {User2 user new User2();user.setId(8L);user.setVersion(1);user.setAge(2);// 第一次使用LambdaQueryWrapperUser2 wrapper new LambdaQueryWrapper();wrapper.eq(User2::getName, 王一蛋);mapper.update(user, wrapper);// 第二次复用user.setAge(3);mapper.update(user, wrapper); }可以看到在第二次复用wrapper时拼接出的SQL中后面WHERE语句中出现了2次version是有问题的。 性能分析插件 该插件会输出SQL语句的执行时间以便做SQL语句的性能分析和调优。 注3.2.0版本之后mp自带的性能分析插件被官方移除了而推荐食用第三方性能分析插件 食用步骤 1、引入maven依赖 dependencygroupIdp6spy/groupIdartifactIdp6spy/artifactIdversion3.9.1/version /dependency2、修改application.yml spring:datasource:driver-class-name: com.p6spy.engine.spy.P6SpyDriver #换成p6spy的驱动url: jdbc:p6spy:mysql://localhost:3306/yogurt?serverTimezoneAsia/Shanghai #url修改username: rootpassword: root3、在src/main/resources资源目录下添加spy.properties #spy.properties #3.2.1以上使用 modulelistcom.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory # 真实JDBC driver , 多个以逗号分割,默认为空。由于上面设置了modulelist, 这里可以不用设置driverlist #driverlistcom.mysql.cj.jdbc.Driver # 自定义日志打印 logMessageFormatcom.baomidou.mybatisplus.extension.p6spy.P6SpyLogger #日志输出到控制台 appendercom.baomidou.mybatisplus.extension.p6spy.StdoutLogger #若要日志输出到文件, 把上面的appnder注释掉, 或者采用下面的appender, 再添加logfile配置 #不配置appender时, 默认是往文件进行输出的 #appendercom.p6spy.engine.spy.appender.FileLogger #logfilelog.log # 设置 p6spy driver 代理 deregisterdriverstrue # 取消JDBC URL前缀 useprefixtrue # 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset. excludecategoriesinfo,debug,result,commit,resultset # 日期格式 dateformatyyyy-MM-dd HH:mm:ss # 是否开启慢SQL记录 outagedetectiontrue # 慢SQL记录标准 2 秒 outagedetectioninterval2 # 执行时间设置, 只有超过这个执行时间的才进行记录, 默认值0, 单位毫秒 executionThreshold10多租户SQL解析器 多租户的概念多个用户共用一套系统但他们的数据有需要相对的独立保持一定的隔离性。 多租户的数据隔离一般有如下的方式 不同租户使用不同的数据库服务器优点是不同租户有不同的独立数据库有助于扩展以及对不同租户提供更好的个性化出现故障时恢复数据较为简单。缺点是增加了数据库数量购置成本维护成本更高不同租户使用相同的数据库服务器但使用不同的数据库不同的schema优点是购置和维护成本低了一些缺点是数据恢复较为困难因为不同租户的数据都放在了一起不同租户使用相同的数据库服务器使用相同的数据库共享数据表在表中增加租户id来做区分优点是购置和维护成本最低支持用户最多缺点是隔离性最低安全性最低添加多租户拦截器配置。添加配置后在执行CRUD的时候会自动在SQL语句最后拼接租户id的条件 package com.example.mp.config;import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler; import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor; import net.sf.jsqlparser.expression.Expression; import net.sf.jsqlparser.expression.LongValue; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;Configuration public class MybatisPlusConfig {Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantLineHandler() {Overridepublic Expression getTenantId() {// 返回租户id的值, 这里固定写死为1// 一般是从当前上下文中取出一个 租户idreturn new LongValue(1);}/**** 通常会将表示租户id的列名需要排除租户id的表等信息封装到一个配置类中如TenantConfig**/Overridepublic String getTenantIdColumn() {// 返回表中的表示租户id的列名return manager_id;}Overridepublic boolean ignoreTable(String tableName) {// 表名不为 user2 的表, 不拼接多租户条件return !user2.equals(tableName);}}));// 如果用了分页插件注意先 add TenantLineInnerInterceptor 再 add PaginationInnerInterceptor// 用了分页插件必须设置 MybatisConfiguration#useDeprecatedExecutor falsereturn interceptor;}}测试代码 Test public void testTenant() {LambdaQueryWrapperUser2 wrapper new LambdaQueryWrapper();wrapper.likeRight(User2::getName, 王).select(User2::getName, User2::getAge, User2::getEmail, User2::getManagerId);user2Mapper.selectList(wrapper); }动态表名SQL解析器 当数据量特别大的时候我们通常会采用分库分表。这时可能就会有多张表其表结构相同但表名不同。例如order_1order_2order_3查询时我们可能需要动态设置要查的表名。mp提供了动态表名SQL解析器食用示例如下 先在mysql中拷贝一下user2表 配置动态表名拦截器 package com.example.mp.config;import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.handler.TableNameHandler; import com.baomidou.mybatisplus.extension.plugins.inner.DynamicTableNameInnerInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.HashMap; import java.util.Random;Configuration public class MybatisPlusConfig {Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor new MybatisPlusInterceptor();DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor new DynamicTableNameInnerInterceptor();HashMapString, TableNameHandler map new HashMap();// 对于user2表进行动态表名设置map.put(user2, (sql, tableName) - {String _ _;int random new Random().nextInt(2) 1;return tableName _ random; // 若返回null, 则不会进行动态表名替换, 还是会使用user2});dynamicTableNameInnerInterceptor.setTableNameHandlerMap(map);interceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor);return interceptor;}} 测试 Test public void testDynamicTable() {user2Mapper.selectList(null); }总结 条件构造器AbstractWrapper中提供了多个方法用于构造SQL语句中的WHERE条件而其子类QueryWrapper额外提供了select方法可以只选取特定的列子类UpdateWrapper额外提供了set方法用于设置SQL中的SET语句。除了普通的Wrapper还有基于lambda表达式的Wrapper如LambdaQueryWrapperLambdaUpdateWrapper它们在构造WHERE条件时直接以方法引用来指定WHERE条件中的列比普通Wrapper通过字符串来指定要更加优雅。另还有链式Wrapper如LambdaQueryChainWrapper它封装了BaseMapper可以更方便地获取结果。条件构造器采用链式调用来拼接多个条件条件之间默认以AND连接当AND或OR后面的条件需要被括号包裹时将括号中的条件以lambda表达式形式作为参数传入and()或or()特别的当()需要放在WHERE语句的最开头时可以使用nested()方法条件表达式时当需要传入自定义的SQL语句或者需要调用数据库函数时可用apply()方法进行SQL拼接条件构造器中的各个方法可以通过一个boolean类型的变量condition来根据需要灵活拼接WHERE条件仅当condition为true时会拼接SQL语句使用lambda条件构造器可以通过lambda表达式直接使用实体类中的属性进行条件构造比普通的条件构造器更加优雅若mp提供的方法不够用可以通过自定义SQL原生mybatis的形式进行扩展开发使用mp进行分页查询时需要创建一个分页拦截器Interceptor注册到Spring容器中随后查询时通过传入一个分页对象Page对象进行查询即可。单表查询时可以使用BaseMapper提供的selectPage或selectMapsPage方法。复杂场景下如多表联查使用自定义SQL。AR模式可以直接通过操作实体类来操作数据库。让实体类继承自Model即可
http://www.w-s-a.com/news/609104/

相关文章:

  • j2ee网站开发搜索推广的流程
  • 网站目录结构图虚拟主机如何安装WordPress
  • 信产部网站备案保定软件开发网站制作
  • 东莞网站设计定做东莞网站建设最牛
  • 网站开发的软件天猫的网站导航怎么做的
  • 做链接哪个网站好网站建设平台方案设计
  • 资质升级业绩备案在哪个网站做网站建设方案费用预算
  • 做网站找哪个平台好wordpress 3.9 性能
  • 大兴模版网站建设公司企业网站备案案例
  • h5建站是什么wordpress客户端 接口
  • 济南自适应网站建设制作软件下载
  • 望都网站建设抖音广告投放收费标准
  • 网站制作软件排行榜上海市网站建设公司58
  • 什么是网站风格中国工商网企业查询官网
  • 专业建设专题网站wordpress lnmp wamp
  • 环保网站 下载页网站
  • 开源小程序模板江门关键词优化排名
  • 网站开发 知乎房地产型网站建设
  • 买完域名网站怎么设计wordpress 纯代码
  • 公司网站怎么做百度竞价宁波网络公司哪家好
  • 河西网站建设制作微信分销系统多层
  • 网站制作完成后应进入什么阶段石家庄网站建设找哪家好
  • 南通外贸网站推广自在源码网官网
  • 个人网站模板html下载餐饮vi设计案例欣赏
  • 高端网站建设wanghess网站开发售后服务承诺
  • 江西网站建设费用企业网站推广的方法有( )
  • 中国十大网站开发公司企业网站建设的要素有哪些
  • 网站防站做网站吉林
  • 嘉定区网站建设公司企业信息公示查询系统官网
  • 一个具体网站的seo优化产品介绍网站模板下载地址