Spring Data JPARepository:如何有条件地获取子项
除非提供某个执行参数,否则如何配置其JPA实体不获取相关实体。
根据Spring的文档, 4.3.9。 配置Fetch-和LoadGraphs ,您需要使用@EntityGraph
注释来指定查询的获取策略,但是这不允许我在运行时决定是否要加载这些实体。
我可以将子实体放在单独的查询中,但为了做到这一点,我需要配置我的存储库或实体以不检索任何子节点。 不幸的是,我似乎找不到任何关于如何做到这一点的策略。 FetchPolicy
被忽略, FetchPolicy
仅在指定我想要急切检索的实体时才有用。
例如,假设Account
是父级, Contact
是子级,而Account可以有多个Contacts。
我希望能够做到这一点:
if(fetchPolicy.contains("contacts")){ account.setContacts(contactRepository.findByAccountId(account.getAccountId()); }
问题是spring-data无论如何急切地获取联系人。
Account Entity类如下所示:
@Entity @Table(name = "accounts") public class Account { protected String accountId; protected Collection contacts; @OneToMany //@OneToMany(fetch=FetchType.LAZY) --> doesn't work, Spring Repositories ignore this @JoinColumn(name="account_id", referencedColumnName="account_id") public Collection getContacts() { return contacts; } //getters & setters }
AccountRepository类如下所示:
public interface AccountRepository extends JpaRepository { //@EntityGraph ... <-- has type= LOAD or FETCH, but neither can help me prevent retrieval Account findOne(String id); }
如果没有调用getContacts()导致的对象方法,则延迟提取应该正常工作。
如果您更喜欢手动工作,并且真的想要控制它(可能更多的上下文取决于用例)。 我建议您从帐户实体中删除联系人,然后在联系人中映射帐户。 告诉hibernate忽略该字段的一种方法是使用@Transient注释来映射它。
@Entity @Table(name = "accounts") public class Account { protected String accountId; protected Collection contacts; @Transient public Collection getContacts() { return contacts; } //getters & setters }
然后在您的服务类中,您可以执行以下操作:
public Account getAccountById(int accountId, Set fetchPolicy) { Account account = accountRepository.findOne(accountId); if(fetchPolicy.contains("contacts")){ account.setContacts(contactRepository.findByAccountId(account.getAccountId()); } return account; }
希望这是你正在寻找的。 顺便说一句,代码未经测试,所以你应该再次检查。
您可以使用@Transactional
。
为此,您需要懒惰地获取帐户实体。
@Transactional
Annotations应围绕所有不可分割的操作。
在您的服务层中写入方法,该方法接受一个标记以急切地获取联系人。
@Transactional public Account getAccount(String id, boolean fetchEagerly){ Account account = accountRepository.findOne(id); //If you want to fetch contact then send fetchEagerly as true if(fetchEagerly){ //Here fetching contacts eagerly Object object = account.getContacts().size(); } }
@Transactional是一种服务,可以在单个事务中进行多次调用,而无需关闭与端点的连接。
希望您觉得这个有帮助。 🙂
有关详细信息, 请参阅此链接
请查找与JPA 2.1一起运行的示例。
设置您只想加载的属性(使用attributeNodes列表):
您的实体具有实体图注释:
@Entity @NamedEntityGraph(name = "accountGraph", attributeNodes = { @NamedAttributeNode("accountId")}) @Table(name = "accounts") public class Account { protected String accountId; protected Collection contacts; @OneToMany(fetch=FetchType.LAZY) @JoinColumn(name="account_id", referencedColumnName="account_id") public Collection getContacts() { return contacts; } }
您的自定义界面:
public interface AccountRepository extends JpaRepository { @EntityGraph("accountGraph") Account findOne(String id); }
只会急切地加载“accountId”属性。 所有其他属性将在访问时延迟加载。
Spring数据不会忽略fetch=FetchType.Lazy
。
我的问题是我使用dozer-mapping
将我的实体转换为图形。 显然, dozer
调用getter和setter来映射两个对象,所以我需要添加一个自定义字段映射器配置来忽略PersistentCollections ……
GlobalCustomFieldMapper.java:
public class GlobalCustomFieldMapper implements CustomFieldMapper { public boolean mapField(Object source, Object destination, Object sourceFieldValue, ClassMap classMap, FieldMap fieldMapping) { if (!(sourceFieldValue instanceof PersistentCollection)) { // Allow dozer to map as normal return; } if (((PersistentCollectiosourceFieldValue).wasInitialized()) { // Allow dozer to map as normal return false; } // Set destination to null, and tell dozer that the field is mapped destination = null; return true; } }
- 在OneToMany注释列表中重复
- java.net.SocketException:管道损坏
- 需要一个Hibernate中主键@OneToOne映射的示例
- Oracle序列事务性
- java.lang.IllegalStateException:BindingResult和bean名称’category’的普通目标对象都不能作为请求属性使用
- 创建名为’entityManagerFactory’的bean时出错调用init方法失败
- “Spring事务”和“Hibernate事务”之间有什么区别
- JPA / Hibernate选择查询返回重复记录
- org.hibernate.loader.MultipleBagFetchException:无法同时获取多个行李