Java Spring Data JPARepository:如何有条件地获取子实体
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/33266952/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Spring Data JPARepository: How to conditionally fetch children entites
提问by cosbor11
How can one configure their JPA Entities to not fetch related entities unless a certain execution parameter is provided.
除非提供了某个执行参数,否则如何配置他们的 JPA 实体以不获取相关实体。
According to Spring's documentation, 4.3.9. Configuring Fetch- and LoadGraphs, you need to use the @EntityGraph
annotation to specify fetch policy for queries, however this doesn't let me decide at runtime whether I want to load those entities.
根据 Spring 的文档,4.3.9。配置 Fetch- 和 LoadGraphs,您需要使用@EntityGraph
注释来指定查询的提取策略,但这并不能让我在运行时决定是否要加载这些实体。
I'm okay with getting the child entities in a separate query, but in order to do that I would need to configure my repository or entities to not retrieve any children. Unfortunately, I cannot seem to find any strategies on how to do this. FetchPolicy
is ignored, and EntityGraph
is only helpful when specifying which entities I want to eagerly retrieve.
我可以在单独的查询中获取子实体,但是为了做到这一点,我需要配置我的存储库或实体以不检索任何子实体。不幸的是,我似乎找不到任何关于如何做到这一点的策略。FetchPolicy
被忽略,并且EntityGraph
仅在指定我想要急切检索的实体时才有帮助。
For example, assume Account
is the parent and Contact
is the child, and an Account can have many Contacts.
例如,假设Account
是父级,Contact
是子级,并且一个帐户可以有多个联系人。
I want to be able to do this:
我希望能够做到这一点:
if(fetchPolicy.contains("contacts")){
account.setContacts(contactRepository.findByAccountId(account.getAccountId());
}
The problem is spring-data eagerly fetches the contacts anyways.
问题是 spring-data 无论如何都急切地获取联系人。
The Account Entity class looks like this:
帐户实体类如下所示:
@Entity
@Table(name = "accounts")
public class Account
{
protected String accountId;
protected Collection<Contact> contacts;
@OneToMany
//@OneToMany(fetch=FetchType.LAZY) --> doesn't work, Spring Repositories ignore this
@JoinColumn(name="account_id", referencedColumnName="account_id")
public Collection<Contact> getContacts()
{
return contacts;
}
//getters & setters
}
The AccountRepository class looks like this:
AccountRepository 类如下所示:
public interface AccountRepository extends JpaRepository<Account, String>
{
//@EntityGraph ... <-- has type= LOAD or FETCH, but neither can help me prevent retrieval
Account findOne(String id);
}
采纳答案by Rowanto
The lazy fetch should be working properly if no methods of object resulted from the getContacts() is called.
如果没有调用 getContacts() 导致的对象方法,延迟获取应该正常工作。
If you prefer more manual work, and really want to have control over this (maybe more contexts depending on the use case). I would suggest you to remove contacts from the account entity, and maps the account in the contacts instead. One way to tell hibernate to ignore that field is to map it using the @Transient annotation.
如果您更喜欢更多的手动工作,并且真的想对此进行控制(根据用例可能会有更多的上下文)。我建议您从帐户实体中删除联系人,并在联系人中映射帐户。告诉 hibernate 忽略该字段的一种方法是使用 @Transient 注释映射它。
@Entity
@Table(name = "accounts")
public class Account
{
protected String accountId;
protected Collection<Contact> contacts;
@Transient
public Collection<Contact> getContacts()
{
return contacts;
}
//getters & setters
}
Then in your service class, you could do something like:
然后在您的服务类中,您可以执行以下操作:
public Account getAccountById(int accountId, Set<String> fetchPolicy) {
Account account = accountRepository.findOne(accountId);
if(fetchPolicy.contains("contacts")){
account.setContacts(contactRepository.findByAccountId(account.getAccountId());
}
return account;
}
Hope this is what you are looking for. Btw, the code is untested, so you should probably check again.
希望这是你正在寻找的。顺便说一句,该代码未经测试,因此您可能应该再次检查。
回答by André Blaszczyk
Please find an example which runs with JPA 2.1.
请找到一个与 JPA 2.1 一起运行的示例。
Set the attribute(s) you only want to load (with attributeNodes list) :
设置您只想加载的属性(使用 attributeNodes 列表):
Your entity with Entity graph annotations :
带有实体图注释的实体:
@Entity
@NamedEntityGraph(name = "accountGraph", attributeNodes = {
@NamedAttributeNode("accountId")})
@Table(name = "accounts")
public class Account {
protected String accountId;
protected Collection<Contact> contacts;
@OneToMany(fetch=FetchType.LAZY)
@JoinColumn(name="account_id", referencedColumnName="account_id")
public Collection<Contact> getContacts()
{
return contacts;
}
}
Your custom interface :
您的自定义界面:
public interface AccountRepository extends JpaRepository<Account, String> {
@EntityGraph("accountGraph")
Account findOne(String id);
}
Only the "accountId" property will be loaded eagerly. All others properties will be loaded lazily on access.
只有“accountId”属性会被急切地加载。所有其他属性将在访问时延迟加载。
回答by Harshal Patil
You can use @Transactional
for that.
你可以用@Transactional
它。
For that you need to fetch you account entity Lazily.
为此,您需要懒惰地获取您的帐户实体。
@Transactional
Annotations should be placed around all operations that are inseparable.
@Transactional
应该在所有不可分割的操作周围放置注释。
Write method in your service layer which is accepting one flag to fetch contacts eagerly.
在您的服务层中写入方法,该方法接受一个标志来急切地获取联系人。
@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 is a Service that can make multiple call in single transaction without closing connection with end point.
@Transactional 是一种可以在单个事务中进行多次调用而无需关闭与端点的连接的服务。
Hope you find this useful. :)
希望您觉得这个有帮助。:)
For more details refer this link
有关更多详细信息,请参阅此链接
回答by cosbor11
Spring data does not ignore fetch=FetchType.Lazy
.
Spring 数据不会忽略fetch=FetchType.Lazy
.
My problem was that I was using dozer-mapping
to covert my entities to graphs. Evidently dozer
calls the getters and setters to map two objects, so I needed to add a custom field mapper configuration to ignore PersistentCollections...
我的问题是我dozer-mapping
用来将我的实体转换为图形。显然dozer
调用 getter 和 setter 来映射两个对象,所以我需要添加一个自定义字段映射器配置来忽略 PersistentCollections...
GlobalCustomFieldMapper.java:
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;
}
}
回答by leventgo
If you are trying to send the resultset of your entities to a client, I recommend you use data transfer objects(DTO) instead of the entities. You can directly create a DTO within the HQL/JPQL. For example
如果您尝试将实体的结果集发送给客户端,我建议您使用数据传输对象 (DTO) 而不是实体。您可以直接在 HQL/JPQL 中创建 DTO。例如
"select new com.test.MyTableDto(my.id, my.name) from MyTable my"
and if you want to pass the child
如果你想通过孩子
"select new com.test.MyTableDto(my.id, my.name, my.child) from MyTable my"
That way you have a full control of what is being created and passed to client.
这样您就可以完全控制正在创建和传递给客户端的内容。