作者jtorngl (木正同)
看板java
标题[问题] Spring Data JPA no Session问题
时间Fri May 6 12:54:33 2022
※问题类别: Spring Data JPA
※系统环境: bellsoft-jdk-11.0.14.1
很久没有使用 JPA,最近可能会用到
又回头看了 Spring Data JPA
一开始使用 Spring Boot 练习
第一问题通常会遇到 infinite recursion 的 JsonMappingException
所以使用 Jackson 的各种 annotation 或是转 DTO 来解
因为觉得 Spring Boot 背後做太多事,可能会错过什麽
所以改用 Spring 来练习一下
果不其然就发生了 LazyInitializationException - no Session
查了一下,Spring Boot 的 spring.jpa.open-in-view 预设为 true
让 entity 在脱离 transaction scope 时
还能使用 oiv 的 connection 去查 lazy entity 的资料
不过查资料时,是建议关闭这个功能,毕竟拉长占用 connection 的时间
而查到的几种解法
1. 把 @Transactional 设定在操作 lazy entity 的 method
因为 Spring Data JPA 执行流程,会导到 SimpleJpaRepository 来处理
而 SimpleJpaRepository 在 class 上宣告了 @Transactional(readOnly = true)
因为练习的 service 内的 method,可能会查询多次
不希望每个查询都使用一个 read only transaction
所以在 XxxService 的 class 也使用 @Transactional(readOnly = true)
希望多个查询使用同一个 read only transaction
所以我在 XxxService 的 class 上也使用 @Transactional(readOnly = true)
然後在执行 insert, update, delete 的 method 使用 @Transactional
https://imgur.com/2nVTBXH
但如果要使用 @Transactional 来解 no Session 的错误
那就变成要把 @Transactional 设定在 controller 的 method 了
这和 open-in-view 的做法似乎差别不大,只是用同一个 connection 而已?
2. 把 entity 关联设为 FetchType.EAGER
即使没用到,也强制查询关联的 table,应该也不会这样使用
3. Spring Boot 设定 hibernate.enable_lazy_load_no_trans=true
(Spring 的话,在设定 EntityManagerFactory 时,设定 jpa properties)
似乎也是 Hibernate 实做,另开个 Session 查询 lazy entity
-------------------------------------------------------------------
查到以上的做法都是不建议的
4. 避免使用双向关联
但是单向关联就会发生了
总不会 entity 都不要关联,每个 entity 自行操作?
5. 使用 JOIN FETCH
使用 @Query 自己下 JPQL
6. 使用 DTO
Spring Data JPA 的 projection 功能
虽然 interface based projection 有支援 nested projection
但是使用 @Query 时,JPQL 却不知怎麽下
例如有一个 Employee 的 entity,和关联的 Department
https://imgur.com/rebQiC7
projection 的 interface 这样宣告
https://imgur.com/pcB86Q6
JPQL 或 native SQL 都无法设值到 department 的 deptName
https://imgur.com/XRhI92i
除非把关联的 entity 内的 field 全部移到 Employee 内,只有一层的结构
而 class based projection 是另外定义 DTO class
但是不支援 nested projection
也就是如果我希望 DTO 能长这样,似乎是不可行的
https://imgur.com/EzAIeV6
而且 DTO 的 constructor 似乎要符合查询的 field 数目
我使用 overload 的 constructor 想用在多种查询
也遇过 no suitable constructor 之类的错误
以及 ConverterNotFoundException: No converter found ... 的错误
难不成不同的查询,就需要一个对应的 DTO 吗?!
6.1. 使用 library 把 entity 转成 DTO
也有看到使用 modelmapper 做 converter 的范例
不过这个范例我不知道为什麽可以成功
https://www.baeldung.com/entity-to-and-from-dto-for-a-java-spring-application
因为他的 service 和 controller 都没看到使用 @Transactional
也就是使用 SimpleJpaRepository 的 read only transaction
那在 controller 使用 ModelMapper 时,就会炸 no Session 了
我是把 ModelMapper 移到 service 内执行
因为 @Transactional 也都是挂在 service layer
6.2. 是否直接把 entity 做 deep copy 到结构完全一样的 DTO 就好了
还没测试,只是突然想到
以前在用 JPA 时,只是使用而没深入了解
没有发生 no Session 的错误
同样双向关联的 entity,同样是把 transaction 挂在 service layer
现在回想,当时在 render JSP 时,为什麽没问题呢?
後面还有 N + 1 的问题
目前查到是使用 FETCH JOIN,或是 JPA 2.1 的 @EntityGraph
Spring JDBC 用久了,现在觉得 Data JPA 用起来还挺不顺手
如果要直接操作 entity,就要处理 no Session 的问题
很多教学的范例,其 entity 都只是简单的 field,甚至没有关联的 entity
但实务上应该不会这样吧?
如果要操作 DTO,表示要大量使用 JPQL 或 Data JPA 的 query methods?
题外话
不知道 JOOQ 好不好用,Spring Boot 有看到
但似乎都是 Data JPA 和 MyBatis 较多
--
※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 111.249.65.177 (台湾)
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/java/M.1651812876.A.961.html
1F:→ ssccg: transaction又不是spring data jpa才有的东西,lazy关联不 05/06 14:30
2F:→ ssccg: 过就是个抽象化的query,不管是用jpa还是从底层driver自己 05/06 14:31
3F:→ ssccg: 写,都一样要自己决定这个query要在哪个transaction执行啊 05/06 14:32
4F:→ ssccg: 要像看起来一样方便就open-in-view,从上到下都用entity 05/06 14:35
5F:→ ssccg: 不然总是要自己决定transaction要包到哪,在这之上就不去乱 05/06 14:36
6F:→ ssccg: 用entity关联,而是明确重下一次query。而且包到哪应该是从 05/06 14:38
7F:→ ssccg: 资料是否需要transaction决定,不是api操作方不方便决定 05/06 14:40
8F:→ ssccg: 至於DTO,主要还是因为要用DTO才用,标志transaction分界只 05/06 14:48
感谢 s大说明
其实是自己在练习,把查到的方式都试一遍
然後想问实务上,使用哪种方式居多而已
当然实务也还是得看情况选择使用,没有标准答案
只是以前都只是 MVC + JSP 在使用
加上短暂使用 JPA,後来就长期使用 JDBC template
使用 JDBC template 其实就是一次查询
把 view 要用到的资料一次查回到 VO
重点在 SQL 的调教而已
现在会需要使用 Data JPA,使用 RESTful 回传 JSON
但是 JSON 的内容依 API 需要有各种内容
例如可能只有 Employee,也可能会需要有 Department
流量不大的情况,的确可以使用 open-in-view
直接把 entity output JSON 就好
然後去处理 N + 1 的 SQL 问题
只是想问看看,哪个方式处理会比较好
例如流量若较大,能尽早结束 transaction 关闭 connection
是否就是使用 Data JPA 的 projection 直接转 DTO
那就是要去解决,怎麽样能像 entity 那样结构的 DTO 而已
若要符合自己提问的情况,依照过去使用 JDBC template 的情况
会使用 DTO 来处理,只是 两种 projection 我都试不出符合 DTO 的结构
9F:→ ssccg: 是顺便而已 05/06 15:09
※ 编辑: jtorngl (111.249.65.177 台湾), 05/06/2022 15:31:03
10F:推 ntpuisbest: 关联很多实务上很多都是用jdbc template吧 05/06 18:45
11F:→ ssccg: 我个人DTO用mapstruct,transaction通常只开到我自己的 05/06 19:03
12F:→ ssccg: Repository层(在这里面才再看情用jpa/jdbc),出了这层就是 05/06 19:05
13F:→ ssccg: DTO,会分有载入关联和没有的API 05/06 19:07
14F:→ ssccg: 不过基本上这问题还是看你做的系统需求,就像你说的本来就 05/06 19:08
15F:→ ssccg: 是个很浅的data service就没差 05/06 19:10
16F:推 Jichang: 不用open session 可以用Hibernate.initialize 05/15 02:47
17F:→ Jichang: 也可以batch query 出来塞进去 05/15 02:48