除非提供某个执行参数,否则如何配置其JPA实体不获取相关实体。
根据Spring的文档4.3.9. Configuring Fetch- and LoadGraphs,您需要使用@EntityGraph
注释来指定查询的获取策略,但是这不允许我在运行时决定是否要加载这些实体。
我可以将子实体放在单独的查询中,但为了做到这一点,我需要配置我的存储库或实体以不检索任何子节点。不幸的是,我似乎找不到任何关于如何做到这一点的策略。 FetchPolicy
被忽略,EntityGraph
仅在指定我想要急切检索的实体时才有用。
例如,假设Account
是父级,Contact
是子级,而帐户可以有多个联系人。
我希望能够这样做:
if(fetchPolicy.contains("contacts")){
account.setContacts(contactRepository.findByAccountId(account.getAccountId());
}
问题是spring-data无论如何急切地获取联系人。
帐户实体类如下所示:
@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
}
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);
}
答案 0 :(得分:11)
如果没有调用getContacts()导致的对象方法,则lazy fetch应该正常工作。
如果您更喜欢手动工作,并且真的想要控制它(可能更多的上下文取决于用例)。我建议您从帐户实体中删除联系人,然后在联系人中映射帐户。告诉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
}
然后在您的服务类中,您可以执行以下操作:
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;
}
希望这就是你要找的东西。顺便说一下,代码未经测试,所以你应该再次检查。
答案 1 :(得分:9)
您可以使用@Transactional
。
为此,您需要懒惰地获取帐户实体。
@Transactional
注释应围绕所有不可分割的操作。
在服务层中编写方法,该方法接受一个标记以急切地获取联系人。
@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是一种可以在单个事务中进行多次调用的服务 没有关闭与终点的连接。
希望你觉得这很有用。 :)
有关详细信息refer this link
答案 2 :(得分:6)
请查找使用JPA 2.1运行的示例。
设置您只想加载的属性(使用attributeNodes列表):
您的实体具有实体图注释:
@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;
}
}
您的自定义界面:
public interface AccountRepository extends JpaRepository<Account, String> {
@EntityGraph("accountGraph")
Account findOne(String id);
}
只会急切地加载“accountId”属性。所有其他属性将在访问时延迟加载。
答案 3 :(得分:2)
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;
}
}
答案 4 :(得分:0)
如果您尝试将实体的结果集发送给客户端,则建议您使用数据传输对象(DTO)代替实体。您可以在HQL / JPQL中直接创建DTO。 例如
"select new com.test.MyTableDto(my.id, my.name) from MyTable my"
如果您想让孩子通过
"select new com.test.MyTableDto(my.id, my.name, my.child) from MyTable my"
这样,您就可以完全控制正在创建并传递给客户端的内容。