DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (`id` bigint(20) NULL DEFAULT NULL COMMENT '主键',`name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '姓名',`age` int(11) NULL DEFAULT NULL COMMENT '年龄',`email` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '邮箱',`manager_id` bigint(20) NULL DEFAULT NULL COMMENT '直属上级id',`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',`update_time` datetime(0) NULL DEFAULT NULL COMMENT '修改时间',`version` int(11) NULL DEFAULT 1 COMMENT '版本',`deleted` int(1) NULL DEFAULT 0 COMMENT '逻辑删除标识(0,未删除;1,已删除)'
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `user` VALUES (1234, '大boss', 40, 'boss@163.com', NULL, '2019-10-02 10:08:02', '2019-10-02 10:08:05', 1, 0);
INSERT INTO `user` VALUES (2345, '王天风', 25, 'wtf@163.com', 1234, '2019-10-02 10:09:07', '2019-10-02 10:09:10', 1, 0);
INSERT INTO `user` VALUES (2346, '李艺伟', 28, 'lyw@163.com', 2345, '2019-10-02 10:10:09', '2019-10-02 10:10:12', 1, 0);
INSERT INTO `user` VALUES (3456, '张雨绮', 31, 'zyq@163.com', 2345, '2019-10-02 10:10:54', '2019-10-02 10:10:58', 1, 0);
INSERT INTO `user` VALUES (4566, '刘雨红', 32, 'lyh@163.com', 2345, '2019-10-02 10:11:51', '2019-10-02 10:11:55', 1, 0);
SET FOREIGN_KEY_CHECKS = 1;

逻辑删除

设定逻辑删除规则

在配置文件中配置逻辑删除和逻辑未删除的值

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

在pojo类中在逻辑删除的字段加注解@TableLogic

@Data
@EqualsAndHashCode(callSuper = false)
public class User extends Model<User> {@TableId(type = IdType.AUTO)private Long id;@TableField(condition = SqlCondition.LIKE)private String name;private Integer age;private String email;private Long managerId;private LocalDateTime createTime;private LocalDateTime updateTime;private Integer version;@TableLogicprivate Integer deleted;
}

测试

通过id逻辑删除

@Test
public void deleteById(){userMapper.deleteById(4566L);
}

查询中排除删除标识字段及注意事项

逻辑删除字段只是为了标识数据是否被逻辑删除,在查询的时候,并不想也将该字段查询出来。

我们只需要在delete字段上增加@TableField(select = false)mybatisplus在查询的时候就会自动忽略该字段。

@Test
public void selectIgnoreDeleteTest(){userMapper.selectById(3456L);
}

其他测试

package com.dsf.mp.fakeDelete.dao;import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.dsf.mp.fakeDelete.FakeDeleteApp;
import com.dsf.mp.fakeDelete.entity.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.time.format.DateTimeFormatter;
import java.util.List;@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@RunWith(SpringRunner.class)
@SpringBootTest(classes = FakeDeleteApp.class)
public class UserMapperTest {@AutowiredUserMapper userMapper;@Testpublic void insert() {User user = new User();user.setName("刘明强");user.setAge(31);user.setManagerId(1088248166370832385L);user.setEmail("lmq@baomidou.com");int row = userMapper.insert(user);System.out.println("影响记录数:"+row);}@Testpublic void deleteById(){int rows = userMapper.deleteById(1171357729945505793L);System.out.println("影响行数:"+rows);}@Testpublic void selectById(){User user = userMapper.selectById(1171357729945505793L);System.out.println(user);}@Testpublic void updateById(){User user = new User();user.setAge(32);user.setId(1171357729945505793L);int rows = userMapper.updateById(user);System.out.println("影响行数:"+rows);}@Testpublic void mySelectList(){List<User> list = userMapper.mySelectList(Wrappers.<User>lambdaQuery()
//                .select(User.class,x->!x.getColumn().equals("deleted"))   //方式1.select(User.class,x->!x.isLogicDelete())   //方式2.gt(User::getAge, 25).eq(User::getDeleted,0));list.forEach(System.out::println);}
}
  • 自定义sql,MybatisPlus不会忽略deleted属性,需要我们手动忽略。

自动填充

MybaitsPlus在我们插入数据或者更新数据的时候,为我们提供了自动填充功能。类似MySQL提供的默认值一样。

如果我们需要使用自动填充功能,我们需要在实体类的相应属性上加@TableField注解,并指定什么时候进行自动填充。mybatisPlus为我们提供了三种填充时机,在FieldFill枚举中。

public enum FieldFill {/*** 默认不处理*/DEFAULT,/*** 插入时填充字段*/INSERT,/*** 更新时填充字段*/UPDATE,/*** 插入和更新时填充字段*/INSERT_UPDATE
}

设置好之后,我们还需要编写具体的填充规则,具体是编写一个填充类并交给Spring管理,然后实现MetaObjectHandler接口中的insertFillupdateFill方法。

Eg

  1. 插入User对象的时候自动填充插入时间,更新User对象的时候自动填充更新时间。
  • 指定实体类中需要自动填充的字段,并设置填充时机。
@Data
@EqualsAndHashCode(callSuper = false)
public class User extends Model<User> {...@TableField(fill = INSERT)private LocalDateTime createTime;@TableField(fill = UPDATE)private LocalDateTime updateTime;...
}
  • 编写填充规则
@Component
public class MyMetaObjHandler implements MetaObjectHandler {@Overridepublic void insertFill(MetaObject metaObject) {if(metaObject.hasSetter("createTime")){setInsertFieldValByName("createTime", LocalDateTime.now(),metaObject);}}@Overridepublic void updateFill(MetaObject metaObject) {if(metaObject.hasSetter("updateTime")){setUpdateFieldValByName("updateTime",LocalDateTime.now(),metaObject);}}
}

解释一下为什么要用if判断是否有对应的属性

mybatisPlus在执行插入或者更新操作的时候,每次都会执行该方法,有些表中是没有设置自动填充字段的,而且有些自动填充字段的值的获取比较消耗系统性能,所以为了不必要的消耗,进行if判断,决定是否需要填充。

有些时候我们已经设置了属性的值。不想让mybatisPlus再自动填充,也就是说我们没有设置属性的值,mybatisPlus进行填充,如果设置了那么就用我们设置的值。这种情况我们只需要在填充类中提前获取默认值,然后使用该默认值就可以了。

@Override
public void updateFill(MetaObject metaObject) {if(metaObject.hasSetter("updateTime")){Object updateTime = getFieldValByName("updateTime", metaObject);if(Objects.nonNull(updateTime)){setUpdateFieldValByName("updateTime",updateTime,metaObject);}else{setUpdateFieldValByName("updateTime",LocalDateTime.now(),metaObject);}}
}

对 @TableField(fill = FieldFill.INSERT_UPDATE) 理解

并不是有了这个注解就能进到MyMetaObjHandler,更不是注解了1个字段进去1次,注解了2个字段进去2次,并不是的,只要我们实现了MyMetaObjHandler就都可以进去的,而且一次UPDATE OR INSERT操作只会进一次;只是是否对该字段拥有写入权限的问题,只要被标注,就会在自定义的MyMetaObjHandler里进行对该字段有写入权限,否则哪怕set了,也没效果。

乐观锁

乐观锁适用于读多写少的情况,更新数据的时候不使用“锁“而是使用版本号来判断是否可以更新数据。通过不加锁来减小数据更新时间和系统的性能消耗,进而提高数据库的吞吐量。CAS机制就是一种典型的乐观锁的形式。

乐观锁是逻辑存在的一种概念,我们如果使用乐观锁需要手动在表的加上version字段。

mysql使用乐观锁伪代码示例:

update user
set balabala....
where balabala... and version = xxx

配置类中注入乐观锁插件

@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor(){return new OptimisticLockerInterceptor();
}

实体类中的版本字段增加@version注解

@Data
@EqualsAndHashCode(callSuper = false)
public class User extends Model<User> {...@Versionprivate Integer version;...
}

测试:更新王天风的年龄

@Test
public void testLock(){int version = 1;User user = new User();user.setEmail("wtf@163.com");user.setAge(34);user.setId(2345L);user.setManagerId(1234L);user.setVersion(1);userMapper.updateById(user);}

数据库中的version已经变成2

/*** 在 update(entity, wrapper) 方法下, wrapper 不能复用!!!* 原因:*   每应用一次QueryWrapper,都会自动为where子句添加一次version=?,两次应用就会添加两次,*   出现形如“where ... and version=2 and version=3”的情况* 解决:*   重新创建一个QueryWrapper对象*/
@Test
public void update() throws InterruptedException {User user = userMapper.selectById(1171357729945505793L);user.setAge(34);LambdaQueryWrapper<User> query = Wrappers.<User>lambdaQuery().eq(User::getId, user.getId());int rows1 = userMapper.update(user,query);//第一次用querySystem.out.println("影响行数:"+rows1);user = userMapper.selectById(1171357729945505793L);user.setAge(35);int rows2 = userMapper.update(user,query);//复用querySystem.out.println("影响行数:"+rows2);
}

注意事项

  1. 支持的类型只有:int,Integer,long,Long,Date,Timestamp,LocalDateTime
  2. 整数类型下 newVerison = oldVersion+1
  3. newVersion会写到entity中
  4. 仅支持updateById(id)与update(entity,wrapper)方法
  5. 在update(entiry,wrapper)方法下,wrapper不能复用

性能分析

配置类中注入性能分析插件

@Bean
// @Profile({"dev,test"})
public PerformanceInterceptor performanceInterceptor() {PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();// 格式化sql输出performanceInterceptor.setFormat(true);// 设置sql执行最大时间,单位(ms)performanceInterceptor.setMaxTime(5L);return performanceInterceptor;
}

执行sql就可以打印sql执行的信息了

  • 开启性能分析会消耗系统的性能,所以性能分析插件要配合@Profile注解执行使用的环境。

依靠第三方插件美化 SQL 输出

https://mp.baomidou.com/guide/p6spy.html

第三方依赖

<dependency><groupId>p6spy</groupId><artifactId>p6spy</artifactId><version>3.8.5</version>
</dependency>

更改配置文件中的dirver和url

spring:datasource:
#    driver-class-name: com.mysql.cj.jdbc.Driverusername: rootpassword: root
#    url: jdbc:mysql://localhost:3306/test?serverTimezone=CTT&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=truedriver-class-name: com.p6spy.engine.spy.P6SpyDriverurl: jdbc:p6spy:mysql://localhost:3306/test?serverTimezone=CTT&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true

增加spy.properties配置文件

module.log=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory
# 自定义日志打印
logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger
#日志输出到控制台
appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger
# 使用日志系统记录 sql
#appender=com.p6spy.engine.spy.appender.Slf4JLogger
# 设置 p6spy driver 代理
deregisterdrivers=true
# 取消JDBC URL前缀
useprefix=true
# 配置记录 Log 例外,可去掉的结果集有error,info,batch,debug,statement,commit,rollback,result,resultset.
excludecategories=info,debug,result,batch,resultset
# 日期格式
dateformat=yyyy-MM-dd HH:mm:ss
# 实际驱动可多个
#driverlist=org.h2.Driver
# 是否开启慢SQL记录
outagedetection=true
# 慢SQL记录标准 2 秒
outagedetectioninterval=2

测试

打印到文件

  • 开启美化SQL插件会消耗系统的性能,所以插件要配合@Profile注解执行使用的环境。

多租户

常用情景:一条记录操作者的自动赋值!

过滤方式:传统过滤、注解过滤。

过滤维度:表级过滤、SQL语句过滤。

package com.dsf.mp.tenant.dao;import com.baomidou.mybatisplus.annotation.SqlParser;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.dsf.mp.tenant.entity.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;public interface UserMapper extends BaseMapper<User> {//通过注解的方式过滤掉自定义sql方法的sql解析,false-不过滤sql解析,true-过滤sql解析@SqlParser(filter = true)@Select("select * from user ${ew.customSqlSegment}")List<User> mySelectList(@Param(Constants.WRAPPER)Wrapper<User> wrapper);
}
package com.dsf.mp.tenant.configuration;import com.baomidou.mybatisplus.core.parser.ISqlParser;
import com.baomidou.mybatisplus.core.parser.ISqlParserFilter;
import com.baomidou.mybatisplus.core.parser.SqlParserHelper;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.plugins.tenant.TenantHandler;
import com.baomidou.mybatisplus.extension.plugins.tenant.TenantSqlParser;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.ArrayList;@Configuration
public class MybatisPlusConf {@BeanPaginationInterceptor paginationInterceptor(){PaginationInterceptor paginationInterceptor = new PaginationInterceptor();ArrayList<ISqlParser> sqlParsersList = new ArrayList<>();TenantSqlParser tenantSqlParser = new TenantSqlParser();tenantSqlParser.setTenantHandler(new TenantHandler() {/*** 租户ID,通常指公司代码或ID,获取途径包括:session、配置文件、枚举类、cookie、redis* 这里的ID是写死的,实际项目中要想办法获取*/@Overridepublic Expression getTenantId() {return new LongValue(1088248166370832385L);}/*** 指定租户ID的表列名*/@Overridepublic String getTenantIdColumn() {return "manager_id";}/*** 过滤掉(即不引入)哪些表的多租户功能* false-过滤,默认; true-不过滤;*/@Overridepublic boolean doTableFilter(String tableName) {if("user".equals(tableName)){return false;}return true;}});sqlParsersList.add(tenantSqlParser);paginationInterceptor.setSqlParserList(sqlParsersList);/*** 过滤掉(即不引入)哪些方法的sql解析(即不进行多租户功能)*/paginationInterceptor.setSqlParserFilter(new ISqlParserFilter() {@Overridepublic boolean doFilter(MetaObject metaObject) {MappedStatement ms = SqlParserHelper.getMappedStatement(metaObject);if("com.dsf.mp.tenant.dao.UserMapper.selectById".equals(ms.getId())){return true;}return false;}});return paginationInterceptor;}
}
package com.dsf.mp.tenant;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
@MapperScan("com.dsf.mp.tenant.dao")
public class TenantApp {public static void main(String[] args) {SpringApplication.run(TenantApp.class, args);}
}
package com.dsf.mp.tenant.dao;import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.dsf.mp.tenant.TenantApp;
import com.dsf.mp.tenant.entity.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.*;@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@RunWith(SpringRunner.class)
@SpringBootTest(classes = TenantApp.class)
public class UserMapperTest {@AutowiredUserMapper userMapper;/*** 测试多租户查询方法* 效果:where ... and manager_id=1088248166370832385*/@Testpublic void selectList(){List<User> list = userMapper.selectList(null);list.forEach(System.out::println);}/*** 测试多租户更新方法*/@Testpublic void updateById(){User user = userMapper.selectById(1094592041087729666L);user.setAge(34);int rows = userMapper.updateById(user);System.out.println("受影响的行数:"+rows);}/*** 测试多租户插入方法*/@Testpublic void insert(){User user = new User();user.setName("李国民");user.setAge(29);user.setEmail("lgm@baomidou.com");int rows = userMapper.insert(user);System.out.println("受影响的行数:"+rows);}/*** 测试过滤掉内置方法的多租户功能*/@Testpublic void selectById(){User user = userMapper.selectById(1094590409767661570L);System.out.println(user);}/*** 测试过滤掉自定义方法的多租户功能*/@Testpublic void mySelectList(){List<User> list = userMapper.mySelectList(Wrappers.<User>lambdaQuery().gt(User::getAge,30));list.forEach(System.out::println);}
}
# 配置数据源
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://testhost:3306/mp?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghaiusername: rootpassword: '#Dsf135246'# 配置日志
logging:level:root: warncom.dsf.mp.tenant.dao: tracepattern:console: '%p%m%n'mybatis-plus:mapper-locations: ['classpath:/mapper/*Mapper.xml']
#  global-config.sql-parser-cache: true  #3.1.1以上版本不需要配置该项

动态表名

应用场景:同样的表分表时的操作!

package com.dsf.mp.dynamicTableNameParser.entity;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;private  LocalDateTime updateTime;private  Integer version;private  Integer deleted;}package com.dsf.mp.dynamicTableNameParser.dao;import com.baomidou.mybatisplus.annotation.SqlParser;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.dsf.mp.dynamicTableNameParser.entity.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;public interface UserMapper extends BaseMapper<User> {// 通过注解的方式过滤掉自定义sql方法的sql解析,false-不过滤sql解析,true-过滤sql解析@SqlParser(filter = false)@Select("select * from user ${ew.customSqlSegment}")List<User> mySelectList(@Param(Constants.WRAPPER) Wrapper<User> wrapper);
}
package com.dsf.mp.dynamicTableNameParser.configuration;import com.baomidou.mybatisplus.core.parser.ISqlParser;
import com.baomidou.mybatisplus.core.parser.ISqlParserFilter;
import com.baomidou.mybatisplus.core.parser.SqlParserHelper;
import com.baomidou.mybatisplus.extension.parsers.DynamicTableNameParser;
import com.baomidou.mybatisplus.extension.parsers.ITableNameHandler;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;@Configuration
public class MybatisPlusConf {//user动态表存放对象public static ThreadLocal<String> myTableName=new ThreadLocal<>();@BeanPaginationInterceptor paginationInterceptor(){PaginationInterceptor paginationInterceptor = new PaginationInterceptor();ArrayList<ISqlParser> sqlParsersList = new ArrayList<>();//动态表名解析器Map<String, ITableNameHandler> tableNameHandlerMap = new HashMap<>();tableNameHandlerMap.put("user", new ITableNameHandler() {@Overridepublic String dynamicTableName(MetaObject metaObject, String sql, String tableName) {return myTableName.get();}});DynamicTableNameParser dynamicTableNameParser = new DynamicTableNameParser();dynamicTableNameParser.setTableNameHandlerMap(tableNameHandlerMap);sqlParsersList.add(dynamicTableNameParser);paginationInterceptor.setSqlParserList(sqlParsersList);/*** 过滤掉(即不引入)哪些方法的sql解析功能(即不进行动态表名替换)*/paginationInterceptor.setSqlParserFilter(new ISqlParserFilter() {@Overridepublic boolean doFilter(MetaObject metaObject) {MappedStatement ms = SqlParserHelper.getMappedStatement(metaObject);if("com.dsf.mp.dynamicTableNameParser.dao.UserMapper.selectById".equals(ms.getId())){return true;}return false;}});return paginationInterceptor;}
}
# 配置数据源
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://testhost:3306/mp?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghaiusername: rootpassword: '#Dsf135246'# 配置日志
logging:level:root: warncom.dsf.mp.dynamicTableNameParser.dao: tracepattern:console: '%p%m%n'mybatis-plus:mapper-locations: ['classpath:/mapper/*Mapper.xml']
package com.dsf.mp.dynamicTableNameParser.dao;import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.dsf.mp.dynamicTableNameParser.DynamicTableNameParserApp;
import com.dsf.mp.dynamicTableNameParser.configuration.MybatisPlusConf;
import com.dsf.mp.dynamicTableNameParser.entity.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;@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@RunWith(SpringRunner.class)
@SpringBootTest(classes = DynamicTableNameParserApp.class)
public class UserMapperTest {@AutowiredUserMapper userMapper;/*** 测试动态表名查询方法*/@Testpublic void selectList(){MybatisPlusConf.myTableName.set("user_2019");List<User> list = userMapper.selectList(null);list.forEach(System.out::println);}/*** 测试动态表名更新方法*/@Testpublic void updateById(){MybatisPlusConf.myTableName.set("user_2019");User user = userMapper.selectById(1094592041087729666L);user.setAge(34);int rows = userMapper.updateById(user);System.out.println("受影响的行数:"+rows);}/*** 测试动态表名插入方法*/@Testpublic void insert(){MybatisPlusConf.myTableName.set("user_2019");User user = new User();user.setName("李国民");user.setAge(29);user.setEmail("lgm@baomidou.com");int rows = userMapper.insert(user);System.out.println("受影响的行数:"+rows);}/*** 测试过滤掉内置方法的动态表名*/@Testpublic void selectById(){MybatisPlusConf.myTableName.set("user_2019");User user = userMapper.selectById(1094590409767661570L);System.out.println(user);}/*** 测试过滤掉自定义方法的动态表名*/@Testpublic void mySelectList(){MybatisPlusConf.myTableName.set("user_2019");List<User> list = userMapper.mySelectList(Wrappers.<User>lambdaQuery().gt(User::getAge,30));list.forEach(System.out::println);}
}

SQL注入器:封装自定义通用SQL

实现步骤

  1. 创建定义方法的类
  2. 创建注入器
  3. 在mapper中加入自定义方法
package com.dsf.mp.sqlInjector.entity;import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableLogic;
import lombok.Data;
import java.time.LocalDateTime;@Data
public class User {private Long id;private String name;@TableField(fill= FieldFill.UPDATE)private Integer age;private String email;private Long managerId;private LocalDateTime createTime;private  LocalDateTime updateTime;private  Integer version;@TableLogic@TableField(select=false)private  Integer deleted;
}
package com.dsf.mp.sqlInjector.component.method;import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;public class DeleteAllMethod extends AbstractMethod {@Overridepublic MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {//执行的sqlString sql = "delete from "+tableInfo.getTableName();//mapper接口的方法名String method = "deleteAll";SqlSource sqlSource = languageDriver.createSqlSource(configuration,sql,modelClass);return addDeleteMappedStatement(mapperClass,method,sqlSource);}
}
package com.dsf.mp.sqlInjector.dao;import com.baomidou.mybatisplus.annotation.SqlParser;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.dsf.mp.sqlInjector.entity.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;public interface UserMapper extends MyBaseMapper<User> {@SqlParser(filter = true)@Select("select * from user ${ew.customSqlSegment}")List<User> mySelectList(@Param(Constants.WRAPPER) Wrapper<User> wrapper);//如果只想在本接口中调用自定义的method可以直接写到这里
//    int deleteAll();
}
package com.dsf.mp.sqlInjector.dao;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import org.apache.ibatis.annotations.Param;
import java.util.List;public interface MyBaseMapper<T> extends BaseMapper<T> {int deleteAll();int insertBatchSomeColumn(List<T> list);int deleteByIdWithFill(T t);int alwaysUpdateSomeColumnById(@Param((Constants.ENTITY)) T entity);
}
package com.dsf.mp.sqlInjector.component;import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.baomidou.mybatisplus.extension.injector.methods.additional.AlwaysUpdateSomeColumnById;
import com.baomidou.mybatisplus.extension.injector.methods.additional.InsertBatchSomeColumn;
import com.baomidou.mybatisplus.extension.injector.methods.additional.LogicDeleteByIdWithFill;
import com.dsf.mp.sqlInjector.component.method.DeleteAllMethod;
import org.springframework.stereotype.Component;
import java.util.List;@Component
public class MySqlInjector extends DefaultSqlInjector {@Overridepublic List<AbstractMethod> getMethodList(Class<?> mapperClass) {//一定要调用super.getMethodList,否则内置的方法全都不能用了List<AbstractMethod> methodList = super.getMethodList(mapperClass);//添加自定的方法组件:删除全部methodList.add(new DeleteAllMethod());//添加选装件:批量插入
//        methodList.add(new InsertBatchSomeColumn(x->!x.isLogicDelete()));methodList.add(new InsertBatchSomeColumn(x->!x.isLogicDelete()&&!x.getColumn().equals("age")));//构造器中指定哪些字段插入,哪些不插入//添加选装件:逻辑删除并填充methodList.add(new LogicDeleteByIdWithFill());//添加选装件:根据id更新固定的字段methodList.add(new AlwaysUpdateSomeColumnById(x->!x.getColumn().equals("name")));//构造器中指定哪些字段更新,哪些不更新return methodList;}
}
  • 除了自定义的类,还有一些MP官方自带的选装件。
package com.dsf.mp.sqlInjector.dao;import com.dsf.mp.sqlInjector.SqlInjectorApp;
import com.dsf.mp.sqlInjector.entity.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.Arrays;
import java.util.List;
import static org.junit.Assert.*;@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
@RunWith(SpringRunner.class)
@SpringBootTest(classes = SqlInjectorApp.class)
public class UserMapperTest {@AutowiredUserMapper userMapper;/*** 测试自定义方法组件:删除全部*/@Testpublic void deleteAll(){int rows = userMapper.deleteAll();System.out.println("受影响的行数:"+rows);}/*** 测试选装件:批量删除* 如果插入时字段为null,则不管数据库是否设置默认值,插入的数据均是null*/@Testpublic void insertBatchSomeColumn(){User user1 = new User();user1.setName("李兴华");user1.setAge(34);user1.setManagerId(1088248166370832385L);User user2 = new User();user2.setName("杨红");user2.setAge(29);user2.setManagerId(1088248166370832385L);List<User> asList= Arrays.asList(user1,user2);int rows = userMapper.insertBatchSomeColumn(asList);System.out.println("受影响的行数"+rows);}/*** 测试选装件:逻辑删除并填充*/@Testpublic void deleteByIdWithFill(){User user = userMapper.selectById(1171846939462836226L);
//        user.setAge(23);int rows = userMapper.deleteByIdWithFill(user);System.out.println("受影响的行数"+rows);}/*** 测试选装件:根据id更新固定的字段*/@Testpublic void alwaysUpdateSomeColumnById(){User user = userMapper.selectById(1088248166370832385L);user.setAge(26);user.setName("王地缝");int rows = userMapper.alwaysUpdateSomeColumnById(user);System.out.println("受影响的行数"+rows);}}

MyBatis-Plus - 深入进阶相关推荐

  1. aop拦截mybatis执行sql_Java进阶架构之开源框架面试题系列:Spring+SpringMVC+MyBatis

    开源框架 Spring5 Framework体系结构 spring5概述 Spring5环境搭建 Spring MVC AOP源码解析 IOC源码解析 Mybatis spring 什么是Spring ...

  2. Mybatis自学笔记进阶篇(ResultMap和Log4j)

    5.ResultMap 1.查询为null问题 要解决的问题:属性名和字段名不一致 环境:新建一个项目,将之前的项目拷贝过来 1.查看之前的数据库的字段名 User表名: id int(20) nam ...

  3. 数据库开发技术java方向_Java开发工程师(Web方向) - 03.数据库开发 - 第5章.MyBatis...

    第5章--MyBatis MyBatis入门 Abstract: 数据库框架的工作原理和使用方法(以MyBatis为例) 面向对象的世界与关系型数据库的鸿沟: 面向对象世界中的数据是对象: 关系型数据 ...

  4. 精雕细琢!阿里大师53天悉心打磨出来的MyBatis+设计模式架构指南

    全文内容目录一览 Java设计模式实践指南(字节跳动版) MyBatis入门到进阶(含面试题解) MyBatis底层源码分析(MyBatis架构体系详解) 正文 设计模式是面试中的高频问题,特别是像 ...

  5. 7个月我 自学Java 找到啦一份9k的工作,分享经历

            我是一名普通的不能在普通的大专学生,大学学的是室内设计,毕业后在一所小城市浑浑噩噩的干啦2年工资不到4k,并且在这里看不到任何提升的前景.装饰公司的图纸改的一遍又一遍,同时自己跨行的想 ...

  6. 我的历史文章整理(2017-2019)

    我的写作经历 我是一名普通的 Java 开发者,喜欢在网上分享一些个人的故事以及工作中遇到的问题,自 2017 年开始用 "十三" 这个网名在各个博客平台发表文章,常活跃的平台有 ...

  7. 上周热点回顾(10.16-10.22)

    热点随笔: · 几款效率神器助你走上人生巅峰(Hafiz.Zhang) · 为什么阿里的程序员那么帅?---原来他们都有"编码规约扫描"神器在手(禁心尽力) · 客户懂点代码是最致 ...

  8. 公众号一年多,56 篇精品原创文章汇总!

    大家好,我是不才陈某~ 码猿技术专栏更文已经接近一年半了,今天整理一下近年来陈某的原创精品文章,总共56篇,其中包含<Spring Cloud 进阶>.<Spring Boot 进阶 ...

  9. 小马哥Java知识体系总结

    不断总结积累, 知识的复利是最重要的投资! 非推广, 纯手写, 每天练习总结. Java基础 Java基础01_概述_环境部署_入门程序 Java基础02_变量_数据类型_类型转换_运算符 Java基 ...

  10. Java设计流程执行器_Java进阶面试精选系列:SpringMVC+SpringBoot+Hibernate+Mybatis+设计模式...

    小编精心收集:为金三银四准备,以下面试题先过一遍,为即将到了的面试做好准备,也过一遍基础知识点. 一.Spring/Spring MVC 1.为什么要使用 spring? 2.解释一下什么是 aop? ...

最新文章

  1. 计算机网络技术专业毕业大作业答案,计算机网络技术大作业考核要求A卷
  2. 怎么选择数据服务器?请记住这五条
  3. 使用System Center DPM 2012 SP1保护企业关键数据(二)备份及恢复AD
  4. 卡萨帝:用发明去超越历史!15年走完百年路
  5. css手型指针_前端基础面试题(HTML+CSS部分)
  6. sql语句优化的一些办法
  7. 安卓11及以上adb命令将冰箱设置为设备管理员
  8. 中国神话故事中的仙佛怪关系整理
  9. 第五课:实现花样流水灯
  10. ins的更新带来的一系列问题
  11. vi 常用复制与粘帖技巧
  12. Elasticsearch2.3.4集群安装指南
  13. 双显卡只用独显好吗_双显卡如何切换到独立显卡?双显卡只用独显的方法
  14. CTF之crpto练习三
  15. Java中的短路与运算和短路或运算
  16. 电压跟随器跟随器前后的电压不一致(笔记)
  17. java动物声音模拟器_动物声音模拟器app|动物声音模拟器下载_v9.2.3_9ht安卓下载...
  18. 【Windows】【Linux】---- Java证书导入
  19. C语言_数组的5种复制方法
  20. PCL(Point Cloud Library)的第三方库简介(boost,eigen,flann,vtk,qhull)

热门文章

  1. 如何将文字转换成语音,这种简单方法,建议收藏
  2. Excel怎么对比两列日期,并筛选出不同的日期
  3. JQuery 选择器重点内容
  4. Apple主推的智能家居是什么、怎么用?一篇文章带你从零完全入门 HomeKit
  5. 团队管理27--任务执行之流程机制
  6. minecraft optifine崩溃的一种解决方法
  7. qiankun微前端中主应用路由页面加载微应用的某个路由页面
  8. vue + echarts 多条折线图
  9. 爆炸,解体,入侵,你想得到的你想不到的大BUG们
  10. 跨国企业在中国 | 麦德龙和物美将成立合资公司;洲际与南航开启精英会员等级匹配...