how to force Entity Manager to contain(attach) entity - java

I have a huge object(entity) that stores some vertexes and edges on it.I want to store this entity in server to access every time I want. I will make some algorithms on it, So I do not want to load it from database every time. So I want it to be attached to Entity Manager until I detach. Of course I can open another entity , then old entity will be datached from entity manager, newly persisted entity will be detached.
I am using container managed bean. As you know, I could not control transactions over container managed beans.
I have defined a Singeton bean and define this entity as an attribute inside it. As I understand, when I lookup bean every time, a new transaction is started, So entity manager does not contain my entity in persistence context. So entity manager tries to do so many select statements to attach object to persistence context. If I use Hibernate Session, entity is attached to session until I call session.close(). How can I do this using entity manager?
#Singleton
public class BEan implements BEanRemote {
#PersistenceContext(unitName = "DBService")
private EntityManager em;
// ** newly opened entity.*/
private Entity entity;
}

Related

when entitymanager(session) created in spring?

i read https://www.baeldung.com/spring-open-session-in-view article
According to this article, it says that the session is created in the request phase.
Spring opens a new Hibernate Session at the beginning of the request. These Sessions are not necessarily connected to the database.
But as far as I know, in Spring, when a transaction starts, it gets an entity manager (session). The reason is that there is no need to create an entity manager for logic that does not use JDBC.
#Transactional
#Service
public class DoService {
// when use this service EntityManager is created;
}
I wonder if I'm getting it wrong.

How do we set database session variables in Hibernate?

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.

Register an entity class dynamically without recreating the session factory

I have an entity class which I want to register dynamically.
Can we register an entity class dynamically without recreating the session factory in run time using Hibernate?
No.
The bootstrap process creates the Metadata and passes it to the SessionFactory upon instantiation. For this reason, you can not add entities dynamically to a SessionFactory, meaning that you need to recreate it from scratch.
However, the SessionFactory initialization can be done programmatically so you can recreate it whenever you have new entities that need to be registered.

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.

manual commits with #Transactional attribute

I use spring+hibernate template to process entities. There is rather big amount of entities to be loaded all at once, so I retrieve an iterator from hibernate template.
Every entity should be processed as a single unit of work. I tried to put entity processing in a separate transaction (propagation = REQUIRED_NEW). However I ended with exception stating that proxy is bounded to two sessions. This is due to suspended transaction used for iterator lazy loading. I am using the same bean for lazy loading and for processing entities. (May be it should be refactored into two separate daos: one for lazy loading and one for processing?)
Then I tried to use single transaction that is committed after each entity is processed. Much better here, there are no exceptions during entity processing, but after processing is finished and method returns, exception is thrown from spring transaction managing code.
#Transactional
public void processManyManyEntities() {
org.hibernate.Sesstion hibernateSession = myDao.getHibernateTemplate().getSessionFactory().getCurrentSession();
Iterator<Entity> entities = myDao.findEntitesForProcessing();
while (entities.hasNext()) {
Entity entity = entities.next();
hibernateSession.beginTransaction();
anotherBean.processSingleEntity(entity);
hibernateSession.getTransaction().commit();
}
}
processSingleEntity is a method in another bean annotated with #Transactional, so there is one transaction for all entities. I checked what transaction causes an exception: it is the very first transaction returned from hibernateSession.beginTransaction(), so it just not updated in transaction manager.
There are several questions:
is it possible to avoid session bind exception without refactoring the dao? that is not relevant question as problem is with Session and hibernate not with dao
is it possible to update transaction in the transaction manager? solved
is it possible to use the same transaction (for anotherBean.processSingleEntity(entity);) without #Transactional annotation on processManyManyEntities?
I would prefer to remove the #Transactional annotation on "processManyManyEntities()", and, to eagerly load all data from "findEntitesForProcessing".
If your want each entity of data to be transactional in "processSingleEntity", the #Transactinoal on "processSingleEntity" is fine. You don't have to annotate the #Transactional on "processManyManyEntities()". But in case of lazy loading, eager loading is a necessary mean to prevent the source data from loading in another session, say, in "processSingleEntity".
Since the transaction for each entity of data, the transactional boundary in loading of source data is not the case of your transaction. Don't let the "lazy loading" to complicate your intent of transaction for modification of data.
That is possible to update transaction in transaction manager. All you need to do is get SessionHolder instance from TransactionSynchronizationManager.getResource(sessionFactory) and set transaction in the session holder to the current. That makes possible to commit transaction whenever it is necessary and allows transaction to be committed by spring transaction manager after method return.

Categories