1

I have something like:

    @Entity
    public class Edge {
       @ManyToOne(fetch = FetchType.EAGER)
       @Fetch(value = FetchMode.JOIN)
       @JoinColumn(name = "start_node", referencedColumnName = "id")
       Node start;
       
       @ManyToOne(fetch = FetchType.EAGER)
       @Fetch(value = FetchMode.JOIN)
       @JoinColumn(name = "end_node", referencedColumnName = "id")
       Node end;
    }

Invoking the findAll() method provided by the JPA connector I would like to obtain a (single) query of the form:

SELECT * FROM EDGE edge, NODE start, NODE end WHERE edge.start_id = start.id AND edge.end_id = end.id

Instead, the framework first performs a single query to obtain all the edges, then performs multiple queries (one for edge, ideally) for the nodes. I am an hibernate-begginer. I think this is a very common question. I have found a lot of material related to the problem, but no proper answer.

1 Answers1

0

In short, JPA ignores Hibernate FetchMode.JOIN by default. The reason it does this is JPA fetches entities lazily (only when you need it, e.g. when you call getStart()). As a result, the application reserves resources to only when you need it.

Eager fetching is usually a bad choice and should be avoided, unless you really need to. I would suggest reading this thread as it lists much of the reading materials related to that.

What you could do is use @NamedEntityGraph to define eager-fetching of your entities. Here, you specify the attributes that you need to fetch eagerly (start and end). To read more about this, you can see here and here.

@Entity
@Table(name = "edge")
@NamedEntityGraph(name = "Edge.nodes", attributeNodes = {
        @NamedAttributeNode("start"),
        @NamedAttributeNode("end")
}) // This is the important annotation
public class Edge {
    @EmbeddedId
    private EdgeKey id = new EdgeKey();

    private int value;

    @ManyToOne
    @MapsId("startId")
    @JoinColumn(name = "start_id", referencedColumnName = "id")
    private Node start;

    @ManyToOne
    @MapsId("endId")
    @JoinColumn(name = "end_id", referencedColumnName = "id")
    private Node end;

    // getters, setters...
}

@Embeddable
public class EdgeKey implements Serializable {
    @Column(name = "start_id", nullable = false)
    Integer startId;

    @Column(name = "end_id", nullable = false)
    Integer endId;

    // getters, setters, equals and hash code...
}

Furthermore, you annotate your JPA repository method with @EntityGraph:

@Repository
public interface EdgeRepository extends JpaRepository<Edge, Integer> {
    @Override
    @EntityGraph(value = "Edge.nodes", type = EntityGraph.EntityGraphType.LOAD)
    List<Edge> findAll(); // You override the default implementation of findAll()

    @EntityGraph(value = "Edge.nodes", type = EntityGraph.EntityGraphType.LOAD)
    Edge getByValue(int value); // other example
}

This will result in a query that's similar to this:

SELECT edge0_.end_id AS end_id1_0_0_,
       edge0_.start_id AS start_id2_0_0_,
       node1_.id AS id1_1_1_,
       node2_.id AS id1_1_2_,
       edge0_.value AS value3_0_0_,
       node1_.value AS value2_1_1_,
       node2_.value AS value2_1_2_
FROM edge edge0_
LEFT OUTER JOIN node node1_ ON edge0_.start_id=node1_.id
LEFT OUTER JOIN node node2_ ON edge0_.end_id=node2_.id
Achmad Afriza
  • 115
  • 10