How disable Hibernate caching for current entity without EntityManager? - java

I have following logic:
ModelEntity savedModelEntity = modelEntityRepository.save(modelEntityForSave);
//saving collection to entity here
ModelEventEntity modelEventEntity = prepareModelEventEntityForSave(savedModelEntity);
modelEventRepository.save(modelEventEntity);
//this modelEntity is cached
ModelEntity modelEntity = modelEntityRepository.findById(savedModelEntity.getId());
How can I disable hibernate caching for this entity only in this place?

findById of the JpaRepository uses EntityManger.find() that will return the entity from the Persistence Context.
Unfortunately the JpaRepository does not expose EntityManager.refresh that you need to use.
So you may use the EntityManager directly and refresh the Entity.
// Inject the EntityManager
#Autowired
private EntityManager em;
// Refresh the Entity
em.refresh(savedModelEntity);

You can add annoation to your repository:
#Modifying(clearAutomatically = true)
That way, we make sure that the persistence context is cleared after our query execution.
or
#Modifying(flushAutomatically = true)
Now, the EntityManager is flushed before our query is executed.

Related

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.

Hibernate sessions management in Spring Boot JPA application with HikariCP data source

In Spring Boot application with HikariCP dataSource I've execute HQL queries with helper class:
public class QueryExecutor {
private Session session;
#Autowired
private SessionFactory sessionFactory;
public QueryExecutor getConnection() {
session = sessionFactory.openSession();
session.beginTransaction();
return this;
}
public void closeConnection() {
session.getTransaction().commit();
session.close();
}
public List execute(String hql, Long limit, Long offset) {
getConnection();
Query query = session.createQuery(hql);
List list = query.list();
closeConnection();
return list;
}
It works ok, but when I start using class widely, application starts freezes because Hibernate Session is closed randomly and transaction wait 30 seconds (default timeout value for HikariCP transactions) before get Session is closed error (or currentPersistenceContext is null if I used getCurrentSession() instead openSesssion()).
First of all, I changed open session to getCurrentSession function. But I also need to specify context with #PersistenceContext or hibernate.current_session_context_class=thread in hibernate.cfg.xml.
I read that this property better to use with default value.
Also I specify hibernate.connection.release_mode=AFTER_TRANSACTION.
But that isn't solve a problem.
After all, I changed class like that:
#PersistenceContext
private EntityManager entityManager;
#Transactional
public List execute(String hql, Long limit, Long offset) {
Query query = entityManager.createQuery(hql);
return query.getResultList();
}
and use javax.persistence.Query instead Hibernate queries.
Now it works ok. But is that a correct modifications?
All functions worked with execute method of QueryExecutor annotated with #Transactional. As I uderstood, in that case no beginTransaction() needed.
But need I close entityManager after execute() ?
SessionFactory used with Hibernate without JPA and EntityManager used with Hibernate JPA technologies?
How can I solve problem without using EntityManager?
You don't need to close transactions manually if you use #Transactional annotation.
But if you use it, I will reccomend you try to use JPA Repositories and wrap in #Transactional annotation the methods of business logic only.
In which case you will no longer need EntityManager and you will be able to create custom complex queries with JpaSpecificationExecutor and JPA Criteria API Queries.

How do I put HQL in an entity?

I have a Hibernate entity that I would like to put a method in. This method would call the entity manager and run a prepared statement, but I don't know how to instantiate the entity manager. Whenever I try, to autowire it like so:
#Autowired
private transient EntityManager entityManager;
the entityManager is null when I run the application. Autowiring works for all my other classes. Why can't I autowire the entityManager in my entity, and how do I execute my query in the Entity?
#Autowired is for beans, classes with #Service or #Component. Classes marked #Configuration can also use #Autowired. New instances of these classes are made and managed by Spring, if you try to create new instances of those classes #Autowired won't work there either. Such as MyClass myClass = new MyClass()
Classes marked with #Entity are not managed beans, they are made by the entity manager when you query them from the database, but when you create a new row you make a new instance. Spring doesn't do it's magic to them. You should call your stored procedure from a #Service just like you would use a #Repository or entity manager to save an #Entity.

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.

The dreaded Lazy Initialization Exception when using JAX-WS and JEE6

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.

Categories