0

I have two entities:

@Entity
@Table(name = "Something")
public class SomethingEntity implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SOMETHING_SEQ")
    @SequenceGenerator(sequenceName = "SOMETHING_SEQ", allocationSize = 1, name = "SOMETHING_SEQ")
    @Column(name = "SOMETHING_ID", precision = 18, scale = 0, nullable = false)
    private Long somethingId;

    @Column(name = "SIMPLE_FIELD", length = 3, nullable = true)
    private String simpleField;

    @OneToOne(cascade = CascadeType.ALL, mappedBy = "somethingId")
    private OtherEntity other;
}

@Entity
@Table(name = "OTHER")
public class OtherEntity implements Serializable {
    @Id
    @OneToOne
    @JoinColumn(name = "SOMETHING_ID")
    private SomethingEntity somethingId;

    @Column(name = "OTHER_SIMPLE_FIELD", precision = 18, scale = 2, nullable = false)
    private Double otherSimpleField;
}

I want Spring-Data to fetch otherEntity eagerly. I know it doesn't work by default, so my repository method is:

@EntityGraph(attributePaths = {"other"})
List<SomethingEntity> findAll(Predicate predicate);

It doesn't work and my guess is, it's because of @OneToOne. I also tried JOIN FETCH, simple select by simpleField (predicate in my example is QueryDSL), nothing works.

The most surprising thing is that the initial SQL is actually correct - it contains correct joins and selects correct fields. Then Spring-Data is loading each @OneToOne relation by id anyway, causing 100 queries instead of 1.

Shadov
  • 5,061
  • 2
  • 16
  • 27
  • *I know it doesn't work by default*. Optional/Nullable `OneToOne` in Hibernate is always eager. https://stackoverflow.com/questions/1444227/making-a-onetoone-relation-lazy. – Alan Hay Feb 20 '20 at 09:25
  • Yes, and perhaps that's a problem. I load those relations myself, and then Hibernate not-knowing I loaded them, tries to load them manually one by one, probably because OneToOne is eager. I don't want one-by-one eager fetching of relations. – Shadov Feb 20 '20 at 11:00
  • If they are loaded and in the persistence context then Hibernate is not going to load them again. Your question needs more context.What do you mean by *I don't want one-by-one eager fetching of relations*? – Alan Hay Feb 20 '20 at 11:09

1 Answers1

0

I dont understand your use of @EntityGraph, try to use it in entity like this:

@NamedEntityGraphs({
    @NamedEntityGraph(name = "graph.SomethingEntity.OtherEntity", attributeNodes = @NamedAttributeNode("other"))
})
public class SomethingEntity implements Serializable {
[...]

And use the entity graph in your repository method like this:

EntityGraph graph = this.em.getEntityGraph("graph.SomethingEntity.OtherEntity");
Map hints = new HashMap();
hints.put("javax.persistence.fetchgraph", graph);//Important fetchgraph no other type
return entityManager.find(SomethingEntity.class,ID_OF_SOMETHING_ENTITY,  hints);

Another option is create a CriteriaQuery in yout repository method like this:

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<SomethingEntity> cq = cb.createQuery(SomethingEntity.class);

//Need to define variable root to apply fetch
Root<SomethingEntity> rootSE = cq.from(SomethingEntity.class);

/* Use metamodel (in eclipse, property project / JPA / Canonical metamodel
 * and select src folder in source to allow autogenerated metamodels)
 */
rootSE.fetch(SomethingEntity_.other);

// Set your predicates
cq.where(cb.equals(rootSE.get(SomethingEntity_.somethingId),ID_OF_SOMETHING_ENTITY));

cq.select(rootSE);

entityManager.createQuery(cq).getSingleResult();
JLazar0
  • 933
  • 1
  • 6
  • 19
  • This is exactly what I have, in Spring-Data that's how you do it. – Shadov Feb 20 '20 at 11:00
  • you might need the annotation @JoinColumn in SomethingEntity like this: @OneToOne(cascade = CascadeType.ALL, mappedBy = "somethingId") @JoinColumn(name="SOMETHING_ID") private OtherEntity other; – JLazar0 Feb 21 '20 at 06:50