Spring Data Jpa 有更好的映射 SQL 语句的方法吗?

2018-08-20 10:11:39 +08:00
 abcbuzhiming
最近研究了一下 Spring Data Jpa 的文档。发现它的 ORM 方式似乎只有两种:
1 Dao 层的接口,接口方法名以驼峰的方式分割成关键词,映射为 SQL 的语句
2 通过 @query 注解手动编写类 sql 语句

我觉得这两种方式,灵活度都很一般。假设一个表里有 3 个字段,我分别有三个地方用到了这个表,但是是分别的查询 3 个字段。这在 dao 层里得写 3 个接口。这实在是有点累赘,很多 orm 工具都是直接提供条件字段映射类的,不需要从方法名上去映射,灵活的多,一个方法能搞定任何字段的查询。

是我打开 spring data jpa 的方式不对还是就只能这样?
4896 次点击
所在节点    Java
22 条回复
richard1122
2018-08-20 10:18:44 +08:00
qinxi
2018-08-20 10:19:25 +08:00
试试 JpaSpecificationExecutor 或者 QueryDSL
lhx2008
2018-08-20 10:31:23 +08:00
java 写接口语义化不是挺好,如果想不用接口的 orm,可以用 jooq
u21t20o15
2018-08-20 10:36:39 +08:00
我同事用 Freemark 类似 MyBatis 的方式去做映射,一般需要很复杂的 SQL 才那样写
linbiaye
2018-08-20 10:46:21 +08:00
老哥,你的这个头像没穿 bra?
swim2sun
2018-08-20 10:59:38 +08:00
letitbesqzr
2018-08-20 11:13:02 +08:00
QueryDSL +1 .. 我们引入了 spring data jpa ..但是 所有查询 基础接口 都封装自 QueryDSL,只是某些需要反射来排序 是用的 jpa
CtrlSpace
2018-08-20 11:29:30 +08:00
写几个接口而已,不算累赘吧。
abcbuzhiming
2018-08-20 11:29:52 +08:00
@richard1122 首先谢谢,其次这个实现的还是过于简单。它只能附加字段条件,对注入 limit,group,order 就无能为力,而且无法嵌套,不过你这个提醒了我,我发现我文档看的不仔细,这部分文档上有,但是我看漏了

@qinxi 感谢,我在文档上找到这两个了,QueryDSL 比较理想。但是 JpaSpecificationExecutor。。。怎么会有搞的如此复杂的实现?它有没有什么特长?比如 QueryDSL 做不了它能做的事情?光看功能他做的事情和 QueryDSL 差不多,但是 QueryDSL 比它直观和简单的多了


@letitbesqzr 请教有啥是 QueryDSL 实现不了的?现在选型比较关注框架的弱点方面
abcbuzhiming
2018-08-20 11:31:27 +08:00
@CtrlSpace 人的追求是无止境的,你用过 mybatis-plus 就明白了,如果你用过一些动态语言的 ORM 工具比如 ruby 的,你更会明白 java 的这些 ORM 工具局限在哪里
skypyb
2018-08-20 12:50:36 +08:00
注入 entitymanager 自个写 SQL 啊...
我遇到自带的注解解决不了的一般就自己手动写了。写 HQL 还是原生 SQL 都可以自己定
rim99
2018-08-20 13:08:19 +08:00
建议看下文档吧,记得有工厂方法可以传入类对象,定制返回类型的
passerbytiny
2018-08-20 14:58:58 +08:00
首先纠正楼主两点:
一、Hibernate、Jpa 和基于二者的 Spring Data Jpa,都没有“ Dao ”这个概念。
二、既然用了 Spring Data Jpa,那么除非是是否旧的数据库,不应该考虑表、字段等任何数据库方便的概念。

其次,楼主第二段的需求,我没看看明白。

Spring Data Jpa 只是 Hibernate 的一个上层封装,通过约定加自动生成的方式,简化 Jpa/Hibernate 的编码。它本身并不是 ORM,也没有对使用方式做任何强制限制(你可以自由的添加自定义实现)。
abcbuzhiming
2018-08-20 15:13:17 +08:00
@passerbytiny
好吧,严谨一点说,spring data jpa 用 repository 来承担了 dao(或者叫 mapper)层的功能

我没看懂“不应该考虑表、字段等任何数据库方便的概念”这句话,你不从数据出发如何建模呢?或者说你觉得正确的思考方式是什么样的呢?

我第二段的需求,是希望能追求灵活性,举个例子,User 对象(表),在场景 1 下需要搜索 userName 字段,到场景 B 需要检索 nickName 字段,按照 spring data jpa 的文档,正统的做法你得在 UserRepository 准备两个 find 方法
findByUserName(String userName)
findByNickName(String nickName)

这在我看来是比较麻烦的,因为很多其他 Orm 工具提交这样的做法,以下是伪代码:
DYSQL dySQL = new DYSQL() //动态 sql 条件查询类生成
dySQL.eq("列名",列值)..eq("列名",列值).groupy("XXXX").Order("XXX");
select(dySQL);
嗯,其实就是上面提到的 QueryDSL
WispZhan
2018-08-20 15:55:35 +08:00
@abcbuzhiming 看看 ddd。这些概念是 ddd 的
passerbytiny
2018-08-20 16:11:11 +08:00
@abcbuzhiming Repository 不是 Dao,贫血领域模型用 Dao,常规领域模型用 Repository,它俩永远不会一起使用。

领域驱动设计中的“存储库”的含义是:对象集合以及该集合的读写行为,形象一店的说,就是对象的仓库和仓库管理员。Spring 核心给的 Repository 的定义是:表示“存储库”,若是传统 Java EE 模式(这是好听的说法,说不好停的就是老旧的贫血模型模式),也可以表示 Dao。

下面是 spring core 中 @Repository 的 javadoc 和谷歌翻译
https://docs.spring.io/spring/docs/5.0.8.RELEASE/javadoc-api/org/springframework/stereotype/Repository.html

表示带注释的类是“存储库”,最初由域驱动设计( Evans,2003 )定义为“用于封装模拟对象集合的存储,检索和搜索行为的机制”。
实现传统 Java EE 模式(如“数据访问对象”)的团队也可以将此构造型应用于 DAO 类,但在此之前应注意理解数据访问对象和 DDD 样式存储库之间的区别。这个注释是一个通用的刻板印象,个别团队可能会缩小其语义并在适当时使用。
如此注释的类 DataAccessException 在与 a 一起使用时有资格进行 Spring 翻译 PersistenceExceptionTranslationPostProcessor。带注释的类还阐明了它在整个应用程序体系结构中的作用,以用于工具,方面等。
从 Spring 2.5 开始,这个注释也可以作为一个特化 @Component,允许通过类路径扫描自动检测实现类。
passerbytiny
2018-08-20 16:17:39 +08:00
@abcbuzhiming “不应该考虑表、字段等任何数据库方便的概念”,是领域驱动设计的要求。领域驱动设计中,要求完全按照面向对象的方式去建模,数据库的部分由 Jpa 自动去处理。形象一点的说是:引入了 JPA 的实现( Hibernate 等)后,开发人员只需要设计 Java 类,数据库部分(包括结构和读写 SQL )由 JPA 自动处理。
passerbytiny
2018-08-20 16:21:48 +08:00
@abcbuzhiming 你的这个需求,“场景 1 按照方式一搜索、场景 2 按照方式二搜索”是业务逻辑,不是 Dao/Repository 该干的事。

在传统模式中,Dao 层提供 findByUserName(String userName) findByNickName(String nickName)两个方法,Service 层决定调用哪个方法。

在 DDD 模式中,Repository 提供两个检索方法,领域服务或者应用服务决定调用哪个方法。
passerbytiny
2018-08-20 16:34:39 +08:00
补一下 DDD 的软件架构。
简单分层架构:由下到上依次是:模型层(含 Entity/Aggregation、Value Object、Repository )、Domain Service 层、Application Service 层。Domain Service 层是可选的,并且它仅仅是在调用关系上处于上层,业务关系上它跟模型层是平级的。

依赖倒置架构:由下到上依次是:领域核心层( Entity/Aggregation、Value Object、Domain Service、Repository 接口部分)、Application Service 层、基础设施层( JPA、Hibernate、数据库、Domain Service 技术实现部分……)。此时的分层只表示业务依赖关系,与调用关系无关了。

还有更复杂的架构。所有架构中都没有“ Dao ”,数据库也从来不会参与建模。
wancaibida
2018-08-20 18:21:05 +08:00
用 grails gorm

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/481325

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX