Mybatis-plus笔记整理
导入依赖
<!--MP--> <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.1</version> </dependency> <!-- 代码生成器--> <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.4.1</version> </dependency> <!-- 代码生成器模板引擎--> <dependency><groupId>org.apache.velocity</groupId><artifactId>velocity-engine-core</artifactId><version>2.3</version> </dependency>
雪花算法
数据库的扩展方式主要包括:业务分库,主从复制,数据库分表
数据库分表
如果业务持续发展,同一业务的单表数据也会达到数据库服务器的处理瓶颈;例如淘宝几亿用户数据,因此需要对单表数据进行拆分。
垂直分表
列也比较多,查询量比较大的时候,通常把经常被查询的字段 和 数据量比较大的字段,拆分到不同的表中,比如age,sex 主要是查询使用,nickname昵称字段和描述字段主要用于展示使用而且本身还比较长,可以将后面两个字段独立到另一个表中去,这样查询的age 和 sex时 能带来一定的性能提升,相当于对表垂直切了一刀,把非主要查询的这两个字段独立出去。也就是把大字段或一般查询用不到的字段分离到另外一张表中。user user_info 用 id进行关联,user id字段自增长,user_info id字段不设策略,user的id是啥 我就是啥
主键一对一关联;
水平分表
适合表行数特别大的表,数据量特别大的时候,也没有明确查询字段之类的需求,关键还要看表的访问性能,一些比较复杂的表,可能超过1000万就要分表了,按数据分表,多少万条数据在一个表上,多少行数据在一个表上,相当于横着切一刀 分出去。水平分表相比垂直分表会引入更多的复杂性。
复杂性体现:主键自增问题 如果分2张表,按照主键范围存在不同的表上,比如1~1千万,1千万零一~2千万,分别在两张表上,如果满了 扩展容易,再加即可,实现了动态扩展,各表内数据量有多有少,现在一般都是负载均衡,会导致服务器访问不均衡。如果解决均衡问题,可以将表的主键起始id分表设为1、 2、 3 步长为3,这样就均衡了,但是扩展又会出问题,如果扩展又会出现数据迁移问题,重新分配布局。两种方式扩展都可能会遇到之前的表里面有数据被删除,然后后面的数据主键自增已经占用了新的主键,扩展的id并不是理论上应该存的id,而是之前的表可能已经用掉了。会不安全
解决办法:
Hash:取模运算,放到哪一个表中,要求初始表数量确定,表数量太多维护比较麻烦,太少又可能导致单表性能出现问题,表分布会比较均匀,但是扩展会很麻烦,所有数据都要重分布,uuid不能和mysql的聚簇索引一起用,不利于mysql查询优化
雪花算法snow flake(分布式id占19个符号位):mybatis-plus中默认的主键策略就是雪花算法
分布式主键生成算法,它能够保证不同表的主键不重复性,以及相同表的主键的有序性
核心思想:
长度64bit一个Long型,分四个部分:
1.第一个位置,符号位,为0,表示为正数,一般都不会用负数
2.第二个位置,时间戳位,41bit,存储的是差值,约定于69.73年,这样生成的时间都是不一样的
3.第三个位置,5bit是数据中心(相当于机房代码),5bit机器ID(表示数据中心存储数据的机器,可以部署在1024个节点,相当于1024台机器),
4.第四个位置,12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生4096个ID),支持并发量很大
mybatis-plus 默认识别 名字叫“id”的字段,自动进行雪花算法插入数据库id,如果数据库是uid实体类也是uid字段, 或者其他则识别不了,无法进行默认的雪花算法插入主键到数据库,会报错。所以此时需要在实体类的字段上加@TableId 告诉mybatis-plus这个字段是id 字段(数据库中是uid);
@TableName(value="t_user") public class User {//默认雪花算法 也可以设置// @TableId(type= IdType.ASSIGN_ID) private String id; 也可以@TableIdprivate Long uid;
或者,数据库名字叫uid,实体类叫做id,也无法完成映射,所以需要加value属性,如同@TableName注解
@TableName(value="t_user") public class User {//@TableId(value = "uid",type = IdType.AUTO) 主键自增策略@TableId(value = "uid")private Long id;private String name;private int age;private String email;xxxxxxxxxx @TableId(value = "uid")private Long id;private String name;private int age;private String email;
业务中id为空,数据库中也没有设置id;id是主键,不能为空,所以在插入的时候就会报错。如果所有的表都要求主键自增,可以在配置文件中设置
#设置全局主键自增 mybatis-plus.global-config.db-config.id-type=auto
新增两个字段 ,如果类中的列名为驼峰命名
private LocalDateTime createTime; private LocalDateTime updateTime; //在数据库中字段为:create_time update_time mybatis-plus 会自动把驼峰进行转换
如果数据库中的字段名跟实体类不一致,需要用@TableField(value="")
@TableId(value = "uid",type = IdType.AUTO) private Long id; @TableField(value = "username") private String name; private int age; private String email;
数据库字段可进行自动填充,创建和更新时间 可以用CURRENT_TIMESTAMP 系统自动管理,更新字段,再勾上默认根据当前时间戳进行更新,这样管理就不需要每次业务里面涉及。也可以在业务层进行设计,自动填充功能,需要在实体类加注解来标识。
@TableField(fill = FieldFill.INSERT) private LocalDateTime createTime; @TableField(fill = FieldFill.INSERT_UPDATE) //插入或更新的时候自动填充 private LocalDateTime updateTime; //但是填充什么呢?需要定义实现自动填充功能 实现元对象处理器接口
@Slf4j @Component public class MyMeatObjectHandler implements MetaObjectHandler {@Overridepublic void insertFill(MetaObject metaObject) {log.info("insert自动填充....");//插入数据的时候 识别到注解 @TableField(fill = FieldFill.INSERT) 就会进入这个方法来执行this.strictInsertFill(metaObject,"createTime", LocalDateTime.class, LocalDateTime.now());this.strictUpdateFill(metaObject,"updateTime", LocalDateTime.class, LocalDateTime.now());}@Overridepublic void updateFill(MetaObject metaObject) {log.info("update自动填充。。。");//更新的时候this.strictUpdateFill(metaObject,"updateTime",LocalDateTime.class,LocalDateTime.now());} }
//判断当前对象自动填充属性是否包含 当前属性 boolean hasAuthor = metaObject.hasSetter("author"); //如果 有setter的方法 有这个author字段 进行自动填充author if(hasAuthor) {log.info("insert author属性");this.strictInsertFill(metaObject, "author", String.class, "石头"); }
年龄也自动填充
@TableField(fill = FieldFill.INSERT)private Integer age;
log.info("age 填充为18");//对age进行自动填充 当没传age的时候填充18进去 Object age = this.getFieldValByName("age", metaObject); //如果业务层赋值了就不用去填充,如果没有赋值去进行填充 if(age==null){this.strictInsertFill(metaObject, "age", Integer.class, 18);}
@TableLogic 逻辑删除
物理删除:真实删除
逻辑删除:假删除,将对应数据中代表是否被删除字段的状态修改为“被删除状态”,之后在数据库中仍旧能看到此条数据记录使用场景,可以进行数据恢复
数据库中此字段 一般默认类型tinyint类型,默认1为已删除,0未删除
在数据库中创建逻辑删除状态列 同时实体字段一般前面不要加is有些框架识别可能出问题
//private Integer deleted;//逻辑删除字段 0表示false 1表述true@TableLogic@TableField(value = "is_deleted")private Boolean deleted;
执行记录 需要数据库中此字段 值为0
Preparing: UPDATE t_user SET is_deleted=1 WHERE uid=? AND is_deleted=0 ==> Parameters: 1499759447969390597(Long)<== Updates: 0
//自定义 1为未删除 -1为已删除 需要去配置 @TableLogic @TableField(value = "is_deleted") private Integer deleted;
mybatis-plus.global-config.db-config.logic-delete-field=deleted mybatis-plus.global-config.db-config.logic-delete-value=-1 mybatis-plus.global-config.db-config.logic-not-delete-value=1
分页插件
1.添加配置类 config包 可以将配置全部写在这个包内
spring
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean"><!-- 其他属性 略 --><property name="configuration" ref="configuration"/><property name="plugins"><array><ref bean="mybatisPlusInterceptor"/></array></property> </bean> <bean id="configuration" class="com.baomidou.mybatisplus.core.MybatisConfiguration"><!-- 需配置该值为false,避免1或2级缓存可能出现问题,该属性会在旧插件移除后一同移除 --><property name="useDeprecatedExecutor" value="false"/> </bean> <bean id="mybatisPlusInterceptor" class="com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor"><property name="interceptors"><list><ref bean="paginationInnerInterceptor"/></list></property> </bean> <bean id="paginationInnerInterceptor" class="com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor"><!-- 对于单一数据库类型来说,都建议配置该值,避免每次分页都去抓取数据库类型 --><constructor-arg name="dbType" value="H2"/> </bean>
spring-boot
//可将包扫描写在这个类里面 也可以写在启动类上,对持久层的扫描
@Configuration//表示这是一个配置类,应用程序启动的时候会被自动读取并以配置的形式读取
@MapperScan("mybatisplus.mapper")//可以将持久层所有的配置集中在这里管理 全写在这个配置类中
public class MybatisPConfig {/*** 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false* 避免缓存出现问题(该属性会在旧插件移除后一同移除)*///所有的插件都是以拦截器的形式存在@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {//先创建拦截器(插件)管理器MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();//添加分页插件 将分页(这里是mysql)添加到一个内置的拦截器管理器中( 将分页插件的拦截器对象创建出来,然后用此方法配置到 这个管理器之中)interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));//添加乐观锁插件interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());//最后将这个interceptor对象,作为一个bean对象返回return interceptor;}// @Bean
// public ConfigurationCustomizer configurationCustomizer() {
// return configuration -> configuration.setUseDeprecatedExecutor(false);
// }
}
针对mysql的分页配置类 写好了。进行测试
@Slf4j
@SpringBootTest
public class IntercepterTest {@Resourceprivate UserMapper userMapper;@Resourceprivate ProductMapper productMapper;@Testpublic void test1(){//新建分页参数对象 查询第几页,每页5条记录Page<User> userPage = new Page<>(2, 5);//这两个对象是一个对象 参数为页对象 及qureyMapper条件构造器 先传null//Page<User> userPage1 = userMapper.selectPage(userPage, null);//System.out.println("确认是否是一个对象"+(userPage==userPage1)); true//优化为userMapper.selectPage(userPage, null);//当前页码下的所有记录List<User> users = userPage.getRecords();users.forEach(System.out::println);//总数long total = userPage.getTotal();System.out.println("总数+"+total);//有没有下一页boolean bn = userPage.hasNext();System.out.println("下一页??"+bn);//有没有上一页boolean bp = userPage.hasPrevious();System.out.println("上一页?"+bp);}
在xml中如何使用配置分页?
//@Repository public interface UserMapper extends BaseMapper<User> {//自定义一个方法 具体的xml实现在resources下面的mapper文件中//扩展mapperList<User> selectAllByName(String name);//第一个参数为分页对象 第二个为查询条件 根据年龄来查询用户 并分页展示IPage<User> selectPageVo(IPage<?> page, Integer age);
<select id="selectPageVo" resultType="mybatisplus.entity.User">select <include refid="Base_Colum_List"/>from t_user where age > #{age}</select> </mapper>
xml中不需要去进行 分页,只需要写条件即可,我们传递了page对象进去,MP会自动的去读取参数中的配置对象,且会作为整个查询语句的后缀,然后执行sql
@Test public void selectByAge(){Page<User> userPage = new Page<>(2,6);IPage<User> userIPage = userMapp er.selectPageVo(userPage, 18);List<User> users = userIPage.getRecords();users.forEach(System.out::println); }
执行语句为:自动追加
Total: 1==> Preparing: select uid,name,age,email from t_user where age > ? LIMIT ?,?==> Parameters: 18(Integer), 6(Long), 6(Long)<== Columns: uid, name, age, email<== Row: 1500133241527472131, 建国2, 22, jiangu2o@qq.com<== Row: 1500133241527472134, 建国更新, 100, jiangu2o@qq.com<== Row: 1500133241527472139, 观海200, 200, jianguo5@qq.com
但是实体类里面是 id 所以这里自定义的方法 的xml 需要改
<mapper namespace="mybatisplus.mapper.UserMapper"> <!--sql片段--><sql id="Base_Colum_List">uid as id,name,age,email,is_deleted as deleted,create_time as createTime,update_time as updateTime</sql><select id="selectAllByName" resultType="mybatisplus.entity.User">select <include refid="Base_Colum_List"/>from t_userwhere name = #{name}</select><select id="selectPageVo" resultType="mybatisplus.entity.User">select <include refid="Base_Colum_List"/>from t_user where age > #{age}</select>
==> Preparing: select uid as id,name,age,email,is_deleted as deleted,create_time as createTime,update_time as updateTime from t_user where age > ? LIMIT ?,?==> Parameters: 18(Integer), 6(Long), 6(Long)<== Columns: id, name, age, email, deleted, createTime, updateTime<== Row: 1500133241527472131, 建国2, 22, jiangu2o@qq.com, 0, 2022-03-06 14:38:17, 2022-03-06 14:38:17Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@4e1459ea]User(id=1500133241527472131, name=建国2, age=22, email=jiangu2o@qq.com, createTime=2022-03-06T14:38:17, updateTime=2022-03-06T14:38:17, deleted=false)
MP的乐观锁应用
A和B都要对数据进行修改操作,但是取出的数据都是未更新过的数据,同时修改,存入就会存在数据覆盖的情况,出现错误数据。比如两个人同时修改某一商品的价格。一个增加一个减少,造成并发冲突,并发也不高但是造成数据不一致问题。可以通过乐观锁来解决
SELECT id,name,version FROM product WHERE id=1;
查询的时候就需要将 控制版本的version取出来 更新时,对version进行控制,每次有人修改的时候都需要提前判断,符合条件才能进行修改。每修改一次 版本号会加一
//告诉mp这 就是版本号@Versionprivate Integer version; @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();//添加分页插件interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));//添加乐观锁插件interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());return interceptor; }
Preparing: SELECT id,name,price,version FROM product WHERE id=?==> Parameters: 1(Long)<== Columns: id, name, price, version<== Row: 1, 笔记本, 150, 3
UPDATE product SET name=?, price=?, version=? WHERE id=? AND version=?==> Parameters: 笔记本(String), 200(Integer), 4(Integer), 1(Long), 3(Integer)
修改后 将版本号为3的记录 版本号更改为4 那么同时另外一条修改语句 则无法修改
=> Preparing: UPDATE product SET name=?, price=?, version=? WHERE id=? AND version=?==> Parameters: 笔记本(String), 120(Integer), 4(Integer), 1(Long), 3(Integer)<== Updates: 0
Wrapper条件构造
Wrapper → AbstractWrapper
AbstractLambdaWrapper(抽象类) → LambdaUpdateWrapper 和 LambdaQueryWrapper
UpdateWrapper QueryWrapper
/*** 查询名字中包含a* 年龄大于10且小于20 email不为空的用户*/ @Test public void test(){QueryWrapper<User> queryWrapper = new QueryWrapper<User>();//column对应数据库表的列名 而不是属性名queryWrapper.like("name","a");//左侧为%queryWrapper.likeLeft("name","k");//gt为> ge为≥ lt为< le为≤queryWrapper.gt("age",10).lt("age",20).isNotNull("email");List<User> users = userMapper.selectList(queryWrapper);users.forEach(System.out::println);//between 大于等于 小于等于queryWrapper.between("age",0,20);
Preparing: SELECT uid AS id,name,age,email,create_time,update_time,is_deleted AS deleted FROM t_user WHERE is_deleted=0 AND (name LIKE ? AND name LIKE ? AND age > ? AND age < ? AND email IS NOT NULL)==> Parameters: %a%(String), %k(String), 10(Integer), 20(Integer)<== Columns: id, name, age, email, create_time, update_time, deleted<== Row: 1499409859102179330, jack, 18, jianguo@qq.com, null, null, 0
case函数
按照姓名 分国籍 操作 SELECT( CASE NAME WHEN 'jack' THEN '美国' WHEN '花花' THEN '日本' WHEN '建国' THEN '中国' ELSE '韩国' END ) country ,sum(age) 总年龄 FROM`t_user` GROUP BY(CASE NAME WHEN 'jack' THEN '美国' WHEN '花花' THEN '日本' WHEN '建国' THEN '中国' ELSE '韩国' END);
/*** 按年龄降序查询用户,如果年龄相同则按id升序排序*/ @Test public void test2() {QueryWrapper<User> queryWrapper = new QueryWrapper<>();//组装排序条件queryWrapper.orderByDesc("age").orderByAsc("uid");List<User> list = userMapper.selectList(queryWrapper);list.forEach(System.out::println); }
Preparing: SELECT uid AS id,name,age,email,create_time,update_time,is_deleted AS deleted FROM t_user WHERE is_deleted=0 ORDER BY age DESC,uid ASC==> Parameters: <== Columns: id, name, age, email, create_time, update_time, deleted
/*** 删除email为空的用户*/ @Test public void test3(){QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.isNull("email");int res = userMapper.delete(queryWrapper);System.out.println("删除的记录数:"+res);//之前配置了逻辑删除,所以只是把is_deleted更新为1 }
Preparing: UPDATE t_user SET is_deleted=1 WHERE is_deleted=0 AND (email IS NULL)
/*** 查询名字中包含花 且(年龄小于12或email为空的用户),并将这些用户的年龄设置为18,设置为user@haha.com*/ @Test public void test4(){QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.like("name","花").and(i->i.lt("age",18).or().isNull("email"));User user = new User();user.setAge(18);user.setEmail("user@haha.com");int res = userMapper.update(user, queryWrapper);System.out.println("更新的条数为:"+res);
Preparing: UPDATE t_user SET age=?, email=?, update_time=? WHERE is_deleted=0 AND (name LIKE ? AND (age < ? OR email IS NULL))==> Parameters: 18(Integer), user@haha.com(String), 2022-03-14T23:07:38.807(LocalDateTime), %花%(String), 18(Integer)<== Updates: 5
查询所有用户的用户名和年龄 其他的不要
//查询用户名和年龄 其他的不要 @Test public void test5(){QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.select("name","age");//其他属性 不会返回 返回的是map泛型//select语句通常会和selectMaps一起出现List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);maps.forEach(System.out::println); }
==> Preparing: SELECT name,age FROM t_user WHERE is_deleted=0==> Parameters: <== Columns: name, age<== Row: Jack, 20<== Row: 正阳, 20<== Row: Billie, 24
/*** 使用子查询* 查询id不大于3的所有用户的id列表*/ @Test public void test6(){QueryWrapper<User> queryWrapper = new QueryWrapper<>();//如果子查询由用户输入 容易sql注入 queryWrapper.inSql("uid","select uid from t_user where uid<=3");// 可以这么写 queryWrapper.le("uid",3);List<User> users = userMapper.selectList(queryWrapper);users.forEach(System.out::println); }
SELECT uid AS id,name,age,email,create_time,update_time,is_deleted AS deleted FROM t_user WHERE is_deleted=0 AND (uid IN (select uid from t_user where uid<=3))==> Parameters: <== Columns: id, name, age, email, create_time, update_time, deleted<== Row: 2, Jack, 20, test2@baomidou.com, null, null, 0<== Total: 1
UpdateWrapper
/*** 查询名字中包含花 且(年龄小于等于18或email不为空的用户),并将这些用户的年龄设置为16,设置为user@updateWrapper.com*/@Testpublic void test7(){UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();updateWrapper.set("age",16).set("email","user@updateWrapper.com").like("name","花").and(i-> i.le("age",18).or().isNotNull("email"));//如果有自动填充功能,必须要把user对象传进去,否则 无法实现,比如updateTime//不需要 可以传nullUser user = new User();int res = userMapper.update(user, updateWrapper);System.out.println("更新的条数为:"+res);}
Preparing: UPDATE t_user SET age=?,email=? WHERE is_deleted=0 AND (name LIKE ? AND (age <= ? OR email IS NOT NULL))==> Parameters: 16(Integer), user@updateWrapper.com(String), %花%(String), 18(Integer)<== Updates: 5
==> Preparing: UPDATE t_user SET update_time=?, age=?,email=? WHERE is_deleted=0 AND (name LIKE ? AND (age <= ? OR email IS NOT NULL))==> Parameters: 2022-03-14T23:33:21.205(LocalDateTime), 16(Integer), user@updateWrapper.com(String), %花%(String), 18(Integer)<== Updates: 5
condition 动态组装查询条件
/*** 查询名字中含有 n,年龄大于10且小于20的用户,查询条件来源于用户输入,是可选择的*/ @Test public void test8() {String username = "花";Integer ageBegin =10;Integer ageEnd = 20;QueryWrapper<User> queryWrapper = new QueryWrapper<>();if (StringUtils.isNotBlank(username)) {queryWrapper.like("name", username);}if(ageBegin!=null){queryWrapper.ge("age",ageBegin);}if(ageEnd !=null) {queryWrapper.le("age", ageEnd);}List<User> users = userMapper.selectList(queryWrapper);users.forEach(System.out::println); } //利用重载的方法@Testpublic void test8() {String username = "花";Integer ageBegin = null;Integer ageEnd = 20;QueryWrapper<User> queryWrapper = new QueryWrapper<>();queryWrapper.like(StringUtils.isNotBlank(username), "name", username).ge(ageBegin != null, "age", ageBegin).le(ageEnd != null, "age", ageEnd);List<User> users = userMapper.selectList(queryWrapper);users.forEach(System.out::println); }
Preparing: SELECT uid AS id,name,age,email,create_time,update_time,is_deleted AS deleted FROM t_user WHERE is_deleted=0 AND (name LIKE ? AND age >= ? AND age <= ?)==> Parameters: %花%(String), 10(Integer), 20(Integer)<== Columns: id, name, age, email, create_time, update_time, deleted<== Row: 1499759447893893121, 花花0, 16, user@updateWrapper.com, null, 2022-03-14 23:33:21, 0
SELECT uid AS id,name,age,email,create_time,update_time,is_deleted AS deleted FROM t_user WHERE is_deleted=0 AND (name LIKE ? AND age <= ?)==> Parameters: %花%(String), 20(Integer)<== Columns: id, name, age, email, create_time, update_time, deleted<== Row: 1499759447893893121, 花花0, 16, user@updateWrapper.com, null, 2022-03-14 23:33:21, 0<== Row: 1499759447969390594, 花花1, 16, user@updateWrapper.com, null, 2022-03-14 23:33:21, 0<== Row: 1499759447969390595, 花花2, 16, user@updateWrapper.com, null, 2022-03-14 23:33:21, 0
lambdaQueryWrapper
/*** 查询名字中含有 n,年龄大于10且小于20的用户,查询条件来源于用户输入,是可选择的*/ @Test public void test9() {String username = "花";Integer ageBegin = null;Integer ageEnd = 20;LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();lambdaQueryWrapper.like(StringUtils.isNotBlank(username), User::getName, username).ge(ageBegin != null, User::getAge, ageBegin).le(ageEnd != null, User::getAge, ageEnd);List<User> users = userMapper.selectList(lambdaQueryWrapper);users.forEach(System.out::println); }
lambdaUpdateWrapper
/*** 查询名字中包含花 且(年龄小于等于18或email不为空的用户),并将这些用户的年龄设置为16,设置为user@updateWrapper.com*/ @Test public void test10() {LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();updateWrapper.set(User::getAge, 16).set(User::getEmail, "user@updateWrapper.com").like(User::getName, "花").and(i -> i.le(User::getAge, 18).or().isNotNull(User::getEmail));//如果有自动填充功能,必须要把user对象传进去,否则 无法实现,比如updateTime//不需要 可以传nullUser user = new User();int res = userMapper.update(user, updateWrapper);System.out.println("更新的条数为:" + res); }
其实就是把条件组装里的字符串形式 替换为Lambda替换,避免编译的时候没有发现的不必要的错误。
Gennerator代码生成器
public class CodeGenerator {@Testpublic void genCode(){// 代码生成器AutoGenerator mpg = new AutoGenerator(); // 全局配置GlobalConfig gc = new GlobalConfig();String projectPath = System.getProperty("user.dir");gc.setOutputDir(projectPath + "/src/main/java");gc.setAuthor("eric");//去掉Service接口的首字母I 不加 接口名字前面会有I %s是占位符 实体名字gc.setServiceName("%sService");//生成后是否打开资源管理器gc.setOpen(false);//主键策略gc.setIdType(IdType.AUTO);//开启swagger2模式gc.setSwagger2(true);mpg.setGlobalConfig(gc); // 数据源配置DataSourceConfig dsc = new DataSourceConfig();dsc.setUrl("jdbc:mysql://localhost:3306/srb_core?serverTimezone=GMT%2B8&characterEncoding=utf8");// dsc.setSchemaName("public");dsc.setDriverName("com.mysql.cj.jdbc.Driver");dsc.setUsername("root");dsc.setPassword("root");dsc.setDbType(DbType.MYSQL);mpg.setDataSource(dsc); // 包配置PackageConfig pc = new PackageConfig(); // pc.setModuleName();pc.setParent("com.xie.srb.core");//此对象与数据库表结构一一对应,通过dao层向上传输数据源对象pc.setEntity("pojo.entity"); // pc.setService(); // pc.setXml();mpg.setPackageInfo(pc); // 策略配置StrategyConfig strategy = new StrategyConfig();//数据库表映射到实体的命名策略 数据库表名称 下划线变驼峰strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略 列名转驼峰strategy.setColumnNaming(NamingStrategy.underline_to_camel); // strategy.setSuperEntityClass("你自己的父类实体,没有就不用设置!");strategy.setEntityLombokModel(true);//逻辑删除字段strategy.setLogicDeleteFieldName("is_deleted");//去掉布尔值的is_前缀(确保tinyint(1) 实体类中最好不要用is做前缀 容易出问题 变成deleted 会加字段映射@TableFieldstrategy.setEntityBooleanColumnRemoveIsPrefix(true);//restful风格控制器 一般都会返回json 有@ResponseBody 会直接用@RestControllerstrategy.setRestControllerStyle(true);mpg.setStrategy(strategy);//执行mpg.execute(); } }
核心模块的yml配置
erver:port: 8110 spring:profiles:active: devapplication:# 以后要注册到微服务中 起一个 服务名name: service-coredatasource:type: com.zaxxer.hikari.HikariDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/srb_core?serverTimezone=GMT%2B8&characterEncoding=utf8username: rootpassword: root mybatis-plus:configuration:#日志输出log-impl: org.apache.ibatis.logging.stdout.StdOutImplmapper-locations: classpath:com/xie/srb/core/mapper/xml/*.xml
创建主类
package com.xie.srb.core; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.ComponentScan; @SpringBootApplication @ComponentScan("com.xie.srb") public class ServiceCoreApplication {public static void main(String[] args) {SpringApplication.run(ServiceCoreApplication.class,args);} }
配置MP分页
package com.xie.srb.core.config; /*** 配置分页*/ @Configuration @MapperScan("com.xie.srb.core.mapper") //事务处理 @EnableTransactionManagement public class MybatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor(){MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();//分页interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));return interceptor;} }
<build><resources><!-- xml放在java目录--><resource><directory>src/main/java</directory><includes><include>**/*.xml</include></includes></resource><!-- 指定资源的位置(xml放在resources下可以不用指定 --><resource><directory>src/main/java/com/xie/srb/core/mapper/xml</directory></resource></resources> </build>
Mybatis-plus笔记整理相关推荐
- mybatis笔记整理
前言: mybatis可以说是最容易上手的持久层框架了,相比于hibernate 而言,它都是直接用sql语句对数据库进行操作,而不是用hql,尤其是关联关系复杂的时候,mybatis更容易实现.下面 ...
- SpringBoot集成Mybatis用法笔记
今天给大家整理SpringBoot集成Mybatis用法笔记.希望对大家能有所帮助! 搭建一个SpringBoot基础项目. 具体可以参考SpringBoot:搭建第一个Web程序 引入相关依赖 &l ...
- 天猫整站SSM-分页-总结(做个人学习笔记整理用)
天猫整站SSM-分页-herf(做个人学习笔记整理用) 先写Page.java package com.how2java.tmall.util;public class Page {private i ...
- SpringBoot笔记整理(二)
SpringBoot笔记整理(一) SpringBoot笔记整理(二) SpringBoot笔记整理(三) SpringBoot笔记整理(四) Spring Boot与日志(日志框架.日志配置) 1. ...
- 动力节点—2020最新MyBatis教程笔记
文章目录 1 介绍 2 快速入门 2.1 操作步骤(P9) 2.1.1 首先创建maven 2.1.2 接着会做一些配置 2.2 在编译的target目录下面缺少xml的解决方式(P11) 2.3 日 ...
- 运维开发笔记整理-前后端分离
运维开发笔记整理-前后端分离 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.为什么要进行前后端分离 1>.pc, app, pad多端适应 2>.SPA开发式的流 ...
- 《繁凡的深度学习笔记》前言、目录大纲 一文让你完全弄懂深度学习所有基础(DL笔记整理系列)
<繁凡的深度学习笔记>前言.目录大纲 (DL笔记整理系列) 一文弄懂深度学习所有基础 ! 3043331995@qq.com https://fanfansann.blog.csdn.ne ...
- 一文让你完全弄懂逻辑回归和分类问题实战《繁凡的深度学习笔记》第 3 章 分类问题与信息论基础(上)(DL笔记整理系列)
好吧,只好拆分为上下两篇发布了>_< 终于肝出来了,今天就是除夕夜了,祝大家新快乐!^q^ <繁凡的深度学习笔记>第 3 章 分类问题与信息论基础 (上)(逻辑回归.Softm ...
- 一文让你完全弄懂回归问题、激活函数、梯度下降和神经元模型实战《繁凡的深度学习笔记》第 2 章 回归问题与神经元模型(DL笔记整理系列)
<繁凡的深度学习笔记>第 2 章 回归问题与神经元模型(DL笔记整理系列) 3043331995@qq.com https://fanfansann.blog.csdn.net/ http ...
- mybatis学习笔记(13)-延迟加载
2019独角兽企业重金招聘Python工程师标准>>> mybatis学习笔记(13)-延迟加载 标签: mybatis [TOC] resultMap可以实现高级映射(使用asso ...
最新文章
- 虚拟机批量安装LINUX,基于vmware workstation的 pxe + kickstart批量安装linux
- 日本共享自行车建立的强大地下停车系统
- HelloWorld讲解
- Node.js + React + MongoDB 实现 TodoList 单页应用
- 用css和js写表单验证,使用javascript及正则表达式实现表单验证(CSS,js练习)
- linux echo输出转义换行回车引号
- mysql——数据库设计中int与varchar中的长度含义
- [Python学习]错误篇二:切换当前工作目录时出错——FileNotFoundError: [WinError 3] 系统找不到指定的路径...
- 学习了Python大数据之后,将来主要做什么
- resources 下字体文件报错 simsun.ttc is not a valid TTF file
- 精选150道iOS面试题
- 现代信息检索——基本概念
- MySQL查看锁及事务隔离级别的命令
- 模拟斗地主发牌, 把54张牌发给三个玩家, 地主多三张, 对每个玩家的牌进行排序
- 29.递归三元表达式生成式匿名函数
- 阿里测试左移和开发赋能分享
- 什么是MES生产制造执行系统?实施MES生产管理系统有哪些目标?
- docker中部署piggymetrics微服务项目
- Buuctf—极客大挑战练习
- 详解广播域和冲突域的区别
热门文章
- Jquery实现全选和反选
- python 编程接口_Python 中的面向接口编程
- Android Framewrok 添加系统默认依赖库
- java 自动下载文件_java+selenium+new——无人化自动下载文件——基于firefox浏览器...
- 江西省中小学生学籍管理-跨省办理平台数字证书(2)
- ios 表情符号 键盘_iOS中表情键盘的完整实现方法详解
- 腾讯云数据库公有云市场稳居TOP 2!
- 中国公有云厂商2018年收入利润综合排名
- 如何进行智能工厂物流规划
- NTT DOCOMO在东京开始3G手机试运营