#Scheduled #Transactional lazy init collection error - no proxy - java

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

Related

Postgres Hibernate set session variables for row level security

I am having trouble finding information about this issue I am running into. I am interested in implementing row level security on my Postgres db and I am looking for a way to be able to set postgres session variables automatically through some form of an interceptor. Now, I know that with hibernate you are able to do row-level-security using #Filter and #FilterDef, however I would like to additionally set policies on my DB.
A very simple way of doing this would be to execute the SQL statement SET variable=value prior to every query, though I have not been able to find any information on this.
This is being used on a spring-boot application and every request is expected to will have access to a request-specific value of the variable.
Since your application uses spring, you could try accomplishing this in one of a few ways:
Spring AOP
In this approach, you write an advice that you ask spring to apply to specific methods. If your methods use the #Transactional annotation, you could have the advice be applied to those immediately after the transaction has started.
Extended TransactionManager Implementation
Lets assume your transaction is using JpaTransactionManager.
public class SecurityPolicyInjectingJpaTransactionManager extends JpaTransactionManager {
#Autowired
private EntityManager entityManager;
// constructors
#Override
protected void prepareSynchronization(DefaultTransactionStatus status, TransactionDefinition definition) {
super.prepareSynchronization(status, definition);
if (status.isNewTransaction()) {
// Use entityManager to execute your database policy param/values
// I would suggest you also register an after-completion callback synchronization
// This after-completion would clear all the policy param/values
// regardless of whether the transaction succeeded or failed
// since this happens just before it gets returned to the connection pool
}
}
}
Now simply configure your JPA environment to use your custom JpaTransactionManager class.
There are likely others, but these are the two that come to mind that I've explored.

why the lazy collection is loaded

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.

Bulk import and #Transactional

I'm developing an import module to load in a bulk way from an excel file. I'm using spring to manage the transactional context and spring-data-jpa like DAO, to read a excel file I'm using POI.
#Service
public class ImportService{
#Autowired
UserRepository userRepository;
#Autowired
UserDetailRepository userDetailRepository;
#Autowired
AddressRepository addressRepository;
#Transactional(propagation = Propagation.REQUIRES_NEW)
public Map<String,StringBuffer> import(byte[] bytesUploaded) {
wb = new XSSFWorkbook(new ByteArrayInputStream(bytesUploaded));
XSSFSheet mySheet = (XSSFSheet) wb.getSheetAt((short)0);
for(Row row: mySheet){
if(row.getRowNum() >= 2 ){
readRowFromExcel(row);
}
}
}
#Transactional(propagation = Propagation.NESTED,rollbackFor=Exception.class)
private void readRowFromExcel(Row row) {
try{
User user = readUserFromFile(row);
UserDetail = readUserDetailFromFile(row,user);
Address address = readAddressFromFile(row,user);
//A few check to verify the corretly load of the entities
userDetailRepository.save(userDetail);
addressRepository.save(address);
userRepository.save(user);
}
catch (Exception e){
//Do something
}
}
}
I would want that if an exception occurred during read or the save of the row nothing become persistent, however an internal exception mustn't cause rollback the outer transaction.
How can I manage this kind of situation?
Am I facing the problem correctly?
Can you give me some suggestion about this (I'm new of spring)?
Thanks
A good start is the Transaction management section of the Spring framework reference documentation
There are a few things in your sample code that needs improvements:
Declarative transaction management
See section 11.5.1
The most important concepts to grasp with regard to the Spring Framework’s
declarative transaction support are that this support is enabled via AOP
proxies, and that the transactional advice is driven by metadata (currently
XML- or annotation-based). The combination of AOP with transactional metadata
yields an AOP proxy that uses a TransactionInterceptor in conjunction with
an appropriate PlatformTransactionManager implementation to drive transactions
around method invocations.
What that means typically is that calling your private method will not honour the transaction demarcation as you expect. Calling other methods of your service internally won't work either by default as it does not go through the proxy. In your case, Using #Transactional provides additional useful references.
The #Transactional on readRowFromExcel seems useless as you have the guarantee that a fresh transaction will be created before calling import.
Exception handling
In order for the AOP proxy to manage an exception thrown during a transactional method, you have to actually make sure the exception is thrown outside the method boundary so that the proxy can see it. In the case of your example, the do something part should make sure to throw the appropriate exception. If you want fine-grained management of the exception, you should probably define another exception type than Exception.
Wrapping up
If you want to read your excel file and save the content to your store using Spring data, you should probably have a single public annotated method whose purpose is to read an item and store it using Spring data. This would need some refactoring of your API. If the excel sheet is fairly large, you should also consider Spring Batch to process it.
What would be the purpose of the outer transaction here? You should also consider failure scenario such as when one particular row cannot be read (or written to the store). Are you leaving the file half-processed or are you flagging this error somewhere?

JSF + Hibernate: Collection is not associated with any session

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.

Seeking a Spring (3.0.5) Solution for: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here

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
}

Categories