实体类演示
@TableName("process_agent_manage")
public class AgentManageConfigEntity extends SuperEsmEntity{
@TableField("authorizer_id")
private String authorizerId;
@TableField("agent_id")
private String agentId;
@TableField("expiration_begin")
private Long expirationBegin;
@TableField("expiration_end")
private Long expirationEnd;
@TableField("agent_type")
private Integer agentType;
@TableField("step_code")
private String stepCode;
@TableField("remark")
private String remark;
@TableField("corp_id")
private String corpId;
@TableLogic
@TableField("is_del")
private Integer isDel;
@TableField("ref_agent_id")
private Long refAgentId;
}
实体类注解大全
@TableField(exist = false) 不是数据库字段
@TableLogic 标记为逻辑删除
@TableField 标记她的数据名
@TableId 标记为主键
@Repository
public interface TestMysqlMapper extends BaseMapper<TemEntity> {
}
LambdaQueryWrapper<TemEntity> queryWrapper =
Wrappers.<TemEntity>lambdaQuery()
.eq(TemEntity::getNeedAmount, 0);
LambdaUpdateWrapper<TemEntity> updateWrapper =
Wrappers.<TemEntity>lambdaUpdate(new TemEntity()
.setAccount_id("1111"))
.eq(TemEntity::getNeedAmount, 0);
LambdaQueryWrapper<TemEntity> queryWrapper2 =
new QueryWrapper<TemEntity().lambda()
.eq(TemEntity::getNoAmount, 0);
LambdaUpdateWrapper<TemEntity> updateWrapper2 =
new UpdateWrapper<TemEntity>(
newTemEntity().setAccount_id("1111")).lambda()
.eq(TemEntity::getNoAmount, 0);
插入一个
approveConfigMapper.insert(approveConfigEntity);
按照id更新一个
id放在了entity里面
approveConfigMapper.updateById(entity);
按照条件更新一个
条件通过QueryWrapper显示
approveConfigMapper.update(entity, new QueryWrapper<ApproveConfigEntity>().lambda()
.eq(ApproveConfigEntity::getId, approveConfigInputVO.getId())
.eq(ApproveConfigEntity::getCorpId, approveConfigInputVO.getCorpId()))
按照id删除一个
billFinanceConfigMapper.deleteBatchIds(ids);
按照id删除多个
billFinanceConfigMapper.deleteById(id);
按照条件删除
billFinanceConfigMapper.delete(new QueryWrapper<BillFinanceConfigEntity>().lambda()
.eq(BillFinanceConfigEntity::getCorpId, billFinanceConfigInputVO.getCorpId())
.eq(BillFinanceConfigEntity::getDeptId, billFinanceConfigInputVO.getDeptId())
);
按照id查询单个
这个方法查出来的,如果存在多个就会报错,可以拿唯一索引或者主键作为条件
ApproveConfigEntity approveConfigEntity = approveConfigMapper.selectOne(new QueryWrapper<ApproveConfigEntity>().lambda()
.eq(ApproveConfigEntity::getCorpId, billFinanceConfigInputVO.getCorpId())
.eq(ApproveConfigEntity::getDeptId, billFinanceConfigInputVO.getDeptId()));
按照条件查询多个(不分页)
List<BillFinanceConfigEntity> billFianceList = billFinanceConfigMapper.selectList(new QueryWrapper<BillFinanceConfigEntity>().lambda()
.eq(BillFinanceConfigEntity::getCorpId, approveConfigInputVO.getCorpId())
.eq(BillFinanceConfigEntity::getDeptId, approveConfigInputVO.getDeptId())
);
按照条件查询多个(分页)
appRefsCurrencyMapper.selectPage(new Page<>(po.getPageIndex(), po.getPageSize()), new QueryWrapper<AppRefsCurrencyEntity>()
.lambda()
.like(StringUtils.isNotEmpty(po.getKeyword()), AppRefsCurrencyEntity::getCuryName, po.getKeyword()))
分页查询mapper应用
IPage<AgentManageConfigVO> queryList(Page<AgentManageConfigEntity> objecPage, @Param("po") AgentManageConfigInputVO po);
allEq
filter : 过滤函数,是否允许字段传入比对条件中
null2IsNull:是否过滤null
allEq(Map<R, V> params)
allEq(Map<R, V> params, boolean null2IsNull)
allEq(boolean condition, BiPredicate<R, V> filter, Map<R, V> params, boolean null2IsNull)
例1: allEq({id:1,name:"老王",age:null})--->id = 1 and name = '老王' and age is null
例2: allEq({id:1,name:"老王",age:null}, false)--->id = 1 and name = '老王'
ne
不等于
ne(R column, Object val)
func
通过java内的条件决定用哪个语句,多重分支时适用
func(i -> if(true) {
i.eq("id", 1)
} else{
i.ne("id", 1)
}
)
inSql
inSql(“age”, “1,2,3,4,5,6”)—>age in (1,2,3,4,5,6)
inSql(“id”, “select id from table where id < 3”)—>id in (select id from table where id < 3)
inSql(R column, String inValue)
nested
用and组合里面的条件
nested(
i -> i.eq("name", "李白").ne("status", "活着"))
(name = '李白' and status <> '活着')
apply
拼接sql,通过{index}可以取到参数,有sql注入风险
last(String lastSql)
例: apply("id = 1")--->id = 1
例: apply("date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")--->date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")
例: apply("date_format(dateColumn,'%Y-%m-%d') = {0}", "2008-08-08")--->date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")
last
无视优化规则,尾部直接拼接sql
last(String lastSql)
exists
exists(String existsSql)
exists("select id from table where age = 1")--->exists (select id from table where age = 1)
基本配置
mybatis-plus:
global-config:
db-config:
logic-delete-field: is_delete # 统一全局状态字段名(一般不全局,有些是物理删除)
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
标记法
@TableLogic
private Integer is_delete;
影响
插入: 不作限制,手动指定条件字段
查找:自动忽略删除状态的数据
更新:自动忽略删除状态的数据
删除:转变为更新
调用
testMysqlMapper.deleteById(1)
指定好对枚举字段的解析,然后mapper可以使用枚举作为参数传给SQL
@EnumValue注解法
ublic enum GradeEnum {
PRIMARY(1, "小学"), SECONDORY(2, "中学"), HIGH(3, "高中");
GradeEnum(int code, String descp) {
this.code = code;
this.descp = descp;
}
@EnumValue//标记数据库存的值是code
private final int code;
//。。。
}
IEnum接口法
public enum AgeEnum implements IEnum<Integer> {
ONE(1, "一岁"),
TWO(2, "二岁"),
THREE(3, "三岁");
private int value;
private String desc;
@Override
public Integer getValue() {
return this.value;
}
}
全局配置
mybatis-plus:
# 支持统配符 * 或者 ; 分割
typeEnumsPackage: com.jm.values.enums
序列化配置
前端对枚举的转化
@Bean
public Jackson2ObjectMapperBuilderCustomizer customizer(){
return builder -> builder.featuresToEnable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
}
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(SerializationFeature.WRITE_ENUMS_USING_TO_STRING, true);
public enum GradeEnum {
PRIMARY(1, "小学"), SECONDORY(2, "中学"), HIGH(3, "高中");
GradeEnum(int code, String descp) {
this.code = code;
this.descp = descp;
}
@EnumValue
@JsonValue //标记响应json值
private final int code;
}
mybatis中已经蕴含了大量的对jdk原生类型的数据对应处理,但是我们的自定义类型则需要通过类型处理器去特殊处理
局部
@Data
@Accessors(chain = true)
@TableName(autoResultMap = true)
public class User {
private Long id;
/**
* 注意!! 必须开启映射注解 @TableName(autoResultMap = true)
* 这个需求就是前端对应一个实体数据传过来
* 我们不需要手动调用json存该string
* 而是调用类型处理器
*/
@TableField(typeHandler = JacksonTypeHandler.class)
private OtherInfo otherInfo;
}
全局(待更新)
配置
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
实体
@Version
private Integer version;
支持
int,Integer,long,Long,Date,Timestamp,LocalDateTime
原理
取出记录时\前端自带,获取当前version
执行更新时插件自动添加, set version = newVersion where version = oldVersion
公共字段注入
/*
* 自动填充处理器,用于填充公共字段
* 无值则入库会是null
* strict则是实体类的注解说明以及实体类的类型来决定是否注入
* fillStrategy:直接注入,不管策略如何spring-boot-maven-plugin
* @author luozhifeng
* @date 2020/9/29
*/
@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill ....");
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐使用)
// 或者
this.fillStrategy(metaObject, "createTime", LocalDateTime.now()); // 也可以使用(3.3.0 该方法有bug)
}
@Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill ....");
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐)
this.fillStrategy(metaObject, "updateTime", LocalDateTime.now()); // 也可以使用(3.3.0 该方法有bug)
}
}
公共字段配置
/**
*
* 默认不处理 DEFAULT
* 插入填充字段 INSERT
* 更新填充字段 UPDATE
* 插入和更新填充字段 INSERT_UPDATE
* 注解则是指定该属性在对应情况下必有值,如果无值则入库会是null
*/
public class SuperEntity {
private static final long serialVersionUID =4359709211352400087L;
@TableId(type = IdType.ID_WORKER)
private Long id;
@TableField(value = "create_by", fill = FieldFill.INSERT)
private String createBy;
@TableField(value = "update_by", fill = FieldFill.INSERT_UPDATE)
private String updateBy;
@TableField(value = "create_date", fill = FieldFill.INSERT)
private long createDate;
@TableField(value = "update_date", fill = FieldFill.INSERT_UPDATE)
private long updateDate;
}
所有注入器都是通过返回一个SqlParserList整合进mybatis
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
paginationInterceptor.setSqlParserList(getISqlParser());
return paginationInterceptor;
}
如果指定条件满足则直接return,最后向应用删除失败而不会进入真正的processDelete
public List<ISqlParser> getISqlParser(){
List<ISqlParser> sqlParserList = new ArrayList<>();
sqlParserList.add(new BlockAttackSqlParser() {
@Override
public void processDelete(Delete delete) {
if ("user".equals(delete.getTable().getName())) {
return ;
}
super.processDelete(delete);
}
});
return sqlParserList;
}
public List<ISqlParser> getISqlParser(){
List<ISqlParser> sqlParserList = new ArrayList<>();
DynamicTableNameParser dynamicTableNameParser = new DynamicTableNameParser();
dynamicTableNameParser.setTableNameHandlerMap(new HashMap<String, ITableNameHandler>(2) {{
put("user", (metaObject, sql, tableName) -> {
// metaObject、sql,我們可以以這些作爲依据来生成表名
//下面这个是随机时间
String year = "_2018";
int random = new Random().nextInt(10);
if (random % 2 == 1) {
year = "_2019";
}
return tableName + year;
});
}});
sqlParserList.add(dynamicTableNameParser);
return sqlParserList;
}
装饰BaseMapper
public interface MyBaseMapper<T> extends BaseMapper<T> {
Integer deleteAll();
int myInsertAll(T entity);
int mysqlInsertAllBatch(@Param("list") List<T> batchList);
}
创建实现链接器
method:指定接口中的方法名完成连接
sql :最终执行的sql
public class DeleteAll extends AbstractMethod {
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
/* 执行 SQL ,动态 SQL 参考类 SqlMethod */
String sql = "delete from " + tableInfo.getTableName();
/* mapper 接口方法名一致 */
String method = "deleteAll";
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
return this.addDeleteMappedStatement(mapperClass, method, sqlSource);
}
}
MyInsertAll
主要演示怎么通过实体类的TableInfo 完成插入
public class MyInsertAll extends AbstractMethod {
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
String sql = "insert into %s %s values %s";
StringBuilder fieldSql = new StringBuilder();
fieldSql.append(tableInfo.getKeyColumn()).append(",");
StringBuilder valueSql = new StringBuilder();
valueSql.append("#{").append(tableInfo.getKeyProperty()).append("},");
tableInfo.getFieldList().forEach(x->{
fieldSql.append(x.getColumn()).append(",");
valueSql.append("#{").append(x.getProperty()).append("},");
});
fieldSql.delete(fieldSql.length()-1, fieldSql.length());
fieldSql.insert(0, "(");
fieldSql.append(")");
valueSql.insert(0, "(");
valueSql.delete(valueSql.length()-1, valueSql.length());
valueSql.append(")");
SqlSource sqlSource = languageDriver.createSqlSource(configuration, String.format(sql, tableInfo.getTableName(), fieldSql.toString(), valueSql.toString()), modelClass);
return this.addInsertMappedStatement(mapperClass, modelClass, "myInsertAll", sqlSource, new NoKeyGenerator(), null, null);
}
}
MysqlInsertAllBatch
主要演示sql怎么样访问注解值
prepareValuesSqlForMysqlBatch中")
public class MysqlInsertAllBatch extends AbstractMethod {
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
final String sql = "";
final String fieldSql = prepareFieldSql(tableInfo);
final String valueSql = prepareValuesSqlForMysqlBatch(tableInfo);
final String sqlResult = String.format(sql, tableInfo.getTableName(), fieldSql, valueSql);
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sqlResult, modelClass);
return this.addInsertMappedStatement(mapperClass, modelClass, "mysqlInsertAllBatch", sqlSource, new NoKeyGenerator(), null, null);
}
private String prepareFieldSql(TableInfo tableInfo) {
StringBuilder fieldSql = new StringBuilder();
fieldSql.append(tableInfo.getKeyColumn()).append(",");
tableInfo.getFieldList().forEach(x -> {
fieldSql.append(x.getColumn()).append(",");
});
fieldSql.delete(fieldSql.length() - 1, fieldSql.length());
fieldSql.insert(0, "(");
fieldSql.append(")");
return fieldSql.toString();
}
private String prepareValuesSqlForMysqlBatch(TableInfo tableInfo) {
final StringBuilder valueSql = new StringBuilder();
valueSql.append("" );
valueSql.append("#{item.").append(tableInfo.getKeyProperty()).append("},");
tableInfo.getFieldList().forEach(x -> valueSql.append("#{item.").append(x.getProperty()).append("},"));
valueSql.delete(valueSql.length() - 1, valueSql.length());
valueSql.append("");
return valueSql.toString();
}
}
创建全局注入器
public class MyLogicSqlInjector extends DefaultSqlInjector {
/**
* 如果只需增加方法,保留MP自带方法
* 可以super.getMethodList() 再add
* @return
*/
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
List<AbstractMethod> methodList = super.getMethodList(mapperClass);
methodList.add(new DeleteAll());
methodList.add(new MyInsertAll());
methodList.add(new MysqlInsertAllBatch());
methodList.add(new SelectById());
return methodList;
}
}
注入全局注入器
@Bean
public MyLogicSqlInjector myLogicSqlInjector() {
return new MyLogicSqlInjector();
}
mybatis-plus:
# 【别名/类型处理器】
type-aliases-superType: null
type-aliases-package: null
type-handlers-package: com.jm.config.typeHandler
type-enums-package: null
# 【sql执行器类型】
executorType: simple
# 【基本扫描】
configLocation: …… #【mybatis————原生配置——xml文件】
mapper-locations: #【mybatis————mapper扫描(,号隔开)】
classpath:/mapper/*Mapper.xml, /mapper/*/*Mapper.xml
#【mybatis————原生配置——配置类】
configuration:
mapUnderscoreToCamelCase: true #驼峰命名
defaultEnumTypeHandler: #枚举处理org.apache.ibatis.type.EnumTypeHandler
aggressiveLazyLoading: true #select懒加载
autoMappingBehavior: partial #
autoMappingUnknownColumnBehavior: NONE
localCacheScope: SESSION
cacheEnabled: true
configurationFactory:
callSettersOnNulls: false
configurationProperties: #【mybatis————外部配置——引入其他路径配置类】
global-config:
banner: true #【控制台打印sql】
enableSqlRunner: true #【初始化SqlRunner】
#sql阻断器
sqlInjector: com.baomidou.mybatisplus.core.injector.DefaultSqlInjector
#mapper父类
superMapperClass: com.baomidou.mybatisplus.core.mapper.Mapper.class
#主键自增
identifierGenerator: com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator
#自动注入处理器
metaObjectHandler: null
# 【mybatis-plus策略配置】
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #日志打印实现
cache-enabled: false #二级缓存
dbconfig:
# 【主键与互策略】
db-type: mysql
id-type: 2 # 【主键策略】 0:"数据库ID自增", 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)", 3:"全局唯一ID UUID";
field-strategy: 2 # 【字段策略】 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断"
key-generator: null
#【驼峰命名策略】
map-underscore-to-camel-case: true #实体类映射驼峰命名转化
db-column-underline: true #数据库字段驼峰下划线转换
table-underline: true #表驼峰命名
table-prefix: null #表统一前缀
capital-mode: false #大写命名
#【格式化策略】
column-format: null
property-format: null
# 【逻辑删除策略】
logic-delete-field: is_delete
logic-delete-value: 0
logic-not-delete-value: 1
#【自动填充策略】
#com.baomidou.mybatisplus.annotation.FieldStrategy
insertStrategy: NOT_NULL
updateStrategy: NOT_NULL
selectStrategy: NOT_NULL
#【刷新mapper策略】
refresh-mapper: true # 刷新mapper 调试神器
ExecutorType.SIMPLE
该执行器类型不做特殊的事情,为每个语句的执行创建一个新的预处理语句(PreparedStatement)
ExecutorType.REUSE
该执行器类型会复用预处理语句(PreparedStatement)
ExecutorType.BATCH
该执行器类型会批量执行所有的更新语句
autoMappingBehavior
MyBatis 自动映射策略,通过该配置可指定 MyBatis 是否并且如何来自动映射数据表字段与对象的属性,总共有 3 种可选值:
AutoMappingBehavior.NONE:不启用自动映射
AutoMappingBehavior.PARTIAL:只对非嵌套的 resultMap 进行自动映射
AutoMappingBehavior.FULL:对所有的 resultMap 都进行自动映射
autoMappingUnknownColumnBehavior
MyBatis 自动映射时未知列或未知属性处理策略,通过该配置可指定 MyBatis 在自动映射过程中遇到未知列或者未知属性时如何处理,总共有 3 种可选值:
AutoMappingUnknownColumnBehavior.NONE:不做任何处理 (默认值)
AutoMappingUnknownColumnBehavior.WARNING:以日志的形式打印相关警告信息
AutoMappingUnknownColumnBehavior.FAILING:当作映射失败处理,并抛出异常和详细信息
localCacheScope
微服务架构中需要关闭一级缓存,
原因:Service1先查询数据,若之后Service2修改了数据,之后Service1又再次以同样的查询条件查询数据,因走缓存会出现查处的数据不是最新数据
callSettersOnNulls
类型:boolean
默认值:false
即 MyBatis 在使用 resultMap 来映射查询结果中的列,如果查询结果中包含空值的列,则 MyBatis 在映射的时候,不会映射这个字段