Abh*_*sun 4 java hibernate lazy-loading lazy-initialization spring-boot
我的 Spring Boot 项目中有一个客户和一个客户信息实体。他们有一对多的关系。
\n@Data\n@Builder\n@Entity\n@NoArgsConstructor\n@AllArgsConstructor\n@Table(name = "customer")\npublic class Customer implements Serializable{\n\n @Serial\n private static final long serialVersionUID = 1L;\n\n @Id\n @GeneratedValue(strategy = GenerationType.IDENTITY)\n private Long serialNumber;\n\n private Long customerId;\n private String name;\n\n\n\n @Column(name = "session_id", length = 128)\n private String sessionId;\n\n @JsonManagedReference("customer-customer_info")\n @OneToMany(targetEntity = CustomerInfo.class, mappedBy="Customer", cascade = CascadeType.ALL)\n private List<CustomerInfo> customerInfoList;\n\n}\n\n@Data\n@Builder\n@Entity\n@NoArgsConstructor\n@AllArgsConstructor\n@Table(name = "customer_info")\npublic class CustomerInfo implements Serializable{\n\n @Id\n @GeneratedValue(strategy = GenerationType.IDENTITY)\n private Long CustomerInfoId;\n\n @ManyToOne\n @JsonBackReference("customer-customer_info")\n @ToString.Exclude\n @JoinColumn(name="customer_session_id", nullable=false, referencedColumnName = "session_id")\n private Customer customer;\n\n private String metaKey;\n\n @Convert(converter = Encryptor.class)\n private String metaValue;\n}\nRun Code Online (Sandbox Code Playgroud)\n每当我尝试使用客户实体借助 getter 函数(customer.getCustomerInfo())来获取 customerInfo 时。抛出上述异常。我在很多地方都读到,该错误是由于 JPA 会话关闭并且不保留子实体而引起的。但是,我还没有从任何其他堆栈溢出答案中得到解决方案。
\n部分有效的解决方案,但并不理想:
\n遗憾的是,这个问题并没有在我的本地环境中复制。我不明白为什么?\n(我明白为什么我无法复制问题,我看到@RestController 方法默认情况下似乎是事务性的,为什么?并在我的应用程序中添加了 spring.jpa.open-in-view=false 。属性文件)
\n堆栈溢出中建议的解决方案并且我已经尝试过,
\nList<CustomerInfo> customerInfoList = customer.getCustomerInfoList();[这一行不会抛出错误,我有事件打印了 customerInfoList 的 className,它是 persistBag。下面的行抛出错误。]Hibernate.initialise(customerList)Spring 启动版本:2.6.2\nJava:17
\n有两个服务存在此问题:ConnectionServiceImpl 和 CompletionServiceImpl。发生问题的服务类别:
\n连接服务实现->
\n@Service(\xe2\x80\x9cConnectionServiceImpl\xe2\x80\x9d)\npublic class ConnectionServiceImpl implements ConnectionService {\n\n@Autowired\nprivate CompletionService completionService;\n\n@Override\npublic boolean test(){\n Request request = completionService.recordData(\xe2\x80\x9csession-id\xe2\x80\x9d);\n\n try {\n System.out.println(request.toString()); //this is working fine\n\n String str = GsonSerializer.getInstance().toJson(request); //this is throwing the exception\n System.out.println(str);\n }\n catch(Exception ex) {\n System.out.println(ex);\n//org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.test.entity.Customer.customerInfoList, could not initialize proxy - no Session\n }\n return true;\n}\n}\n\nRun Code Online (Sandbox Code Playgroud)\nCompletionServiceImpl->
\n@Service(\xe2\x80\x9cCompletionServiceImpl\xe2\x80\x9d)\npublic class CompletionServiceImpl implements CompletionService {\n\n@Autowired\nprivate CustomerInfoRepository customerInfoRepository;\n\n@Override\npublic Request recordData(String session){\n return prepareData(session);\n}\n\nprivate Request prepareData(String session){\n Request request = new Request();\n fillData(request, session);\n return request;\n}\n\nprivate void fillData(Request request, String session){\n Customer customer = customerRepository.findBySessionId(sessionId);\n request.setCustomerId(customer.getCustomerId());\n request.setData(parseResponse(customer, request));\n return ;\n}\n\nprivate List<CustomerInfo> parseResponse(Customer customer, Request request){\n List<CustomerInfo> customerInfoList = customerInfoRepository.findBySessionId(customer.getSessionId());\n CustomerInfo customerInfo = customerInfoList.stream()\n .filter(info -> info.getMetaKey().equalsIgnoreCase(KEY))\n .findFirst()\n .orElse(null);\n\n request.setKey(customerInfo.getMetaValue());\n return customerInfoList;\n}\n\n}\n\nRun Code Online (Sandbox Code Playgroud)\n当我在调试模式下运行代码时,我在断点处看到此错误,\n方法抛出 \'org.hibernate.LazyInitializationException\' 异常。无法评估 com.test.entity.Customer.toString()
\n导致整个问题的请求类Request序列化是:
\n\n@Data\npublic class Request implements Serializable {\n\n private Long customerId;\n private List<?> data;\n private String key;\n}\n\nRun Code Online (Sandbox Code Playgroud)\n我发现的唯一似乎是一个昂贵的操作的解决方案是通过 BeanUtils 将数据 customerInfoList 转换为 InfoDao,以便将 CustomerInfoList 的所有字段复制到 InfoDao(没有 Customer 字段)。请提出更好的解决方案。
\nprivate List<InfoDao> convertToArray(List<?> list){\n List<InfoDao> list1 = new ArrayList<>();\n for(Object v:list){\n InfoDao infoDao = new InfoDao();\n BeanUtils.copyProperties(v,infoDao);\n list1.add(infoDao);\n }\n return list1;\n}\nRun Code Online (Sandbox Code Playgroud)\nInfoDao 与 CustomerInfo 具有相同的架构,但没有 Customer 引用。
\n@Data\n@Builder\n@NoArgsConstructor\n@AllArgsConstructor\npublic class InfoDao implements Serializable{\n\n private Long CustomerInfoId;\n\n private String metaKey;\n\n private String metaValue;\n}\nRun Code Online (Sandbox Code Playgroud)\n
正如您已经发现的,问题在于事务会话已关闭,并且当您调用 getCustomerInfoList() 时,您将收到 LazyInitializationException。
@EntityGraph("customerInfoList"),该方法将在 db 语句中包含一个联接,以便在调用 findBySessionId 时始终获取 customerInfoList。(与 EAGER 相反,EAGER 会执行单独的查询来获取数据)| 归档时间: |
|
| 查看次数: |
26427 次 |
| 最近记录: |