I have a Project entity with a oneToMany relationship with Event entity
public class Project {
....
#OneToMany(mappedBy = "dossier", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
private List<Event> events;
}
I have a ProjectService class
#Service
#Transactional
public class ProjectService {
public List<Project> findAll() {
return (List<Project>) projectRepository.findAll();
}
}
And a ProjectController
#RestController
#RequestMapping(value = "/projects")
public class ProjectController {
#RequestMapping(method= RequestMethod.GET)
public List<Project> getAllProject() {
return projectService.findAll();
}
}
In my client code I saw that the events of the projects are loaded and I don't understand why.
I expected that at the end of the transaction of the method findAll in DossierService, the entities will be detached. Obviously my entities are still attached as the events are retrieved during the jackson serialization in my controller.
Project.events is by default lazy-loaded because it is a OneToMany relationship.
It does not mean that Project.events is not loaded. It means that it will be loaded as soon as Project.getEvents() is called.
This occurs at the JSON serialization (when ProjectController.getAllProject() returns its response).
In order to prevent that, there are 2 ways :
Either you explicitly call project.setEvents(null) (or an empty list) on every project returned by ProjectService.
Or you add a #JsonIgnore annotation on Project.events.
EDIT: if you are using spring-boot, an OpenEntityManagerInViewInterceptor is registered by default :
Spring web request interceptor that binds a JPA EntityManager to the thread for the entire processing of the request. Intended for the "Open EntityManager in View" pattern, i.e. to allow for lazy loading in web views despite the original transactions already being completed.
You can disable this behavior by adding this line to application.properties :
spring.jpa.open-in-view=false
With this configuration, calling the getter outside of the hibernate session will cause a LazyInitializationException.
There are two possibilities:
An OpenSessionInView or OpenEntityManagerInView bean has been defined. This type of bean causes the Session or EntityManager, respectively, to be opened at the time the controller is invoked and remains open until the controller's body has been serialized.
It is typically considered acceptable behavior in demo/small applications, but it's highly frowned upon in larger more complex applications as your queries should return fully initialized entities required for your view, controller, or logic.
The JSON library has a hibernate addon enabled where it is capable of reattaching and hydrating the entity during the serialization process.
Otherwise, the default behavior that you expect where the Session/EntityManager has closed and the entity is detached once it's returned by the service is accurate. The expected behavior would be a LazyInitializationException otherwise.
Your entities are still attached until:
You ask the entity manager to clear the persistence context using entityManager.clear()
You ask the entity manager to detach your entity using entityManager.detach(project) for every project.
But if you know that your events will be loaded most of the time, you should then consider using FetchType.EAGER so that everything would be fetched at once.
Related
When using Hibernate and JPA, I have an existing DAO Abstract Class that sets up an entity manager this way:
#PersistenceContext(unitName = "<name>")
private EntityManager entityManager;
And in certain methods, it's used in the following manner:
public ObjectType findByPrimaryKey(int id) {
return entityManager.find(ObjectType, id);
}
I wanted to set a database configuration parameter in the same transaction as the "find" query. However, I can't seem to find the internal transaction that entityManager uses. I wrote an Aspect that checks for Transactional annotation and sets the variable in there, and added #Transactional on top of findByPrimaryKey method, but that still didn't get set in the session.
Is there something incorrect here or another way to do it? Ideally, want to set a special variable before every query.
Final solution was to combine Spring's "#Transactional" annotation, which automatically opens a transaction, before calling my Data access layer, with an Aspect that pointcuts to "#Transactional" annotation and runs queries within a transaction. Just executing any query within an "#Transactional" method will also work, before calling the Hibernate data access layer.
Without the "#Transactional" annotation, I couldn't control Hibernate's transaction management.
We have been developing a web site for about a year now, using Spring 4, Hibernate, repositories and JPA. We use the OpenEntityManagerInViewFilter to keep sessions open when retrieving data via endpoints, but recently have been working with scheduled actions and are now running into problems.
We have one method in one class with a #Scheduled annotation, and another method in another class with a #Transactional annotation. The calling method finds data on the file system and passes it to the second class/method.
Here's a simplified version of the code:
<code>
// metadataRepository
#Transactional
public Metadata findOneById(String uuid);
#Transactional
public void processQueueMessages(Map<String, MessageAttributeValue> attributes ) throws IOException{
Metadata metadata = null;
try{
metadata = metadataRepository.findOneById(uuid);
// REST call to get list of locations
GeotaggerList locationList = response.getResult();
// this call just maps fields, no database interaction
Map<String, GeoLocation> locationNameMap = metadataService.getKwebLocations(locationList, null);
for (Map.Entry<String, GeoLocation> entry : locationNameMap.entrySet()){
entry.getValue().setMetadata(metadata);
metadata.getGeoLocations().add(entry.getValue()); //fails
}
metadataService.save(metadata, username);
}
} catch (Exception e){
e.printStackTrace();
return;
}
</code>
The method/class that calls processQueueMessages has a #Scheduled annotation. I tried adding a #Transactional annotation on the caller, to no avail.
I've tried a few variants of placing the annotations, but always get this error:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: .domain.Metadata.geoLocations, could not initialize proxy - no Session
I can't do an eager fetch in the definition of the Metadata domain object because it's used a lot of places where we don't want eager loading.
I'm open to suggestions.
After much searching, the answer was pretty simple, as it usually is. I use a configuration class, and it needed an annotation of
#EnableTransactionManagement
The answer was in the spring data documentation
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/transaction.html
I must have read that page a dozen times, but missed the key statement.
You could try using hibernate fetch profiles.
https://docs.jboss.org/hibernate/orm/3.5/reference/en/html/performance.html#performance-fetching-profiles
Basically, you keep the same lazy loading by default on your model, but create a fetch profile which eagerly loads your geolocation property. Then when you load the metadata from your scheduled method, activate your fetch profile.
It seems that metadata object is already out of transactional session after its retrieval from metadataRepository, otherwise there is no point in throwing lazy exception. Check that metadataRepository.findOneById(uuid) is not running in a separate transaction, e.g. by marking it as REQUIRES_NEW
In order to prevent the lazy loading exception, you should preload the data inside call to metadataRepository.findOneById either by simply calling metadata.getGeoLocations().isEmpty() or by using a fetch join query to retrieve data eagerly
First of all, I use Java EE, Hibernate with EntityManager and PrimeFaces.
I have one EJB module (business logic and domain) and two WAR modules (Jersey WS and JSF PrimeFaces).
I decided to initialize lazy collections in JSF WAR module to avoid lazy initialization exception. I don't use extended entity manager.
#ManagedBean(name = "company")
#SessionScoped
public class CompanyBean {
#EJB
private CompanyFacade cf;
...
public String showDetails(Long id) {
company = cf.find(id);
Hibernate.initialize(company.getCompanyTypes());
Hibernate.initialize(company.getPrimaryUser());
Hibernate.initialize(company.getBlocked());
Hibernate.initialize(company.getAddresses());
Hibernate.initialize(company.getContacts());
return "DETAILS";
}
...
}
And I get:
Caused by: org.hibernate.HibernateException: collection is not associated with any session
at org.hibernate.collection.AbstractPersistentCollection.forceInitialization(AbstractPersistentCollection.java:474)
at org.hibernate.Hibernate.initialize(Hibernate.java:417)
at minepackage.CompanyBean.showDetails(CompanyBean.java:79)
...
I don't understand it. There has to be a session when one line before the initialization it was fetched from database, doesn't it? I initialize attributes in WS module in similar way and there it's working.
Any idea what's happening?
I think the session is closed after your EJB finished, so the objects are in detached state. So Hibernate.initialize() won't work any more. You have multiple options here:
Open the transaction on the client side (in your JSF bean or in a servlet filter). This way the session will still be open when your are calling Hibernate.initialize().
Modify your EJB to load the full object and all the required collections. You could use fetch joins and/or use Hibernate.initialize() there.
Create a more fine grained API in your EJB. Method like CompanyFacade.getAddressesByCompany().
I would prefer a combination of the latter two. Use fetch joins to load the one-to-one and many-to-one relationships in your find method and add extra methods for loading the one-to-many collections (like addresses). This will also improve performance of your backend because it reduces the number of database queries.
I have a Transaction problem on Spring 3.0.5. In my case I get the so-called exception "No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here"... I have tried everything so far. I can see in my log that the transactional services are detected and registered as Spring beans and I can also see in the logs that they are proxied and associated with underlying Spring transaction interceptors. (Advise) When I run my Spring MVC app my controller will call the service...... :-( but the transaction interceptors are not triggered. (??) I expect my Spring service proxy to open a session and start a transaction before calling my target service method. Since this does not happen, I get the above exception. I have been almost two days on this problem. Tried everything which I found on the internet...but in vain.
I have layered architecture: presentation (springmvc), service (transaction annotated), dataacess (Spring/Hibernate classic sessionfactory). My model objects are annotated with jpa (javax.persistence.*). My spring context config files are separated in appContext.xml, appContext-service.xml and appContext-dao.xml. I have defined my Spring LocalSessionFactoryBean, Datasource and TransactionManager (HibernateTransactionManager) in appContext-dao.xml. I have put in appContext-service.xml where my service implementations resides. In all of my config files I have included and to detect my beans through Controller, Service and Repository annotations.
I appreciate any kind of help.
It sounds like you are doing everything correctly and you know what you are doing. There's not much we can do here unless you show some configuration.
What I'd suggest is some debugging.
First: do you have Unit tests in the service layer that test the queries you are using? Perhaps you can find the error in the service layer.
Then: debug the MVC app, check the types of the injected services. Verify that they are proxies, not the original types.
If they are the original types, you
have an error in your transaction
configuration .
If they are proxies, step through the
query methods and verify that the
transaction logic is applied.
This sounds like accessing a lazily-loaded list or set of you dao after the closing of the transaction. This typically happens if you access that list in the view in stead of the controller, as your controller probably calls methods in transaction scope, and then leaves the transaction and forwards the loaded bean to the view.
Simple solutions:
Configure your data bean to eagerly load
Force loading of the dependencies in the controller (just loop through them)
have a look at this article ans possibly also quite a few right here on SO on lazy loading / lazy fetching of one-to-many associations and the like
Imagine:
// your dao
public class Foo {
// lots of other properties
List<Something> stuff;
// getters and setter for stuff
}
// repository
#Transactional
public class FooRepo {
public Foo loadFoo(int id) {
...
}
}
// Controller
public class Controller {
public void Control() {
sessionContext.set("foo", repo.loadFoo(1)); // transaction managed by spring
}
public void setFooRepo(FooRepo repo) {this.repo = repo};
}
// View
for (Something something : foo.getStuff()) {
// Ooops transaction is closed! stuff is lazily loaded so NOT AVAILABLE
// practical solutions:
// - do not load lazily (Foo.hbm.xml)
// - or force loading by getting all Somethings in the controller
}
I am getting a LIE when using the JAX-WS #Path and the #Stateless (or #RequestScoped) annotation. The code:
#Path("/users")
#Stateless
#Produces(MediaType.APPLICATION_XML)
public class UserResourceRESTService {
#Inject
#UserRepository
#PersistenceContext
private EntityManager em;
#GET
#Path("/{id:[1-9][0-9]*}")
public User lookupUserById(#PathParam("id") long id) {
return em.find(User.class, id);
}
}
The actual exception I am getting:
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
The user object has an address, which has a country. If I change this to a stateful bean and use an extended context it works, but this really shouldn't be a SFSB should it? I am at a bit of a loss as to why the "em" wouldn't be able to open a session when using a stateless bean?
When you are operating in extended persistence context your returned User entity remains in managed state and when you are using transaction persistence context transaction ends once lookupUserById() method returns and result of that method is an entity which is already detached from persistence context. As a result all properties of that entity which are marked as LAZY are not accessible anymore.
If you want to access these lazy properties after entity became detached from persistence context invoke specific getter methods on that entity prior returning from lookupUserById method.
i.e.
public User lookupUserById(#PathParam("id") long id) {
User user = em.find(User.class, id);
user.getAddress().getCountry();
return user;
}
You can also annotate your relationships in User class with fetch = FetchType.EAGER. If you always return everything it's no point to have lazy loading in my opinion.