Mybatis-flex代替繁琐的JPA

news/2024/10/6 14:15:06

1. 前言

最近在新的SpringBoot项目中采用JPA来作为数据库的持久层。刚开始得益于Spring框架自带,IDEA也有丰富的支持;可以自行匹配数据库字段,接口中方法可以直接提示,支持JPQL,原生SQL等方式。写起来也是非常顺手。但是当业务中有一些复杂一点的需求,在JPA中实现就非常麻烦,且不直观。

本文不是批判JPA和Mybatis-plus的不足,也不会来对比他们的写法优劣。主要是介绍从编码生产力方面解决我日常写代码的一些问题和带来哪些便利。

2. 使用痛点

2.1. JpaRepository(优点)

JPA的Mapper操作类是通过继承JpaRepository来进行CRUD操作,同时有一个非常便捷的操作,我们只需要在接口中通过一定的规则定义方法名称,我们就可以执行对应的SQL。

interface AppRealtimeRecordRepository: JpaRepository<AppRealtimeRecord, Int> {fun findByCardNo(cardNo: String?): AppRealtimeRecord?@Query("select count(1) from AppRealtimeRecord arr where " +"arr.deviceSn = :#{#orderQueryRequest.deviceSn} AND " +"arr.createTime BETWEEN COALESCE(:#{#orderQueryRequest.payStartTime}, arr.createTime) AND " +"COALESCE(:#{#orderQueryRequest.payEndTime}, arr.createTime)")fun summaryQuery(orderQueryRequest: OrderQueryRequest): Long
}

 

 

findByCardNo可以直接生成通过卡号查询实体的语句,非常方便。但是如果我们查询条件比较多,用这种方式就会生成非常长的方法名称,这个时候就需要通过@Query进行参数指定查询。

2.2. 条件查询

经常有一个需求,需要判断某一个字段存在时,在进行SQL查询。但是这种情况使用JPA会感觉非常麻烦。无论是用JPQL还是原生的SQL感觉很难比较好的实现。

上面提供了一个示例是判断开始时间和结束时间是否为空,在进行比较判断。利用的数据库的判空逻辑来进行实现,感觉非常不直观,也不利于SQL调试。

2.2.1. Mybatis-Flex

我们Mybatis-Flex中就非常优雅的可以实现。像写SQL一样非常直观的实现SQL的编写。

QueryWrapper query = QueryWrapper.create().where(EMPLOYEE.LAST_NAME.like(searchWord)) //条件为null时自动忽略.and(EMPLOYEE.GENDER.eq(1)).and(EMPLOYEE.AGE.gt(24));
List<Employee> employees = employeeMapper.selectListByQuery(query);

 

 

不需要判断条件,默认条件为空时,自动忽略。

2.3. 查询部分字段

这个又是JPA一个比较麻烦的点,JPA是以对象的形式来操作的SQL,所以每次都是查询出来全部的数据。有两种方式可以解决。

  • 查询出数据集,用List<Object[]>数组来接收,在转化成对应我们需要的部分数据。

  • 重新构建一个Model数据,里面只放置我们需要的数据字段,通过new Model(ParamA, ParamB)的方式来解决。

无论哪一种方式都会感觉非常别扭,只是一个很简单的需求。实现起来异常的麻烦。

2.3.1. Mybatis-Flex

在Mybatis-Flex中实现非常简单。使用select方法即可,不传参数值就是查询所有字段。可以定义自己想要查询的字段即可。

// 查询所有数据
QueryWrapper queryWrapper = QueryWrapper.create().select().where(ACCOUNT.AGE.eq(18));
Account account = accountMapper.selectOneByQuery(queryWrapper);// 查询部分字段,也可以使用Lambda表达式
select(QueryColumn("id"), QueryColumn("name"), QueryColumn("category_id"))

 

 

2.4. kotlin支持

我同步也有一个Kotlin的项目用的JPA,我也一起改成了flex版本。花了不多时间就改造完成,感觉代码整个都看起来非常优雅。

2.4.1. 分页查询
fun detailList(fairyDetailParam: FairyDetailParam): List<FairyDetail> {val paginateWith = paginate<FairyDetail>(pageNumber = fairyDetailParam.current,pageSize = fairyDetailParam.size,init = {select(QueryColumn("id"), QueryColumn("name"), QueryColumn("category_id"))whereWith {FairyDetail::categoryId eq fairyDetailParam.categoryId}})return paginateWith.records
}

 

 

定义分页对象和查询条件即可,init参数传入的是QueryScope,可以自由匹配需要查询的参数。flex有一个特别好的点是,写代码和写SQL的感觉保持一致性。先写select,再写where,orderBy这些。

// 无需注册Mapper与APT/KSP即可查询操作
val accountList: List<Account> = query {select(Account::id, Account::userName)whereWith {Account::age.isNotNull and (Account::age ge 17)}orderBy(-Account::id)
}

 

 
2.4.2. 更新用户
// 更新用户头像和昵称
update<User> {User::avatarUrl set user.avatarUrlwhereWith {User::openId eq user.openId}
}

 

 

更新用户头像地址,通过OpenId。写起来非常丝滑。

2.5. 代码自动生成

Gradle中需要添加annotationProcessor 'com.mybatis-flex:mybatis-flex-processor:1.9.3'注解来进行代码生成。Maven的用户可以自行在官网查询如何配置。

目前我做的是半自动的数据生成。先手动创建实体类,在通过实体类生成对应的操作对象。

2.5.1. 创建实体
package cn.db101.jcc.entity;import java.io.Serializable;
import java.util.Date;import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.annotation.Table;
import lombok.Data;/*** * @TableName t_banner*/
@Table("t_banner")
@Data
public class Banner {/*** */@Id(keyType = KeyType.Auto)private Integer id;/*** */private String url;/*** 排序,越小越靠前*/private Integer sort;/*** */private Date createTime;}

 

 
2.5.2. 编译项目

会在target或者build目录中生成对应的实体互操作类。

默认生成的实体类和字段都是大写来进行体现。

/*** 通过用户查询收藏列表* @param userId* @return*/
public List<Lineup> lineUpList(int userId) {// 查询收藏的IdList<Favorites> favoritesList = favoritesMapper.selectListByQuery(QueryWrapper.create().select(Favorites::getLineupId).from(Favorites.class).where(FAVORITES.USER_ID.eq(userId)));return lineupService.listFromId(favoritesList.stream().map(Favorites::getLineupId).collect(Collectors.toList()));
}/*** 删除收藏* @param favorites*/
public void deleteFavorites(Favorites favorites) {favoritesMapper.deleteByQuery(QueryWrapper.create().where(FAVORITES.USER_ID.eq(favorites.getUserId())).and(FAVORITES.LINEUP_ID.eq(favorites.getLineupId())));
}public Favorites selectOne(Favorites favorites) {return favoritesMapper.selectOneByQuery(QueryWrapper.create().where(FAVORITES.USER_ID.eq(favorites.getUserId())).and(FAVORITES.LINEUP_ID.eq(favorites.getLineupId())));
}

 

 
2.5.3. 全自动生成

通过连接数据库查询对应的表生成对应的实体,实体互操作 类,Mapper,Controller等。

public class Codegen {public static void main(String[] args) {//配置数据源HikariDataSource dataSource = new HikariDataSource();dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/your-database?characterEncoding=utf-8");dataSource.setUsername("root");dataSource.setPassword("******");//创建配置内容,两种风格都可以。GlobalConfig globalConfig = createGlobalConfigUseStyle1();//GlobalConfig globalConfig = createGlobalConfigUseStyle2();//通过 datasource 和 globalConfig 创建代码生成器Generator generator = new Generator(dataSource, globalConfig);//生成代码
        generator.generate();}public static GlobalConfig createGlobalConfigUseStyle1() {//创建配置内容GlobalConfig globalConfig = new GlobalConfig();//设置根包globalConfig.setBasePackage("com.test");//设置表前缀和只生成哪些表globalConfig.setTablePrefix("tb_");globalConfig.setGenerateTable("tb_account", "tb_account_session");//设置生成 entity 并启用 LombokglobalConfig.setEntityGenerateEnable(true);globalConfig.setEntityWithLombok(true);//设置项目的JDK版本,项目的JDK为14及以上时建议设置该项,小于14则可以不设置globalConfig.setJdkVersion(17);//设置生成 mapperglobalConfig.setMapperGenerateEnable(true);//可以单独配置某个列ColumnConfig columnConfig = new ColumnConfig();columnConfig.setColumnName("tenant_id");columnConfig.setLarge(true);columnConfig.setVersion(true);globalConfig.setColumnConfig("tb_account", columnConfig);return globalConfig;}public static GlobalConfig createGlobalConfigUseStyle2() {//创建配置内容GlobalConfig globalConfig = new GlobalConfig();//设置根包
        globalConfig.getPackageConfig().setBasePackage("com.test");//设置表前缀和只生成哪些表,setGenerateTable 未配置时,生成所有表
        globalConfig.getStrategyConfig().setTablePrefix("tb_").setGenerateTable("tb_account", "tb_account_session");//设置生成 entity 并启用 Lombok
        globalConfig.enableEntity().setWithLombok(true).setJdkVersion(17);//设置生成 mapper
        globalConfig.enableMapper();//可以单独配置某个列ColumnConfig columnConfig = new ColumnConfig();columnConfig.setColumnName("tenant_id");columnConfig.setLarge(true);columnConfig.setVersion(true);globalConfig.getStrategyConfig().setColumnConfig("tb_account", columnConfig);return globalConfig;}
}

 

 

Copy记录仅供自己下次使用方便

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/68314.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

学期(2024-2025-1) 学号20241425 《计算机基础与程序设计》第2周学习总结

学期(2024-2025-1) 学号20241425 《计算机基础与程序设计》第2周学习总结 作业信息这个作业属于哪个课程 <班级的链接>(2024-2025-1-计算机基础与程序设计)这个作业要求在哪里 <作业要求的链接>([2024-2025-1计算机基础与程序设计第二周作业]https://www.cnblo…

foobar2000 v2.1.6 汉化版

foobar2000 v2.1.6 汉化版 -----------------------【软件截图】---------------------- -----------------------【软件介绍】---------------------- foobar2000 是一个 Windows 平台下的高级音频播放器.包含完全支持 unicode 及支持播放增益的高级标签功能. 特色: * 支持的…

面相快速入门教程3面部与年龄

3 面部与年龄0 开始探索自己容貌的最简单方法之一,就是学会观察自己人生旅程的路线图--基于此时此刻的自己,每一个十年,甚至每一段岁月都是怎样的。 每个主要特征大约代表你人生中的一个十年。一个大的、强烈的或特别美丽的特征被认为意味着这些年总体上将是积极的。此外,这…

【VMware VCF】使用 SFTP 服务器备份 VCF 核心组件的配置文件。

可以定期对 VMware Cloud Foundation 环境中的相关核心组件(如 SDDC Manager、NSX Manager 以及 vCenter Server 等)创建配置备份,以防止当意外故障或数据丢失时,能够进行恢复。默认情况下,NSX Manager 组件的备份将创建并存储在 SDDC Manager 设备中内置的 SFTP 服务器上…

搭建Redis“主-从-从”模式集群并使用 RedisTemplate 实现读写分离

一、理论相关 我们知道,Redis具有高可靠性,其含义包括:数据尽量少丢失 - AOF 和 RDB 服务尽量少中断 - 增加副本冗余量,将一份数据同时保存在多个实例上,即主从库模式Redis主从库模式 - 保证数据副本的一致(读写分离):读操作:主库、从库都可以接收 写操作:首先到主库…

折腾笔记[2]-跨平台打包tauri程序

在macOS(arm64)平台打包tauri程序到Windows(amd64)平台. Packaging a Tauri application for the Windows (amd64) platform from macOS (arm64).摘要 在macOS(arm64)平台打包tauri程序到Windows(amd64)平台. Abstract Packaging a Tauri application for the Windows (amd64) …

博客格式-Markdown学习

标题 (#+空格+标题名字 一级标题) (##+空格+标题名字 二级标题) (###+空格+标题名字 三级标题) 字体 加粗 斜体 加粗斜体 划线 引用名人名言分割线图片超链接 百度 表格姓名 年龄 性别骆同学 15 男代码 int main(){cout<<"helloworld"return 0; }a,n=lis…