【问题标题】:JPA Lazy loading is not working in Spring bootJPA延迟加载在Spring引导中不起作用
【发布时间】:2019-09-06 05:31:22
【问题描述】:

我用谷歌搜索了很多,Spring Boot(最新版本)可能没有延迟加载不起作用,这真的很奇怪。以下是我的代码片段:

我的资源:

 public ResponseEntity<Page<AirWaybill>> searchAirWaybill(CriteraDto criteriaDto, @PageableDefault(size = 10) Pageable pageable{
airWaybillService.searchAirWaybill(criteriaDto, pageable);
        return ResponseEntity.ok().body(result);
}

我的服务:

@Service
@Transactional
public class AirWaybillService {

//Methods

 public Page<AirWaybill> searchAirWaybill(AirWaybillCriteriaDto searchCriteria, Pageable pageable){
    //Construct the specification
            return airWaybillRepository.findAll(spec, pageable);
   }
}

我的实体:

@Entity
@Table(name = "TRACKING_AIR_WAYBILL")
@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="@airWaybillId") //to fix Infinite recursion with LoadedAirWaybill class
public class AirWaybill{
//Some attributes
    @NotNull
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "FK_TRACKING_CORPORATE_BRANCH_ID")
    private CorporateBranch corporateBranch;
}

在调试时,我仍然会加载所有延迟加载的属性。见下图。

我的一个问题是杰克逊会参与这种行为吗? 有没有我可能错过的激活延迟加载的方法?

编辑

另一个问题,调试器会不会参与破坏延迟加载?

编辑 2:

对于规范构建,我有:

public static Specification<AirWaybill> isBranchAirWayBill(long id){
    return new Specification<AirWaybill>() {
        @Override
        public Predicate toPredicate(Root<AirWaybill> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
            return cb.equal(root.join("corporateBranch",JoinType.LEFT).get("id"),id);
        }
    };
}

【问题讨论】:

  • 事务配置在哪里?在资源或服务上?
  • 在服务上。我更新了问题
  • 您的标准对corporateBranch有任何条件吗?如果是这样,您如何构建传递给存储库的规范?
  • 是的,但有条件,甚至我删除了规范并只加载了一个页面,但我仍然看到问题
  • 只是放弃调试器相关的问题,启用 Spring Boot SQL 查询日志记录:stackoverflow.com/questions/30118683/…

标签: java spring hibernate spring-boot spring-data-jpa


【解决方案1】:

另一个考虑是在使用 Lombok 时,@Data/@Getter 注解会导致加载惰性项而不需要。所以在使用 Lombok 时要小心。

这是我的情况。

【讨论】:

    【解决方案2】:

    Spring 数据 JPA 也遇到了同样的问题。我添加了以下注释并能够获取给定 ORDER ID 的客户记录

    客户订购:一对多

    给客户的订单是延迟加载。

    Order.java

    @ManyToOne(cascade = CascadeType.ALL,targetEntity = CustomerEntity.class,fetch = FetchType.LAZY)
    @Fetch(FetchMode. JOIN)
    @JoinColumn(name = "CUSTOMER_ID",referencedColumnName = "CUSTOMER_ID",insertable = false,updatable = false)
    @LazyToOne(LazyToOneOption.PROXY)
    Private CustomerEntity customer
    

    Customer.java

    @Entity
    @TabLe(name = "CUSTOMER" ,
    uniqueConstraints = @UniqueConstraint(columnNames= {"mobile"}))
    public class CustomerEntity {
    
    @GeneratedVaLue(strategy = GenerationType.IDENTITY)
    @CoLumn(name = "customer_id" )
    private Integer customerld;
    private String name;
    private String address;
    private String city;
    private String state;
    private Integer zipCode;
    private Integer mobileNumber;
    
    @OneToMany(mappedBy = " customer" )
    @Fetch(FetchMode.JOIN)
    @LazyToOne(LazyToOneOption.PROXY)
    private List<OrderEntity> orders;
    }
    

    【讨论】:

    • 请将代码和数据添加为文本 (using code formatting),而不是图像。图片:A)不允许我们复制粘贴代码/错误/数据进行测试; B) 不允许根据代码/错误/数据内容进行搜索;和many more reasons。除了代码格式的文本之外,只有在图像添加了一些重要的东西,而不仅仅是文本代码/错误/数据传达的内容时,才应该使用图像。
    • 谁能解释一下这个注释。在文档中,没有正确解释。 baeldung.com/hibernate-lazy-loading-workaround 阅读本文后,似乎 @Transactional 更好,但是如果我们添加“enable_lazy_load_no_trans”,那么它将在每个对象一个事务中调用对象列表和每个对象每个引用的惰性对象。 @LazyToOne 是否也将做一次一笔交易,还是总共只额外一笔交易?
    【解决方案3】:

    您可以通过jackson-datatype-hibernate 的两个步骤解决此问题:

    kotlin 示例

    1. 添加build.gradle.kts:
    implementation("com.fasterxml.jackson.datatype:jackson-datatype-hibernate5:$jacksonHibernate")
    
    1. 创建@Bean
       @Bean
       fun hibernate5Module(): Module = Hibernate5Module()
    

    注意Module 是com.fasterxml.jackson.databind.Module,而不是java.util.Module

    【讨论】:

      【解决方案4】:

      休眠会话存在于带有@Transactional 的方法中。 在 Service 类之外传递实体是一种不好的做法,因为在离开您的 search 方法后会话将被关闭。另一方面,您的实体包含惰性初始化集合,一旦会话关闭就无法拉取。

      良好的做法是将实体映射到传输对象并从服务返回这些传输对象(不是原始实体)。

      【讨论】:

      • 精确简洁易懂
      • 对不起,我不明白。您在某处是否有示例显示此运输设计是如何设置的?提前谢谢你
      • 请创建简单的 POJO(比如说 AirWaybillTO 并将您需要的所有属性放在那里,然后从 AirWaybill 映射它们并从您的服务返回 AirWaybillTO。
      【解决方案5】:

      检索到的数据已经是惰性的,但是您正在使用调试模式,当点击查看来自调试器的数据时,它的返回值。

      【讨论】:

      • 在cmets中添加这样的答案。
      【解决方案6】:

      SpringBoot 默认已启用:
      spring.jpa.open-in-view = true
      这意味着交易始终是开放的。尝试禁用它。
      更多信息here

      【讨论】:

      • 很好的帮助,我现在收到org.hibernate.LazyInitializationException: could not initialize proxy - no Session。我尝试安装Hibernate5Module,但还没有工作,仍然是同样的错误
      • 您可以使用@Transactional 解决此问题。请参阅 Kowalski 的回答。
      【解决方案7】:

      只是猜测:您在构建规范时强制获取。

      我期待类似的东西

      static Specification<AirWaybill> buildSpec() {
          return (root, query, criteriaBuilder) -> {
             Join<AirWaybill, CorporateBranch> br = (Join) root.fetch("corporateBranch");
             return criteriaBuilder.equal(br.get("addressType"), 1);
          };
      }
      

      如果是这种情况,请尝试将root.fetch 更改为root.join

      【讨论】:

      • 感谢您的通知,请参阅上面的更新
      【解决方案8】:

      很可能您仍在服务内部进行调试,因此当事务仍处于活动状态时,可以触发延迟加载(在延迟元素上调用的任何方法都会触发从数据库中获取)。

      问题是在事务之外不能发生延迟加载。杰克逊正在解析你的实体,绝对超出了一个边界。

      您应该在构建规范时获取所有必需的依赖项,或者尝试在资源级别使用 @Transactional(但在最后的情况下尝试)。

      请注意,LAZY 获取策略只是一个提示.. 不是强制性操作。渴望是强制性的:

      LAZY 策略是对持久性提供程序运行时的提示, 首次访问数据时应延迟获取数据。这 允许实现急切地获取 LAZY 的数据 已指定策略提示。

      【讨论】:

      • 即使我在浏览器中看到响应,我也看到所有延迟加载的属性都已加载(我不喜欢这样做)。所以调试器与加载的孩子无关
      • 检查我的更新。
      【解决方案9】:

      使用调试器时,您会尝试访问变量的值。因此,在您单击屏幕上的那个小箭头的那一刻,相关变量的值已(懒惰地)加载。

      【讨论】:

      • 谢谢。但即使在我点击箭头之前,我在该属性的行中看到了已经包含信息的行
      • IDE 处理调试的方式不同。 InteliJ 会提前为您加载所有内容,但其他 IDE 可能不会。
      猜你喜欢
      • 2022-01-09
      • 1970-01-01
      • 1970-01-01
      • 2015-02-24
      • 2018-07-14
      • 1970-01-01
      • 2016-09-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多