使用Spring Data JPA轻松获取 [英] Lasy Fetch with Spring Data JPA

查看:139
本文介绍了使用Spring Data JPA轻松获取的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我创建了一对一的关系,我想拥有一些方法来获取相同的实体(Distributor.class),但是其中一个方法却可以进行懒惰的获取,而其他人则很渴望.

I create one-to-one relationship and I want to have methods that fetch the same entity(Distributor.class) but one does lazy fetch, other eager.

@Entity
@Table
public class Distributor {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
    private String site;

    @OneToOne(
            mappedBy = "distributor",
            cascade = CascadeType.ALL,
            orphanRemoval = true,
            fetch = FetchType.LAZY,
            optional = false
    )
    private Location location;

    public void setLocation(Location repositoryLocation) {
        if (repositoryLocation == null) {
            if (this.location != null) {
                this.location.setDataProvider(null);
            }
        }
        else {
            repositoryLocation.setDataProvider(this);
        }
        this.location = repositoryLocation;

    }

// getters/setters/constructor

    }

}

@Entity
@Table(name = "location")
public class Location {

    @Id
    private Long id;

    @Column(name = "country_code")
    private String countryCode;

    private Double longitude;

    private Double latitude;

    @OneToOne(fetch = FetchType.LAZY)
    @MapsId
    @JoinColumn(name = "id")
    private Distributor distributor;

    public Distributor getDistributor() {
        return distributor;
    }

    public void setDistributor(Distributor distributor) {
        this.distributor = distributor;
    }
// other getters/setters
}

我遇到的最大问题是,似乎Spring Data JPA忽略了FetchType并急切地获取所有相关表(基于 @NamedEntityGraph :

Biggest problem I encounter is that it seems that Spring Data JPA ignores FetchType and fetches all related tables eagerly(based on related threads and Spring Data JPA docs). All Spring Data Repository methods that get data using Distributor.class fetch Location eagerly in two selects, from Distributor and Location. By using @NamedEntityGraph as such:

@Entity
@Table
@NamedEntityGraph(
        name = "distributor-entity-graph",
        attributeNodes = {
                @NamedAttributeNode("location"),
        }
)
public class Distributor {

//spring data jpa methods
  @EntityGraph(value = "distributor-entity-graph", type = EntityGraph.EntityGraphType.LOAD)
    Optional<Distributor> findById(Long id);

如果使用这样的图形,我会在Location上获得单个左外部联接,尽管这是一种更好的热切负载,但仍然是热切负载.

I get single left outer join on Location if use such a graph and though it's a better type of eager load this is still eager load.

一切都已经找到诸如错误的解决方法. 是否有某种或多或少的方法可以做到这一点,或者只是不创建关系而是按需获取Location更好(就性能而言)?渴望的负载是一种气味,但是当我获取单个分发服务器时,在大多数情况下我只想这样做,但是在某些情况下,我不想这样做,尤其是当我找到findAll()时.

Everything I have found so far seems like bad workarounds. Is there some more or less neat way of doing this or maybe is it better(in terms of performance first of all) just to not create the relationship and just fetch Location on demand? Eager load is a smell but when I fetch single distributor I want to do just that in most of the cases, but in some cases I don't and especially if I do findAll().

推荐答案

在设置了mappedBy(或使用JoinColumn并且是可选的),因为它需要知道何时使用null或proxy初始化字段.

Hibernate ignores LAZY on the parent-side of every bi-directional one-to-one mapping that has mappedBy set (or uses JoinColumn and is optional), because it needs to know whenever to initialize the field with null or proxy.

您的问题是,即使您在子(Location)上使用MapsId,父级仍然使用mappedBy.

Your problem is, even though you use MapsId on the child (Location), the parent still uses mappedBy.

这是实现延迟加载的双向关联的方法(尽管它有缺点,但请阅读到最后).

Here is how you could achieve lazy loaded bi-directional association (it has it's downsides though, read till the end).

父母

@Entity
public class TestParent {

  @Id
  @GeneratedValue(strategy = IDENTITY)
  private Long id;

  @OneToOne(fetch = FetchType.LAZY, optional = false)
  @JoinColumn(name = "id")
  private TestChild child;
}

  • child字段不是必填字段,它有点hacky,您可以忽略它,并在需要时使用存储库获取子对象-您知道它的ID
  • 由于这不是标准的父级到子级映射,因此您无法在child字段上指定任何级联(这将反映您持久化它们的方式,因此请阅读至最后) ,否则持久将失败,因为它无法将ID分配给子实体.
  • optional = false是强制性的,否则无论如何都会急切地获取它.
    • child field is not mandatory, it is a bit hacky and you could just ignore it and fetch the child when needed using the repository - you know it's id
    • Because this is not standard parent-to-child mapping, you cannot specify any cascades on the child field (which will reflect on the way you persist them, so read till the end), otherwise the persist will fail because it won't be able to assign id to the child entity.
    • optional = false is mandatory, otherwise it will be eagerly fetched anyway.
    • 孩子

      public class TestChild {
      
        @Id
        private Long id;
      
        @OneToOne(fetch = FetchType.LAZY)
        @MapsId
        @JoinColumn(name = "id")
        private TestParent parent;
      }
      

      这是您定义孩子的方式,没有什么重要的改变.

      This is how you defined your child, nothing important has changed.

      坚持

      EntityManager em = entityManagerFactory.createEntityManager();
      em.getTransaction().begin();
      
      TestParent parent = new TestParent();
      TestChild child = new TestChild();
      parent.setChild(child);
      child.setParent(parent);
      
      em.persist(parent);
      em.persist(child);
      
      em.getTransaction().commit();
      em.close();
      

      由于父级不再级联到子级实体,因此您必须单独保留子级.

      Since the parent no longer cascades to the child entity, you have to persist the child separately.

      阅读更多内容.

      这篇关于使用Spring Data JPA轻松获取的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆