Programmatically Fetching a Lazy JPA Field - java

I have a lazily fetched field in my entity
#ElementCollection(fetch = LAZY)
private List<String> emails;
And my transaction boundary stops at service class, I don't want to keep it open while rendering the view. I want my service classes to return detached entities.
In my service class I tried calling the getters but that seem to be erased by the compiler -- maybe it's an optimization for a statement that appear to do nothing
/* User Service class */
#Transactional
public List<User> getAllUsers() {
List<User> users = new ArrayList();
for(User u : userRepo.findAll()) {
u.getEmails(); // <-- this seem to be erased by the compiler optimization.
users.add(u);
}
return users;
}
Hence I'm forced to print the lazy field into TRACE log so that it won't clutter the production logs. Doing this will ensure the lazy field is pre-populated before the entities are detached:
LOG.trace(u.getEmails().toString());
However this solution isn't pretty at all. Is there any better way to do this?
I don't want to mark the field as EAGER because I have another service method that purposely skips the relationship for efficiency.

Since you are using Hibernate, this is probably going to have to be specific. I'm not aware of any JPA functionality that does this. According to the Hibernate Documentation:
The static methods Hibernate.initialize() and Hibernate.isInitialized(), provide the application with a convenient way of working with lazily initialized collections or proxies. Hibernate.initialize(cat) will force the initialization of a proxy, cat, as long as its Session is still open. Hibernate.initialize( cat.getKittens() ) has a similar effect for the collection of kittens.
This should prevent the compiler from erasing that call, and remove the need for you to do some kind of work with the return value to trick the compiler. So Hibernate.initialize(u.getEmails()) should work for you.

Hibernate.initialize(u.getEmails())

Related

String + Jpa + Hibernate, force collection eager initialization on only specific method without transaction

Very simple question and i would like a simple answer.
I'm loading one entity with many lazy collection associated.
This load is done outside a #Transactional method for personal reasons
right after loading the entity i try loading the collections but i get exception
final A cachedEntity = aRepository.find(entity.getDocument());
final Session session = sessionFactory.openSession();
session.beginTransaction();
Hibernate.initialize(cachedEntity.getAddresses()); // exception right here
cachedEntity.getAddresses().addAll(entity.getAddresses());
cachedEntity.getPhones().addAll(entity.getPhones());
cachedEntity.getEmails().addAll(entity.getEmails());
session.close();
failed to lazily initialize a collection of role: addresses, could not initialize proxy - no Session
This is supposed to be super simple, what am i doing wrong?
please dont suggest annotate collections with eager or add #transactional to method
The solution is very simple, I'll share to help others.
I knew there should be a way to tell jpa to force eager load a collection for one specific query only, and there is.
In the repository interface annotate the specific query you want to have eager load with
#EntityGraph(attributePaths = {"lazyCollectionA", "lazyCollectionB"}, type = EntityGraph.EntityGraphType.LOAD)
A find(/*PARAMETERS_HERE*/);
this way when spring builds this query it will create a inner join for the specified collections and eager initialize them.
Another approach would be specify the whole query in the #Query annotation specifing the inner joins you want. but as i want take advantage of spring auto generated queries i just use the #EntityGraph

JPA manually load collections

I have an entity holding a collection (#OneToMany) which loads lazily. So far so good. If I load the entire list of entity objects (findAll()) I don't want the collection loaded at all. I don't access the collection therefore I assumed it will not be loaded before returning it from a REST endpoint, but it seems like Jackson accesses it when parsing it into JSON.
Currently I iterate over the entire entity list and set the collection to NULL. This seems like a very poor way of doing it, is there a way to ONLY manually load the collection with a specially prepared #Query and not load it automatically (either LAZY no EAGER) at all? Are #JsonViews the correct way to go or should I remove the #OneToMany annotation (I guess then I lose the mapping for the queries that actually do load the collection)? Any other suggestions?
Examplecode
#Entity
#Entity
public class Entity {
#OneToMany(targetEntity = Child.class)
private List<Child> children;
}
Jersey Resource
#GET
#Produces({MediaType.APPLICATION_JSON})
public List<Entity> getAllEntities() {
List<Entity> entities = entityService.findAll();
entities.forEach(e-> e.setChildren(null));
return entities ;
}
Repository = JpaRepository with default findAll() implementation.
thanks
Since you mentioned 'suggestion', I faced the same problem myself and I decided to implement custom DTOs to be sent in the API response. So I ommitted these collection fields and all other I did not want the json processors to touch.
I did implement my set of DTOs mirroring actual persisted entities, but there might be some other mappers to do the job
A few time ago, I asked a question about designing model classes for a REST API. There might be some information there useful for you.
Instead of reusing the same model classes for persistence and for the REST API, I've realized the best approach was creating different models. In some situations you don't want the persistence model to be the same as the model you use in your API. So, defining different models is the way to go.
And I chose MapStruct to map from one model to other.

Hibernate : Force lazy-loadding on eager field

One of our model object in our application has many fields configured to be eagerly fetched like so:
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "field")
public Field getField() {
return this.field;
}
However I sometime do not need these information, which slow down my queries for nothing. I cannot change the behaviour and use FetchType.LAZY instead as I've no idea what will be the impact on the whole application (legacy...). Is there a way to simply tell hibernate to fetch nothing, except if it is specified in the query?
Last time I checked there was no proper solution provided by hibernate, so I ended up with this solution:
Configured the problematic references as LAZY.
All affected service methods (that used these models) got an overloaded version with boolean forceEager
by default all existing functions were refactored to call the new ones with forceEager=true
and here comes the trick: as a means of "forcing the eager fetching" I found nothing better than actually accessing the proxied (lazy-fetched) objects. In case for example a lazily referenced list doing list.size() will force Hibernate to load the full list, hence the service returns with fully fetched object.
In case of more than one layer in your objectstructure is affected, you need to traverse through the whole hierarchy and access every lazily loaded object from top to bottom.
This is a bit error-prone solution, so you need to handle it with care.
If its possible to switch to Criteria for this query, you could use FetchMode.SELECT for the field property
crit.setFetchMode("field", FetchMode.SELECT);

Singleton-EJB concurency with a container-managed transaction

I have a Singleton-EJB, that reads all objects from a database with a specific state. Then I do something with these objects and set the state to someting else:
#Singleton
public class MyEJB {
#PersistenceContext(unitName = "MyPu")
private EntityManager em;
#Lock(LockType.WRITE)
public void doSomeStuffAndClose() {
List<MyObj> objects = getAllOpenObjects();
for (MyObj obj : objects) {
// do some stuff here...
obj.setClosed(true);
}
}
private List<MyObj> getAllOpenObjects() {
TypedQuery<MyObj> q = em.createQuery("select o from MyObj o "
+ "where o.closed = false", MyObj.class);
return q.getResultList();
}
}
Now, if i would like to ensure that my method cannot be called concurently, I add the annotation #Lock(LockType.WRITE). But the transaction that sets the states in the database is committed AFTER the lock was released and it is possible that the next caller grabs the same objects again.
How could I prevent this?
If you are using Wildfly: This is a bug. https://issues.jboss.org/browse/WFLY-4844 describes your problem which will be fixed in Wildfly 10. There the problem is described as a timer problem which might be the same as yours.
My workaround is to seperate the code that does the work into another bean which is called by the outer (timer) bean. The outer bean method is annotated to not start a transaction (#TransactionAttribute(TransactionAttributeType.NEVER)), so the transaction is started and safely finished in the second new bean.
You could use SELECT FOR UPDATE to serialize the access of the rows.
With JPA 2 use the LockModeType:
http://docs.oracle.com/javaee/6/api/javax/persistence/LockModeType.html
q.setLockMode(LockModeType.PESSIMISTIC_WRITE)
There's no way to do this in JPA (so, in a portable way). Your options might be:
Some JPA implementations allow setting isolation level on a per-query basis (e.g. OpenJPA), some don't (Hibernate). But even in OpenJPA this hint needs to be implemented in a particular database driver, otherwise it has no effect).
Running a native query – consult your database documentation for details.
As a side comment I should say that JPA (and Java EE in general) is not designed with bulk database operations in mind – it's rather for multiple concurrent queries for data items that in most cases don't overlap.
You can invoke from your doSomeStuffAndClose method Stateful Session Bean with implemented SessionSynchronization interface. Than from afterCompletion method in SFSB you can inform singleton bean that data has been commited and can handle another request.
I know that this way we have two really tight coupled beans, but this should solve your problem.
You're using container-managed concurrency (the default). In JavaEE 7 (not sure about older ones, but likely yes) the transaction is guaranteed to commit before the method exits, hence before lock is released. From the JavaEE 7 tutorials:
"Typically, the container begins a transaction immediately before an enterprise bean method starts and commits the transaction just before the method exits. Each method can be associated with a single transaction. Nested or multiple transactions are not allowed within a method."
https://docs.oracle.com/javaee/7/tutorial/doc/transactions003.htm#BNCIJ
If you're experiencing another behavior, check for any cache that might be active (#Cacheable). You may watch another interesting question here: https://stackoverflow.com/questions/26790667/timeout-and-container-managed-concurrency-in-singleton
By the way, LockType(WRITE) is also default, you don't need to explicit it. Hence, getAllObjects is also LockType(WRITE).

DAO, Spring and Hibernate

Correct me if anything is wrong.
Now when we use Spring DAO for ORM templates, when we use #Transactional attribute,
we do not have control over the transaction and/or session when the method is called externally, not within the method.
Lazy loading saves resources - less queries to the db, less memory to keep all the collections fetched in the app memory.
So, if lazy=false, then everything is fetched, all associated collections, that is not effectively, if there are 10,000 records in a linked set.
Now, I have a method in a DAO class that is supposed to return me a User object.
It has collections that represent linked tables of the database.
I need to get a object by id and then query its collections.
Hibernate "failed to lazily initialize a collection" exception occurs when I try to access the linked collection that this DAO method returns.
Explain please, what is a workaround here?
Update: All right, let me ask you this. DAO is an abstract layer, so a method "getUserById(Integer id)" is supposed to return an Object.
What if in some cases I need these linked collections of the User object and in other situation I need those collections.
Are there only two ways:
1) lazy loading = false
2) create different methods: getUserByIdWithTheseCollections(), getUserByIdWithOtherCollections() and inside those methods use your approach?
I mean are there only 2 ways and nothing better?
Update 2: Explain please, what would give me the explicit use of SESSIONFACTORY?
How does it look in practice? We create an instance of DAO object,
then inject it with session factory and this would mean that two consequent
method calls to DAO will run within the same transaction?
It seems to me that anyway, DAO is detached from the classes that make use of it!
The logic and transactions are encapsulated within DAO, right?
You can get the linked collection in transaction to load it while you're still within the transaction:
User user = sessionFactory.getCurrentSession().get(User.class, userId);
user.getLinkedCollection().size();
return user;
As BalusC has pointed out, you can use Hibernate.initialize() instead of size(). That's a lot cleaner.
Then when you return such an entity, the lazy field is already initialized.
Replying to your PS - is using transactions on service level (rather than DAO) level feasible? It seems to be, as doing each DAO call in separate transaction seems to be a waste (and may be incorrect).
I find that it's best to put #Transactional at the service layer, rather than the DAO layer. Otherwise, all your DAO calls are in separate hibernate sessions - all that object equality stuff won't work.
In my opinion best way to solve this problem will be to design application in a session-per-request model. Then, if you even have an object taken from DAO, until your OSIV pattern works you can use the object safely anywhere in application, even in views without bothering this stuff. This is probably better solution that those proposed because:
Hibernate.initialize() or size is a very artificial workaround - what if you want to have User with different collection initialized, would you write another method for getting user?
Service layer transactional model is OK, but the same problem comes when you want to get object extracted from the service layer to use it in controller or view
You could do something like following:
public User getByUserId(Long id, String ... fetch) {
Criteria criteria = createCriteria();
if (fetch != null) {
for (String fieldName : fetch) {
criteria.setFetchMode(fieldName, FetchMode.JOIN); // fetch these fields eagerly
}
}
return criteria.add(Restrictions.eq("id", id)).list();
}

Categories