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; } }