JPA の EntityGraph は、@MappedSuperclass が考慮されていない


EntityGraph とは

JPA 2.1 で標準化された、Entity のフェッチ戦略として EAGER / LAZY を個別に設定できる EntityGraph。

EntityGraph<Employee> graph = em.createEntityGraph(Employee.class);
graph.addAttributeNodes(Employee_.salary, Employee_.address);

TypedQuery<Employee> query = em.createQuery("SELECT e FROM Employee e ...", graph);
query.setHint("javax.persistence.fetchgraph", graph);
List<Employee> results = query.getResultList();

ちなみに、ヒントの javax.persistence.loadgraph は、EntityGraph で未指定のものは Entity の事前定義に従い、javax.persistence.fetchgraph は、EntityGraph で未指定のものは LAZY 扱いとなる。

上記例では、salaryaddress が EAGERフェッチされる。

詳細は以下

blog1.mammb.com


@MappedSuperclass のフィールドは指定できない

問題となるのは @MappedSuperclass で定義された Entity

@MappedSuperclass
public abstract class BaseEntity {
    @Id
    private Long id;
}

@Entity
public class Employee extends BaseEntity {

    private Long salary;

    @OneToOne(fetch = FetchType.LAZY)
    private Address address;
}

以下のように親クラスのフィールドをグラフに追加できない。

EntityGraph<Employee> graph = em.createEntityGraph(Employee.class);
graph.addAttributeNodes(Employee_.id); // compile error !!

もちろん以下のようにはできるが、これでは意味がない。

EntityGraph<BaseEntity> graph = em.createEntityGraph(BaseEntity.class);
graph.addAttributeNodes(BaseEntity_.id);

この時のスタティックメタモデルは以下で、スタティックメタモデル間での継承関係は有る。

@StaticMetamodel(BaseEntity.class)
public abstract class BaseEntity_ {
    public static volatile SingularAttribute<BaseEntity, Long> id;
}

@StaticMetamodel(Employee.class)
public class Employee_ extends BaseEntity_ {
    public static volatile SingularAttribute<Employee, Long> salary;
    public static volatile SingularAttribute<Employee, address> address;
}


EntityGraph の API バグと回避法

原因は EntityGraph の API が以下の定義となっているため。

public interface EntityGraph<T> {

    /**
     * Add one or more attribute nodes to the entity graph.
     *
     * @param attribute  attribute
     * @throws IllegalStateException if the EntityGraph has been
     *         statically defined
     */
    public void addAttributeNodes(Attribute<T, ?> ... attribute);
}

以下のような定義であるべき。

public interface EntityGraph<T> {
    void addAttributeNodes(Attribute<? super T, ?>... attribute);
}

一応、課題に計上されているが、APIとして公開済みであり、修正は期待できそうにない。

https://github.com/eclipse-ee4j/jpa-api/issues/112

2023年11月追記

上記の誤ったAPIシグネチャはincorrect generic typing in operations of EntityGraphにて対応されました。

利用可能となるのは Jakarta Persistence 3.2 で、Jakarta EE 11 に含まれる予定です。


ワークアラウンドとして、型情報を無視して以下のようにすることは可能。

EntityGraph<Employee> graph = em.createEntityGraph(Employee.class);
Attribute attr = Employee_.id;
graph.addAttributeNodes(attr);