我一直在学习JPA并且发现我们可以从JPA2.1 开始使用实体图。
但我还没有理解实体图的优点。
我知道使用实体图的优点之一是我们只能指定我们想要在整个实体中获取的数据,但是如果我们想要整个实体,还有其他理由使用实体图吗?
或者我们应该只在我们想要检索部分数据时才使用实体图?
如果我们在使用实体图时还有其他目的或优点,我想知道。
我想从数据库加载 UserReference 对象,但从 verifier 属性我只想加载 id、firstName 和 lastName,这样 userReference 看起来像这样:
{
"id": 1,
"company": "company1",
"companyContactName": "some name",
"companyPosition": "programmer",
"referenceDate": "02/04/2005",
"verifier": {
"id":1
"firstName": "Jane",
"lastName": "Smith"
"email":null,
"username":null,
"office:null,
"department":null
}
}
Run Code Online (Sandbox Code Playgroud)
我为 UserReference 类使用了实体图,但我使用的实体图加载了用户拥有的所有信息,包括电子邮件、用户名、办公室和部门。有没有什么方法可以为子图指定 EntityGraphType.FETCH 之类的内容,以便它只加载验证者的 id、firstName 和 lastName?
这是我的 UserReferenceRepository:
public interface UserReferenceRepository extends JpaRepository<UserReference, Long>{
@EntityGraph(value = "userReferenceGraph" , type = EntityGraphType.FETCH )
UserReference findOne(Long id);
}
Run Code Online (Sandbox Code Playgroud)
UserReference 类:
@Getter
@Setter
@EqualsAndHashCode (exclude = {"id", "verifier"})
@ToString(exclude = {"id"})
@Entity
@NamedEntityGraphs({
@NamedEntityGraph(
name = …Run Code Online (Sandbox Code Playgroud) Hibernate 5.0.10 我定义了一个子图,如下所示:
@NamedSubgraph(name = "proc", attributeNodes = {
@NamedAttributeNode("name"),
@NamedAttributeNode("childTable1"),
@NamedAttributeNode("childTable2"),
@NamedAttributeNode("childTable3"),
@NamedAttributeNode("childTable4")
}),
问题是,当使用这个子图时,我会返回根表(名称存在的地方)中定义的所有属性,而不仅仅是我指定的子图属性。有没有办法告诉休眠不获取那些孩子?我的查询看起来就像select rec from records rec join rec.proc
当我有以下实体时。
@Entity
class Article {
@OneToMany
Set<Comment> comments;
}
@Entity
class Comment {
@ManyToOne
User author;
}
Run Code Online (Sandbox Code Playgroud)
并使用 EntityGraph 和静态元模型创建一些视图规范类,如下所示。
class ArticleViewSpec {
/**
* fetch comments and each of comment's author.
*/
public static final Function<EntityManager, EntityGraph<Article>> DETAIL = em -> {
EntityGraph<Article> graph = em.createEntityGraph(Article.class);
Subgraph<Comment> sgComment = graph.addSubgraph(Article_.comments);
sgComment.addAttributeNodes(Comment_.author);
return graph;
};
}
Run Code Online (Sandbox Code Playgroud)
但是上面的类不能被编译,因为预期的类型不是
Subgraph<Comment> sgComment = graph.addSubgraph(Article_.comments);
Run Code Online (Sandbox Code Playgroud)
但
Subgraph<Set<Comment>> sgComment = graph.addSubgraph(Article_.comments);
Run Code Online (Sandbox Code Playgroud)
当我们有扩展的属性时会出现这个问题javax.persistence.metamodel.PluralAttribute。(例如 SetAttribute、ListAttribute)
这种行为显然来自 api 规范。
javax.persistence.EntityGraph#addSubgraph(javax.persistence.metamodel.Attribute<T,X>)
但是,在这些情况下,如何使用 JPA 静态 MetaModel 以可编程方式和类型安全地创建 EntityGraph …
我在我的应用程序中使用@NamedEntityGraph
@NamedEntityGraphs({
@NamedEntityGraph(name = "graph.Employee.assignments", attributeNodes = @NamedAttributeNode("assignmentProjectEmployeeSet")),
@NamedEntityGraph(name = "graph.Employee.absences", attributeNodes = @NamedAttributeNode("absences"))
})enter code here`public class Employee {
Run Code Online (Sandbox Code Playgroud)
当我通过在存储库上使用它时
@EntityGraph(value = "graph.Employee.assignments", type = EntityGraph.EntityGraphType.FETCH)
List<Employee> findAll();
Run Code Online (Sandbox Code Playgroud)
我得到了预期的所有结果。但我需要几个版本的 findAll() 来适应不同的情况。就像是
@EntityGraph(value = "graph.Employee.assignments", type = EntityGraph.EntityGraphType.FETCH)
List<Employee> findAllWithAssignments();
@EntityGraph(value = "graph.Employee.absences", type = EntityGraph.EntityGraphType.FETCH)
List<Employee> findAllWithAbsences();
Run Code Online (Sandbox Code Playgroud)
但是,如果我尝试在存储库中定义新的方法名称,则会收到应用程序上下文错误,因为 Spring 无法解析方法名称。
有可能获得这样的方法吗?
谢谢
马蒂亚斯
我有两个处于一对一关系的实体(可以是多对一的关系,但对于这种情况我认为这并不重要),如下所示:
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
//...
}
@Entity
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String subject;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn
private User otherUser;
//...
}
The repository looks like this
@EntityGraph(attributePaths = {"otherUser"})
Iterable<Post> findAll(Predicate predicate);
Run Code Online (Sandbox Code Playgroud)
因此,我重写了findAll它们默认提供的方法,因为我仍然想使用谓词,并且仍然想将查询连接到另一个表。这几乎可以完美地工作。创建的查询如下所示
select
post0_.id as id1_4_0_,
user0_.id as id1_0_1_,
post0_.subject as subject2_4_0_,
user0_.name as name2_0_1_,
user0_.email as email3_0_1_
from
Post post0_
left outer …Run Code Online (Sandbox Code Playgroud) 我正在尝试使用类型安全方法EntityGraph.addAttributeNodes(Attribute<T, ?> ... attribute)来构建我的实体图。我有一个@MappedSuperclass基本上如下所示的类型层次结构:
@MappedSuperclass
public abstract class BaseEntity
{
@Id
private int dbid;
}
@Entity
public class Entity extends BaseEntity
{
private String someAttribute;
}
Run Code Online (Sandbox Code Playgroud)
EclipseLink 创建了这个元模型:
@Generated(value="EclipseLink-2.5.2.v20140319-rNA", date="2015-08-07T10:46:31")
@StaticMetamodel(BaseEntity.class)
public abstract class BaseEntity_ {
public static volatile SingularAttribute<BaseEntity, Integer> dbid;
}
@Generated(value="EclipseLink-2.5.2.v20140319-rNA", date="2015-08-07T10:46:31")
@StaticMetamodel(Entity.class)
public class Entity_ extends BaseEntity_ {
public static volatile SingularAttribute<Entity, String> someAttribute;
}
Run Code Online (Sandbox Code Playgroud)
问题是我无法dbid使用实体图 API引用属性:
EntityGraph<Entity> graph = em.createEntityGraph( Entity.class );
graph.addAttributeNodes( Entity_.dbid ); // does not compile: …Run Code Online (Sandbox Code Playgroud) Spring Data JPA 是否可以做某事。像这样:
public interface UserDao extends CrudRepository<User, Long> {
@EntityGraph(value = :graphName, type = EntityGraph.EntityGraphType.LOAD)
@Query(value = "SELECT DISTINCT u FROM User u")
List<User> findAllWithDetailsByGraphName(@Param(value="graphName") String graphName);
}
Run Code Online (Sandbox Code Playgroud)
能够在运行时将 graphName 传递给方法并使用一组需要的集合调用负载?此构造不起作用,产生编译时错误。任何围绕它的游戏也失败了......
所以,我在 User 类中有多个集合,我想根据条件加载它们;
@Entity
@Table(name="user")
@NamedEntityGraphs({
@NamedEntityGraph(name = "User.details", attributeNodes = {
@NamedAttributeNode("phones"), @NamedAttributeNode("emails"), @NamedAttributeNode("pets")}),
@NamedEntityGraph(name = "User.phones", attributeNodes =
{@NamedAttributeNode("phones")}),
@NamedEntityGraph(name = "User.emails", attributeNodes =
{@NamedAttributeNode("emails")}),
@NamedEntityGraph(name = "User.pets", attributeNodes =
{@NamedAttributeNode("pets")})
})
public class User {
@Id
@GeneratedValue(strategy= GenerationType.AUTO, generator="native")
@GenericGenerator(name = "native", strategy = …Run Code Online (Sandbox Code Playgroud) 在我当前的项目中,我们的系统中有多个搜索页面,我们从数据库中获取大量数据,并显示在 UI 中的大型表格元素中。我们使用 JPA 进行数据访问(我们的提供者是 Hibernate)。大多数页面的数据是从多个数据库表(许多情况下约为 10 个)收集的,包括来自 OneToMany 关系的一些聚合数据(例如“X 类型关联实体的数量”)。为了提高性能,我们使用结果集分页TypedQuery.setFirstResult()和TypedQuery.setMaxResults()来在用户滚动表时从数据库延迟加载其他行。由于搜索非常动态,因此我们使用 JPA CriteriaQuery API 来构建查询。然而,我们目前在某种程度上遇到了 N+1 SELECT 问题。实际上,在某些情况下这非常糟糕,因为我们可能会迭代 3 个级别的嵌套 OneToMany 关系,其中每个级别上的数据都是延迟加载的。我们无法真正将这些集合声明为在实体映射中预先加载,因为我们只对某些页面中的它们感兴趣。即,我们可能会从多个不同页面中的同一个表中获取数据,但我们会从该表以及不同页面中的不同关联表中显示不同的数据。
为了缓解这个问题,我们开始尝试 JPA 实体图,它们似乎对 N+1 SELECT 问题有很大帮助。但是,当您使用实体图时,Hibernate 显然会在内存中应用分页。我可以在某种程度上理解它为什么这样做,但这种行为在许多情况下否定了实体图的很多(如果不是全部)好处。当我们不使用实体图时,我们可以在不应用任何 WHERE 限制的情况下加载数据(即将整个表视为结果集),无论表有多少百万行,因为只有非常有限的行数实际上是由于分页而获取的。现在分页是在内存中完成的,Hibernate 基本上会获取整个数据库表(加上实体图中定义的所有关系),然后在内存中应用分页,丢弃其余的行。不好。
那么问题是,是否有一种有效的方法可以将分页和实体图与 JPA(Hibernate)一起应用?如果 JPA 没有提供解决方案,Hibernate 特定的扩展也是可以接受的。如果这也不可能,那么其他选择是什么?使用数据库视图?视图会有点麻烦,因为我们支持多个数据库供应商。为不同供应商创建所有必要的视图将大大增加开发工作量。
我的另一个想法是像我们目前所做的那样应用实体图和分页,并且如果它们返回太多行,则不会触发任何查询。我已经需要执行 COUNT 次查询才能使行的延迟加载在 UI 中正常工作。
entitygraph ×9
jpa ×7
java ×6
hibernate ×5
entity ×2
spring ×2
lazy-loading ×1
orm ×1
pagination ×1
spring-boot ×1