How can one configure their JPA Entities to not fetch related entities unless a certain execution parameter is provided.
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.
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.
For example, assume Account
is the parent and Contact
is the child, and an Account can have many Contacts.
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.
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:
public interface AccountRepository extends JpaRepository<Account, String> { //@EntityGraph ... <-- has type= LOAD or FETCH, but neither can help me prevent retrieval Account findOne(String id); }
Crud Repository doesn't provide methods for implementing pagination and sorting. JpaRepository ties your repositories to the JPA persistence technology so it should be avoided. We should use CrudRepository or PagingAndSortingRepository depending on whether you need sorting and paging or not.
PagingAndSortingRepository provides methods to do pagination and sort records. JpaRepository provides JPA related methods such as flushing the persistence context and delete records in a batch.
Its findById method retrieves an entity by its id. The return value is Optional<T> . Optional<T> is a container object which may or may not contain a non-null value. If a value is present, isPresent returns true and get returns the value.
Spring Data JPA @Query The @Query annotation declares finder queries directly on repository methods. While similar @NamedQuery is used on domain classes, Spring Data JPA @Query annotation is used on Repository interface. This frees the domain classes from persistence specific information, which is a good thing.
The lazy fetch should be working properly if no methods of object resulted from the getContacts() is called.
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.
@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.
You can use @Transactional
for that.
For that you need to fetch you account entity Lazily.
@Transactional
Annotations should be placed around all operations that are inseparable.
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.
Hope you find this useful. :)
For more details refer this link
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With