SpringBoot入门教程07——整合mybatis-plus(三)
SpringBoot入门教程07——整合mybatis-plus(三)
大纲
- springboot整合mybatis-plus入门,以及mybatis-plus代码生成工具入门,传送门
- springboot整合mybatis-plus实现事务控制、分页、自定义SQL以及条件构造器Wrapper入门,传送门
- 批量插入、更新
- 字段填充
- 逻辑删除
- lambda表达式格式的条件构造器用法
- 条件构造器Wrapper的setEntity用法
批量插入、更新
最近股市很火,本人就写了一个爬虫,从财经网站爬取股票数据,然后存到本地数据库,由于数据量还比较大,就用到了批量插入、更新功能。
mybatis-plus提供了2套CRUD接口,一套是Mapper接口,另一套是Service接口,2种接口都可以通过mybatis-plus代码生成器生成。
前2篇文章已经详细介绍了Mapper接口的用法,但是Mapper本身并不支持批量插入功能,幸运的是Service接口提供了该功能。
Service接口
public interface IStockDetailService extends IService<StockDetail> {
}
Service接口默认实现类
@Service
public class StockDetailServiceImpl extends ServiceImpl<StockDetailMapper, StockDetail> implements IStockDetailService {
}
业务代码
@Autowired
private IStockDetailService stockDetailService;
@Transactional
public void loadAndSave() {
List<StockDetail> list = new ArrayList<>(5000);
//往list中添加数据省略
stockDetailService.saveBatch(list);
}
如上所示,直接使用Service接口提供的saveBatch(Collection c)方法即可。
字段填充
每一个交易日,我们可能会去财经网站爬取多次数据,在保存数据的时候想记录数据创建时间和修改时间,就用到了字段填充。
创建自定义的MetaObjectHandler,并注入spring容器
@Slf4j
@Component
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.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐使用)
}
@Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill ....");
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推荐使用)
}
}
Entity实体类
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("t_stock_detail")
public class StockDetail implements Serializable {
/**
* 创建时间
*/
@TableField(value = "create_time",fill = FieldFill.INSERT)
private LocalDateTime createTime;
/**
* 更新时间
*/
@TableField(value = "update_time",fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}
如上所示,指定createTime填充策略为插入填充,指定updateTime字段填充策略为插入和更新时填充
逻辑删除
同一个交易日,每次获取到最新数据后,需要把当天历史数据删除,做过开发的都知道,数据最好逻辑删除,万一删错了,还能想办法恢复。
逻辑删除需要在application.yml中增加mybatis-plus的配置
- 指定全局逻辑删除字段 logic-delete-field: delFlag
- 设置逻辑未删除值 logic-not-delete-value: 0
- 设置逻辑已删除值 logic-delete-value: 1
业务代码
//逻辑删除旧数据
LambdaUpdateWrapper<StockDetail> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(StockDetail::getTradeDate, localDate);
stockDetailMapper.delete(updateWrapper);
跟正常删除代码一样,不过最终仅仅是把数据库中当前交易日数据的del_flag值更新为1
但是这里有一个坑,设置了全局逻辑删除字段之后,新增数据时del_flag值为null,就导致查询不到数据,此时就用到了字段填充功能,设置insert时为delFlag字段填充值0即可。
完整的mybatis-plus配置
mybatis-plus:
#外部化xml配置
#config-location: classpath:mybatis-config.xml
#指定外部化 MyBatis Properties 配置,通过该配置可以抽离配置,实现不同环境的配置部署
#configuration-properties: classpath:mybatis/config.properties
#xml扫描,多个目录用逗号或者分号分隔(告诉 Mapper 所对应的 XML 文件位置)
mapper-locations: classpath*:/mapper/*.xml
#MyBaits 别名包扫描路径,通过该属性可以给包中的类注册别名
#type-aliases-package: net.xinhuamm.noah.api.model.entity,net.xinhuamm.noah.api.model.dto
#如果配置了该属性,则仅仅会扫描路径下以该类作为父类的域对象
#type-aliases-super-type: java.lang.Object
#枚举类 扫描路径,如果配置了该属性,会将路径下的枚举类进行注入,让实体类字段能够简单快捷的使用枚举属性
#type-enums-package: com.baomidou.mybatisplus.samples.quickstart.enums
#项目启动会检查xml配置存在(只在开发时候打开)
check-config-location: true
#SIMPLE:该执行器类型不做特殊的事情,为每个语句的执行创建一个新的预处理语句,REUSE:该执行器类型会复用预处理语句,BATCH:该执行器类型会批量执行所有的更新语句
default-executor-type: REUSE
configuration:
# 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN(下划线命名) 到经典 Java 属性名 aColumn(驼峰命名) 的类似映射
map-underscore-to-camel-case: false
# 全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存,默认为 true
cache-enabled: false
#懒加载
#aggressive-lazy-loading: true
#NONE:不启用自动映射 PARTIAL:只对非嵌套的 resultMap 进行自动映射 FULL:对所有的 resultMap 都进行自动映射
#auto-mapping-behavior: partial
#NONE:不做任何处理 (默认值)WARNING:以日志的形式打印相关警告信息 FAILING:当作映射失败处理,并抛出异常和详细信息
#auto-mapping-unknown-column-behavior: none
#如果查询结果中包含空值的列,则 MyBatis 在映射的时候,不会映射这个字段
call-setters-on-nulls: true
# 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
#表名下划线命名默认true
table-underline: true
#id类型
id-type: auto
#是否开启大写命名,默认不开启
#capital-mode: false
#全局逻辑删除字段
logic-delete-field: delFlag
#逻辑未删除值,(逻辑删除下有效)
logic-not-delete-value: 0
#逻辑已删除值,(逻辑删除下有效)
logic-delete-value: 1
#数据库类型
db-type: mysql
LambdaQueryWrapper和LambdaUpdateWrapper
见名知意,就是支持lambda语法的条件构造器。
QueryWrapper用法
QueryWrapper<StockTradeDate> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("is_open",1)
.le("trade_date","2020-0714")
.orderByDesc("trade_date")
.last("limit 1");
LambdaQueryWrapper用法
LambdaQueryWrapper<StockTradeDate> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(StockTradeDate::getIsOpen, 1)
.le(StockTradeDate::getTradeDate,date)
.orderByDesc(StockTradeDate::getTradeDate)
.last("limit 1");
通过对比可知,queryWrapper传的参数是数据库列名,而LambdaQueryWrapper则传的是Entity实体类字段,显而易见后一种语法更符合java开发的习惯,而且能避免数据库字段拼写错误。
条件构造器Wrapper的setEntity方法
后端开发经常会有这样的需求,前端页面有一个搜索框,里面有很多字段,如果用户输入某些字段值,就要把这些字段当做筛选条件去筛选数据。
如果通过判断字段非空,然后一个一个调用QueryWrapper方法有点太蠢,此时可以使用wrapper的setEntity方法
@RequestMapping("/list1")
public Object list1(){
QueryWrapper<StockTradeDate> wrapper = new QueryWrapper<>();
StockTradeDate stockTradeDate = new StockTradeDate();
stockTradeDate.setIsOpen(1L);
stockTradeDate.setTradeDate("2020-07-14");
wrapper.setEntity(stockTradeDate);
List<StockTradeDate> list = tradeDateMapper.selectList(wrapper);
return list;
}
代码简单明了,就不多说。
mybatis-plus底层如何实现setEntity方法我没有深追代码,但是如何自己实现该方法,本人倒是有一些想法
- 方法参数是entity对象
- 根据entity对象可以获取entity实体类的属性Field
- 判断entity实体类的各个Field上有没有@TableField注解
- 如果有,则可获得数据库的字段名,也可以获取该字段的值
- 如果没有,则认为entity实体类的字段名就是数据库的字段名(或者根据转驼峰策略反向得到数据库字段名)
- 最终就能返回一个数据库字段名和对应值的map
- 基于这个map就能拼接出查询sql (条件构造器Wrapper支持传map参数)
简单实现代码如下:
public class MybatisPlusUtil<T> {
public Map<String, Object> convert(T t) {
Map<String, Object> map = new HashMap<>();
try {
Field[] fields = t.getClass().getDeclaredFields();
if (fields != null) {
for (Field field : fields) {
if (Modifier.isStatic(field.getModifiers())) {
continue;
}
if (!field.isAccessible()) {
field.setAccessible(true);
}
Object o = field.get(t);
if(o!=null){
TableField annotation = field.getAnnotation(TableField.class);
if (annotation != null) {
String value = annotation.value();
if (value != null) {
map.put(value, o);
}
}else{
map.put(field.getName(), o);
}
}
}
}
} catch (Exception e) {
System.out.println(e);
}
return map;
}
}
本文地址:https://blog.csdn.net/l229568441/article/details/107350335
上一篇: python的一个小游戏
下一篇: React 中 ref 的使用
推荐阅读
-
三、SpringBoot整合Thymeleaf视图
-
【SpringBoot】廿六、SpringBoot中整合MyBatis-Plus
-
SpringBoot+Mybatis+Durid整合多数据源的三种方式,第一种
-
SpringBoot整合Forest实现调用第三方接口
-
SpringBoot 2.x 整合Mybatis三:tk.mybatis
-
IDEA-SpringBoot整合Servlet三大组件
-
SpringBoot+Mybatis+Durid整合多数据源的三种方式,第三种(注解切换)
-
springboot整合mybatis-plus完整案例
-
SpringBoot2.x系列二:整合第三方组件Mybatis、JPA、Redis、Elasticsearch、ActiveMQ、Kafka、Logback
-
springboot整合mybatis-plus 实现分页查询功能