JPA实体图
JPA 2.1的最新功能之一是能夠使用實(shí)體圖指定獲取計(jì)劃。 這很有用,因?yàn)樗试S您自定義使用查詢或查找操作檢索的數(shù)據(jù)。 當(dāng)使用中型到大型應(yīng)用程序時(shí),通常以不同的方式顯示來自同一實(shí)體的數(shù)據(jù)。 在其他情況下,您只想選擇最小的信息集即可優(yōu)化應(yīng)用程序的性能。
您沒有很多機(jī)制可以控制JPA實(shí)體中加載或不加載的內(nèi)容。 您可以使用EAGER / LAZY提取,但是這些定義幾乎是靜態(tài)的。 您在檢索數(shù)據(jù)時(shí)無法更改其行為,這意味著您受制于實(shí)體中定義的內(nèi)容。 在開發(fā)中更改這些內(nèi)容是一場噩夢,因?yàn)檫@可能導(dǎo)致查詢行為異常。 控制加載的另一種方法是編寫特定的JPQL查詢。 通常,您最終會(huì)得到非常相似的查詢和以下方法: findEntityWithX , findEntityWithY , findEntityWithXandY等。
在JPA 2.1之前,實(shí)現(xiàn)已經(jīng)支持類似于實(shí)體圖的非標(biāo)準(zhǔn)方式來加載數(shù)據(jù)。 您具有Hibernate Fetch Profiles , OpenJPA Fetch Groups和EclipseLink Fetch Groups 。 在規(guī)范中具有這種行為是合乎邏輯的。 它允許您對使用標(biāo)準(zhǔn)API加載的內(nèi)容進(jìn)行更精細(xì),更詳細(xì)的控制。
例
考慮以下實(shí)體圖:
(關(guān)系應(yīng)該是N到N,但讓它保持簡單)。
電影實(shí)體具有以下定義:
電影.java
@Entity @Table(name = "MOVIE_ENTITY_GRAPH") @NamedQueries({@NamedQuery(name = "Movie.findAll", query = "SELECT m FROM Movie m") }) @NamedEntityGraphs({@NamedEntityGraph(name = "movieWithActors",attributeNodes = {@NamedAttributeNode("movieActors")}),@NamedEntityGraph(name = "movieWithActorsAndAwards",attributeNodes = {@NamedAttributeNode(value = "movieActors", subgraph = "movieActorsGraph")},subgraphs = {@NamedSubgraph(name = "movieActorsGraph",attributeNodes = {@NamedAttributeNode("movieActorAwards")})}) }) public class Movie implements Serializable {@Idprivate Integer id;@NotNull@Size(max = 50)private String name;@OneToMany@JoinColumn(name = "ID")private Set<MovieActor> movieActors;@OneToMany(fetch = FetchType.EAGER)@JoinColumn(name = "ID")private Set<MovieDirector> movieDirectors;@OneToMany@JoinColumn(name = "ID")private Set<MovieAward> movieAwards; }靠近實(shí)體,我們可以看到我們有3個(gè)1到N的關(guān)系,并且將movieDirectors設(shè)置為渴望加載。 其他關(guān)系設(shè)置為默認(rèn)的延遲加載策略。 如果要更改此行為,可以使用批注@NamedEntityGraph定義不同的加載模型。 只需設(shè)置一個(gè)名稱即可識別它,然后使用@NamedAttributeNode指定要加載的根實(shí)體的哪些屬性。 對于關(guān)系,您需要為子圖設(shè)置一個(gè)名稱,然后使用@NamedSubgraph 。 詳細(xì):
注解
實(shí)體圖movieWithActors
@NamedEntityGraph(name = "movieWithActors",attributeNodes = {@NamedAttributeNode("movieActors")}) )這將定義一個(gè)名稱為movieWithActors的實(shí)體圖,并指定應(yīng)加載關(guān)系movieActors 。
實(shí)體圖movieWithActorsAndAwards
@NamedEntityGraph(name = "movieWithActorsAndAwards",attributeNodes = {@NamedAttributeNode(value = "movieActors", subgraph = "movieActorsGraph")},subgraphs = {@NamedSubgraph(name = "movieActorsGraph",attributeNodes = {@NamedAttributeNode("movieActorAwards")})})這將定義一個(gè)名稱為movieWithActorsAndAwards的實(shí)體圖,并指定應(yīng)加載關(guān)系movieActors 。 此外,它還指定關(guān)系movieActors應(yīng)該加載movieActorAwards 。
請注意,我們沒有在實(shí)體圖中指定id屬性。 這是因?yàn)闊o論指定什么內(nèi)容,總是會(huì)獲取主鍵。 版本屬性也是如此。
提示
要使用查詢中定義的實(shí)體圖,您需要將它們設(shè)置為提示。 您可以使用兩個(gè)提示屬性,它們也會(huì)影響數(shù)據(jù)的加載方式。
您可以使用javax.persistence.fetchgraph ,此提示會(huì)將Entity Graph中所有指定的屬性視為FetchType.EAGER 。 未指定的屬性被視為FetchType.LAZY 。
另一個(gè)屬性提示是javax.persistence.loadgraph 。 這會(huì)將Entity Graph中所有指定的屬性視為FetchType.EAGER 。 未指定的屬性將被視為其指定的或默認(rèn)的FetchType 。
為了簡化,并基于我們的示例,當(dāng)應(yīng)用實(shí)體圖movieWithActors :
| 電影演員 | 懶 | 急于 | 急于 |
| 電影導(dǎo)演 | 急于 | 懶 | 急于 |
| 電影獎(jiǎng) | 懶 | 懶 | 懶 |
從理論上講,這應(yīng)該是獲取不同關(guān)系的方式。 實(shí)際上,它可能無法通過這種方式工作,因?yàn)镴PA 2.1規(guī)范還指出,JPA提供程序始終可以獲取除實(shí)體圖中指定的狀態(tài)之外的其他狀態(tài)。 這是因?yàn)樘峁┏绦蚩梢詢?yōu)化要獲取的數(shù)據(jù)并最終加載更多的數(shù)據(jù)。 您需要檢查提供商的行為。 例如,即使使用javax.persistence.fetchgraph提示,Hibernate始終會(huì)獲取指定為EAGER的所有內(nèi)容。 在此處檢查問題。
詢問
執(zhí)行查詢很容易。 您可以setHint進(jìn)行操作,但是只需對Query對象調(diào)用setHint :
提示實(shí)體圖
@PersistenceContextprivate EntityManager entityManager;public List<Movie> listMovies(String hint, String graphName) {return entityManager.createNamedQuery("Movie.findAll").setHint(hint, entityManager.getEntityGraph(graphName)).getResultList();}要獲取要在查詢中使用的實(shí)體圖,您需要在EntityManager上調(diào)用getEntityGraph方法并傳遞名稱。 然后在提示中使用參考。 提示必須是javax.persistence.fetchgraph或javax.persistence.loadgraph 。
程式化
注釋可能變得冗長,尤其是當(dāng)您有大圖或許多實(shí)體圖時(shí)。 您可以以編程方式定義實(shí)體圖,而不必使用注釋。 讓我們看看如何:
首先添加一個(gè)靜態(tài)元模型實(shí)體類:
Movie_.java
@StaticMetamodel(Movie.class) public abstract class Movie_ {public static volatile SingularAttribute<Movie, Integer> id;public static volatile SetAttribute<Movie, MovieAward> movieAwards;public static volatile SingularAttribute<Movie, String> name;public static volatile SetAttribute<Movie, MovieActor> movieActors;public static volatile SetAttribute<Movie, MovieDirector> movieDirectors; }確實(shí)不是必需的,您可以通過屬性的字符串名稱來引用屬性,但這將為您提供安全性。
程序?qū)嶓w圖
EntityGraph<Movie> fetchAll = entityManager.createEntityGraph(Movie.class);fetchAll.addSubgraph(Movie_.movieActors);fetchAll.addSubgraph(Movie_.movieDirectors);fetchAll.addSubgraph(Movie_.movieAwards);該實(shí)體圖指定必須加載實(shí)體的所有關(guān)系。 現(xiàn)在,您可以調(diào)整自己的用例。
資源資源
您可以在Github的Java EE示例中找到此示例代碼。 在這里檢查。
特別說明: EclipseLink / Glassfish當(dāng)前存在一個(gè)錯(cuò)誤,該錯(cuò)誤會(huì)阻止javax.persistence.loadgraph提示正常工作。 在此處檢查問題。
結(jié)論
實(shí)體圖填補(bǔ)了JPA規(guī)范中缺少的空白。 它們是一種額外的機(jī)制,可以幫助您查詢真正需要的內(nèi)容。 它們還可以幫助您提高應(yīng)用程序的性能。 但是使用它們時(shí)要聰明。 可能有更好的方法。
翻譯自: https://www.javacodegeeks.com/2014/11/jpa-entity-graphs.html
總結(jié)