指定数据库表名
@TableName("tianshu_log_event")
//@IdClass(EventCompositePK.class) // 联合主键
public class Event implements Serializable {
private static final long serialVersionUID = 1L;
public static final String TABLE_NAME = "tianshu_log_event";
指定主键字段
@TableId(value = "id", type = IdType.ASSIGN_ID) //生成id的类型:数据库自增(AUTO)、默认id生成器(ASSIGN_ID)、自定义id生成器(INPUT)
private Long id;
映射属性和表字段
@TableField(value = "content")
private String content;
不参与序列化,不会作为数据字段
private transient Integer eventId;
private static Integer eventId;
@TableField(exist = false)
private Integer eventId;
CRUD
条件构造器
com.baomidou.mybatisplus.core.conditions.AbstractWrapper
本章主要介绍MyBatis-Plus查询的主要内容,包括普通查询、条件构造器查询、select不列出全部字段查询等内容。
QueryWrapper
new QueryWrapper().orderBy(true, !isDesc, orderBy)
.like(Boolean.valueOf(nameLike), searchName, searchValue);
只列出id和owner字段
wrapper.select("id", "owner").likeLeft("owner", "sp").gt("level", 3);
不列出owner和content字段(对主键:id不生效)
wrapper.likeLeft("owner", "sp").gt("level", 3)
.select(
Event.class
, info -> !info.getColumn().equals("owner") && !info.getColumn().equals("content"));
本章介绍MyBatis-Plus中自定义sql和分页查询的内容。
介绍MyBatis-Plus中更新和删除功能的使用。
本章介绍MyBatis-Plus中的AR模式、主键策略和基本配置等内容。
@Mapper
public interface EventMapper extends BaseMapper {
...
}
public class Event extends Model implements Serializable {
private static final long serialVersionUID = 1L;
...
}
@Autowired
private Event event;
@GetMapping(path = "/ActiveRecord/{id}/")
public ResponseEntity activeRecord(@PathVariable Long id){
Event event = this.event.selectById(id);
return this.responseEntity.success(event);
}
PS:
mapper的装配方式:
1、接口注解装配
@Mapper
public interface EventMapper extends BaseMapper {
...
}
2、统一装配
@EnableTransactionManagement
@Configuration
@MapperScan(value = {"ik.starriver.log.mapper*"})
public class MyBatisPlusAutoConfiguration {
....
}
局部策略设置在表实体上
public class Event extends Model implements Serializable {
@TableId(value = "id", type = IdType.ASSIGN_UUID) //生成id的类型:数据库自增(AUTO)、默认id生成器(ASSIGN_ID)、自定义id生成器(INPUT)
private Long id;
全局策略设置在配置文件中
mybatis-plus:
global-config:
db-config:
id-type: ASSIGN_ID
多模块模式下:要在classpath后面加 “ * ”
mybatis-plus:
type-handlers-package: ik.starriver.log.handler*
mapper-locations: classpath*:/mapper*/*Mapper.xml #
字段生成策略(局部也可以设置,局部优于全局)
global-config:
db-config:
#未设置字段sql语句生成的策略 IGNORED:"直接设置为null",NOT_NULL:"可以插入设置的空字符串"),NOT_EMPTY:"未设置和空字符串都不会插入"
field-strategy: not_empty
局部配置
@TableField(whereStrategy = FieldStrategy.IGNORED)
private String description;
2020-02-28 02:14:29.835 [DEBUG] [http-nio-9000-exec-1] ik.starriver.log.mapper.EventMapper.selectList:143 : ==> Preparing: SELECT id,content,create_time,description,level,owner FROM tianshu_log_event WHERE description=? AND level=?
2020-02-28 02:14:29.851 [DEBUG] [http-nio-9000-exec-1] ik.starriver.log.mapper.EventMapper.selectList:143 : ==> Parameters: null, 3(Integer)
2020-02-28 02:14:29.937 [DEBUG] [http-nio-9000-exec-1] ik.starriver.log.mapper.EventMapper.selectList:143 : <== Total: 0
PS:
字段生成策略也会影响参数为实体的wrapper
字段策略为ignored
sql语句中未设置字段全部插入null
本章介绍通用service的内容。
mybatis进阶
本章介绍课程的主要内容,课程涉及的表结构,以及项目的基本情况。
本章主要介绍MyBatis-Plus中逻辑删除的内容。
逻辑删除:假删除(通过标识字段是否删除,来表示数据删除与否,不会真实删除数据)
配置文件全局设置
{
"name": "mybatis-plus.global-config.db-config.logic-delete-value",
"type": "java.lang.String",
"sourceType": "com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig"
},
{
"name": "mybatis-plus.global-config.db-config.logic-not-delete-value",
"type": "java.lang.String",
"sourceType": "com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig"
},
注解逻辑删除字段
执行删除,实际是update操作,将deleted设置为1,表示逻辑已删除
全部查询,只会返回deleted=0的数据
更新同理
@TableField(select = false)
private Integer deleted;
select排除deleted字段,不查询
PS:
对自定义sql不生效
public interface EventMapper extends BaseMapper {
@Select("select id, content from tianshu_log_event where id = #{id}")
Event selectByIdSQL(Long id);
须自行过滤deleted字段
本章主要介绍MyBatis-Plus自动填充及优化的内容。
申明需要自动填充的字段
@TableField(fill = FieldFill.INSERT_UPDATE)
private Timestamp createTime;
实现MetaObjectHandler 接口,书写填充逻辑(注意:fieldname是java属性名不是数据库字段名)
package ik.starriver.log.autoconfigure;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
@Component
public class DefaultMetaObjectHandler implements MetaObjectHandler {
private static final SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
@Override
public void insertFill(MetaObject metaObject) {
boolean createTime = metaObject.hasSetter("createTime");
Object objVal = getFieldValByName("createTime", metaObject);
if (createTime && null == objVal){
this.strictInsertFill(metaObject,"createTime", Timestamp.class, Timestamp.valueOf(formatter.format(System.currentTimeMillis())));
}
}
@Override
public void updateFill(MetaObject metaObject) {
boolean createTime = metaObject.hasSetter("createTime");
Object objVal = getFieldValByName("createTime", metaObject);
if (createTime && null == objVal){
this.strictUpdateFill(metaObject,"createTime", Timestamp.class, Timestamp.valueOf(formatter.format(System.currentTimeMillis())));
}
}
}
中文乱码:characterEncoding=utf8
时间错误:serverTimezone=GMT%2B8
数据库连接SSL报错:useSSL=false
public class ApplicationAutoConfiguration {
private final static String DATABASE_URL = "jdbc:mysql://10.1.252.23:3306/springtest?serverTimezone=GMT&useSSL=false&characterEncoding=utf8";
插入时只有带有createTime1字段的语句才会自动生成时间(节省了自动生成时间所需开销)
更新时只有指定字段没有设置值,才会自动填充
本章介绍MyBatis-Plus乐观锁的实现。
多读小写乐观锁
多写少读悲观锁
wrapper不能复用!!!!
本章主要介绍MyBatis-Plus的性能分析、参数设置和执行sql分析打印等内容。
@Bean
@Profile({"dev","test"})
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor interceptor = new PaginationInterceptor();
interceptor.setLimit(100);
interceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
return interceptor;
}
PS:
https://mybatis.plus/guide/performance-analysis-plugin.html
本章介绍MyBatis-Plus多租户的概念及实现。
多租户方式:
分库,每个用户一个数据库,数据安全,便于恢复
统一库,分schema
统一库,统一schema,分表
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
/*
* 【测试多租户】 SQL 解析处理拦截器
* 这里固定写成住户 1 实际情况你可以从cookie读取,因此数据看不到 【 麻花藤 】 这条记录( 注意观察 SQL )
*/
List sqlParserList = new ArrayList<>();
TenantSqlParser tenantSqlParser = new MyTenantParser();
tenantSqlParser.setTenantHandler(new TenantHandler() {
/**
* 2019-8-1
*
* https://gitee.com/baomidou/mybatis-plus/issues/IZZ3M
*
* tenant_id in (1,2)
* @param where 如果是where,可以追加,不是where的情况:比如当insert时,不能insert into user(name, tenant_id) values('test', tenant_id IN (1, 2));
* @return
*/
@Override
public Expression getTenantId(boolean where) {
final boolean multipleTenantIds = true;//这里只是演示切换单个tenantId和多个tenantId
//具体场景,可以根据情况来拼接
if (where && multipleTenantIds) {
//演示如何实现tenant_id in (1,2)
return multipleTenantIdCondition();
} else {
//演示:tenant_id=1
return singleTenantIdCondition();
}
}
private Expression singleTenantIdCondition() {
return new LongValue(1);//ID自己想办法获取到
}
private Expression multipleTenantIdCondition() {
final InExpression inExpression = new InExpression();
inExpression.setLeftExpression(new Column(getTenantIdColumn()));
final ExpressionList itemsList = new ExpressionList();
final List inValues = new ArrayList<>(2);
inValues.add(new LongValue(1));//ID自己想办法获取到
inValues.add(new LongValue(2));
itemsList.setExpressions(inValues);
inExpression.setRightItemsList(itemsList);
return inExpression;
}
@Override
public String getTenantIdColumn() {
return "tenant_id";
}
@Override
public boolean doTableFilter(String tableName) {
// 这里可以判断是否过滤表
/*if ("user".equals(tableName)) {
return true;
}*/
// return false;
return !"user".equalsIgnoreCase(tableName);
}
});
sqlParserList.add(tenantSqlParser);
paginationInterceptor.setSqlParserList(sqlParserList);
// paginationInterceptor.setSqlParserFilter(new ISqlParserFilter() {
// @Override
// public boolean doFilter(MetaObject metaObject) {
// MappedStatement ms = SqlParserHelper.getMappedStatement(metaObject);
// // 过滤自定义查询此时无租户信息约束【 麻花藤 】出现
// if ("com.baomidou.springboot.mapper.UserMapper.selectListBySQL".equals(ms.getId())) {
// return true;
// }
// return false;
// }
// });
return paginationInterceptor;
}
本章主要介绍MyBatis-Plus动态表的实现及注意事项。
相同表结构,分表存储,如日志按月分表,按企业对象分表等
本章主要介绍SQL注入器,和选装件的内容。
自定义Method
package ik.starriver.log.sqlinjector.methods;
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 {
@Override
public MappedStatement injectMappedStatement(Class> mapperClass, Class> modelClass, TableInfo tableInfo) {
String mapperName = "deleteAll";
String sql = "DELETE FROM " + tableInfo.getTableName();
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
return addDeleteMappedStatement(mapperClass,mapperName,sqlSource);
}
}
添加到list中,注解装配到Spring IoC容器中
package ik.starriver.log.sqlinjector;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import ik.starriver.log.sqlinjector.methods.DeleteAllMethod;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class CustomizedSqInjector extends DefaultSqlInjector {
@Override
public List getMethodList(Class> mapperClass) {
List methodList = super.getMethodList(mapperClass);
methodList.add(new DeleteAllMethod());
return methodList;
}
}
AutoConfiguration @Bean的方式装配
@Bean
public DefaultSqlInjector defaultSqlInjector(){
CustomizedSqInjector defaultSqlInjector = new CustomizedSqInjector();
return defaultSqlInjector;
}
@Data
@Accessors(chain = true)
@TableName(autoResultMap = true)
public class User {
private Long id;
...
/**
* 注意!! 必须开启映射注解
*
* @TableName(autoResultMap = true)
*
* 以下两种类型处理器,二选一 也可以同时存在
*
* 注意!!选择对应的 JSON 处理器也必须存在对应 JSON 解析依赖包
*/
@TableField(typeHandler = JacksonTypeHandler.class)
// @TableField(typeHandler = FastjsonTypeHandler.class)
private OtherInfo otherInfo;
}
@TableName(value = "tianshu_log_event"
, autoResultMap = true
)
public class Event extends BaseModel {
/**
* 注意!! 必须开启映射注解
*
* @TableName(autoResultMap = true)
*
* 以下两种类型处理器,二选一 也可以同时存在
*
* 注意!!选择对应的 JSON 处理器也必须存在对应 JSON 解析依赖包
*/
@JsonDeserialize(using = EventLevelDeserializer.class)
@TableField(typeHandler = EventLevelHandler.class) //开启autoResultMap后才能使用调到指定的handler
@JsonSerialize(using = EventLevelSerializer.class)
private EventLevelEnum level;