I am trying to run an application in which I am using JPQL. In the beginning of the application, I am running,
public class CacheManager {
private static final Logger LOGGER = LoggerFactory.getLogger(CacheManager.class);
private static ConcurrentHashMap<String, Student> temp;
public static void initLoadingCache(StudentDAO dao) {
LOGGER.debug("Fetching...");
List<Student> students = dao.findAll();
}
where the findall() is as follows alongwith its query:
public List<Student> findAll() {
return namedQuery("Student.findAll").getResultList();
}
where the query is like:
#Entity
#Table(name = "Student")
#NamedQueries({
#NamedQuery(
name = "Student.findAll",
query = "SELECT p FROM Student p")
)})
I keep getting org.hibernate.HibernateException: No session currently bound to execution context but I am not sure why I get this for this particular query even though I am not doing multithreading or any Async calls. Any fix would help a lot.
Entire stacktrace:
org.hibernate.HibernateException: No session currently bound to execution context
at org.hibernate.context.internal.ManagedSessionContext.currentSession(ManagedSessionContext.java:58)
at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:464)
at io.dropwizard.hibernate.AbstractDAO.currentSession(AbstractDAO.java:44)
at io.dropwizard.hibernate.AbstractDAO.namedQuery(AbstractDAO.java:76)
at com.xyz.abc.student.db.StudentDAO.findAll(StudentDAO.java:26)
at com.xyz.abc.student.db.CacheManager.initLoadingCache(CacheManager.java:24)
at com.xyz.abc.student.StudentService.run(StudentService.java:118)
at com.xyz.abc.student.StudentService.run(StudentService.java:43)
at io.dropwizard.cli.EnvironmentCommand.run(EnvironmentCommand.java:43)
at io.dropwizard.cli.ConfiguredCommand.run(ConfiguredCommand.java:87)
at io.dropwizard.cli.Cli.run(Cli.java:78)
at io.dropwizard.Application.run(Application.java:93)
at com.xyz.abc.student.StudentService.main(StudentService.java:46)
I further tried this which works
public StudentDAO(SessionFactory factory, int queryTimeout) {
super(factory);
sessionFactory = factory;
this.queryTimeout = queryTimeout;
}
public List<Student> findAll() throws Exception{
List<Student> students = null;
Session session = sessionFactory.openSession();
try {
ManagedSessionContext.bind(session);
Transaction transaction = session.beginTransaction();
try {
students = list((Query<Student>) namedQuery("Student.findAll"));
transaction.commit();
}
catch (Exception e) {
transaction.rollback();
throw new Exception(e.getMessage());
}
} finally {
session.close();
ManagedSessionContext.unbind(sessionFactory);
}
return students;
}
Try adding #UnitOfWork on your findAll method.
See the documentation for more information
To use the DAO classes in Dropwizard, you can just use the #UnitOfWork annotation in Jersey resources, but elsewhere you need to additionally instantiate your class with UnitOfWorkAwareProxyFactory. It will create a proxy of your class, which will open a Hibernate session with a transaction around methods with the #UnitOfWork annotation. (Dropwizard documentation)
One example is here: How to use UnitOfWorkAwareProxyFactory in Dropwizard v1.1.0
Related
I'm learning about #Transactional and I want to ask you a question. Why is important to use #Transactional at the following methods?
#Repository
public class CustomerDAOImpl implements CustomerDAO {
// need to inject the session factory
#Autowired
private SessionFactory sessionFactory;
#Override
#Transactional
public List<Customer> getCustomers() {
// get the current hibernate session
Session currentSession = sessionFactory.getCurrentSession();
// create a query ... sort by last name
Query<Customer> theQuery =
currentSession.createQuery("from Customer order by lastName",
Customer.class);
// execute query and get result list
List<Customer> customers = theQuery.getResultList();
// return the results
return customers;
}
#Override
#Transactional
public void saveCustomer(Customer theCustomer) {
// get current hibernate session
Session currentSession = sessionFactory.getCurrentSession();
// save/upate the customer ... finally LOL
currentSession.saveOrUpdate(theCustomer);
}
#Override
#Transactional
public Customer getCustomer(int theId) {
// get the current hibernate session
Session currentSession = sessionFactory.getCurrentSession();
// now retrieve/read from database using the primary key
Customer theCustomer = currentSession.get(Customer.class, theId);
return theCustomer;
}
#Override
#Transactional
public void deleteCustomer(int theId) {
// get the current hibernate session
Session currentSession = sessionFactory.getCurrentSession();
// delete object with primary key
Query theQuery =
currentSession.createQuery("delete from Customer where id=:customerId");
theQuery.setParameter("customerId", theId);
theQuery.executeUpdate();
}
}
I thought that we need to use #Transactional when we have 2 or more writes on a database. For example if we want to transfer $100 from user A to user B. In this case we need to do 2 things, first we need to decrease $100 from user A, and second we need to add $100 to user B. And we need this 2 writes as a single atomic operation. And I understand why we need #Transactional in this situation.
But what I don't understand is why do we need #Transactional for the 4 methods in the above code. In getCustomers() method we just retrieve the customers, in saveCustomer() we just save a customer in the database, deleteCustomer() we just delete a customer. So in these methods we have only one write in the database. Then why do we need #Transactional? Thank you!
Do anyone know how to solve this problem?? I am trying to delete an entity but this error message always appears.
This is the code used:
#Override
public void remove(t_diklat diklat) {
Session session = HibernateUtil.getSessionFactory().openSession();
try {
session.getTransaction().begin();
session.delete(diklat);
session.getTransaction().commit();
} catch (Exception ex) {
throw ex;
}
and:
public String delete() {
t_diklat diklat = (t_diklat)(listDiklat.getRowData());
diklatDao dao = new diklat_Impl();
dao.remove(diklat);
return "diklat_client";
}
This is my dao
public interface diklatDao {
public t_diklat getTbl_diklat(Long id);
public void Save(t_diklat diklat);
public void remove(t_diklat diklat);
public void update(t_diklat diklat);
public List<t_diklat> ListTable();
}
I also add #OneToMany(mappedBy = "diklat_id_5", cascade = CascadeType.ALL) to my model.class but still nothing.
I am quite sure that the:
public String delete()
method is called within an already opened session as you are retrieving data:
t_diklat diklat = (t_diklat)(listDiklat.getRowData());
just before you hit the dao.remove() method.
Inside the dao, you open another session and try pass an entity that is already associated with the presiously opened and still not closed session.
The solution would be to use:
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
instead of:
Session session = HibernateUtil.getSessionFactory().openSession();
Edit:
Try not to open any new transactions and do not perform a commit in the dao. The outer session management should be enough.. So in your dao just:
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.delete(diklat);
I have next error: nested exception is org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.example.Model.entities, could not initialize proxy - no Session
My Model entity:
class Model {
...
#OneToMany(fetch = FetchType.LAZY, mappedBy = "model", orphanRemoval = true)
#Cascade(CascadeType.ALL)
#Fetch(value = FetchMode.SUBSELECT)
public Set<Entity> getEntities() {
return entities;
}
public void addEntity(Entity entity) {
entity.setModel(this);
entities.add(entity);
}
}
And I have a service class:
#Service
#Transactional
class ServiceImpl implements Service {
#Override
public void process(Model model) {
...
model.addEntity(createEntity());
...
}
}
I'm calling service from another service method:
#Override
#JmsListener(destination = "listener")
public void handle(final Message message) throws Exception {
Model model = modelService.getById(message.getModelId());
serviceImpl.process(model);
modelService.update(model);
}
But when I'm trying to call this method I'm getting exception on line entities.add(entity); also the same exception occurs when I'm calling getEntities() on model .
I've checked transaction manager and it's configured correctly and transaction exists on this step. Also I've checked tons of answers on stackoverflow connected to this exception but nothing useful.
What could be the cause of it?
It seems that model is a detached entity.
Try to merge and perform operations on a merge instance:
#Override
public void process(Model model) {
...
Model mergedModel = session.merge(model);
mergedModel.addEntity(createEntity());
...
}
So as #Maciej Kowalski mentioned after first #Transactional read of my model it's already in deatached state and call to get entities from another #Transactional method failed with LazyInitializationException.
I've changed my service a bit to get model from database in the same transaction:
#Service
#Transactional
class ServiceImpl implements Service {
#Override
public void process(long modelId) {
...
Model model = modelDao.get(modelId);
model.addEntity(createEntity());
...
}
}
Now everything works as expected.
I am creating a CRUD API using hibernate as my persistence layer.
The API takes JSON and serializes it to a POJO. A management layer then converts the POJO into a new Hibernate Domain object.
This exact same code is run for both Create and Update - the only difference is that for Update I also set the ID field of the hibernate object.
Creating works just fine, but Update fails out with a org.hibernate.exception.LockTimeoutException. After hours of snooping around I'm going to wave the white flag and hope someone can explain all the reasons I'm an idiot.
ClientManager Code
public class ClientManager {
private static final ClientDAO clientDAO = new ClientDAO();
...
public Client updateClient(ClientVO inputVO) {
// Generate a Client from the input
Client client = ClientManager.generateClient(inputVO);
client.setClientKey(Integer.parseInt(inputVO.getPersonalId()));
client.setUpdateDate(new Date());
client.setUpdateTimestamp(new Date());
// Update the client
clientDAO.update(client);
}
...
public static Client generateClient(ClientVO clientVO) {
Client client = new Client();
client.setFirstName(clientVO.getFirstName());
client.setMiddleName(clientVO.getMiddleName());
client.setLastName(clientVO.getLastName());
return client;
}
}
BaseDAO Code (ClientDAO extends BaseDAO)
public class BaseDAO {
public Boolean save(Object object) {
Session session = getSession();
Transaction tx = session.beginTransaction();
session.save(object);
tx.commit();
session.close();
return Boolean.TRUE;
}
public Boolean update(Object object) {
Session session = getSession();
Transaction tx = session.beginTransaction();
session.merge(object);
tx.commit();
session.close();
return Boolean.TRUE;
}
public Session getSession()
{
return HibernateSessionFactory.getSession();
}
}
Entry Point Code
#PUT
#Path("clients/{personalId}")
#Produces({MediaType.APPLICATION_JSON})
public String updateClient(#PathParam("personalId") String personalId, String data) throws JsonParseException, JsonMappingException, IOException {
ClientVO inputVO = om.readValue(data, ClientVO.class);
inputVO.setPersonalId(personalId);
ClientVO outputVO = clientManager.updateClient(inputVO);
return om.writeValueAsString(outputVO);
}
Note that clientKey is the primary key.
The timeout is happening at the point of the .commit() in the update() method of BaseDAO.
I'm happy to provide more code (e.g. ClientVO) if useful.
The only way this can happen is that you have two database connections that both attempt to modify the same entity.
If this happens with a single-user, it is because you don't use the same Session for the whole request but instead you create several ones. I would say that you open a Hibernate Session and a transaction in some outer-level and when the update method is called you open another Session and a new transaction that conflicts with the outer one that might have already acquired locks on the same entity (because you loaded the entity and change it).
When try to place a list of stings taken from an object I've loading in from a database via hibernate I'm getting this exception.
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
The method I've used to load the list is within a transaction. But when I try to place the list in the model I get the above exception. I'm taking from this that hibernate is requiring me to have even this line of code within a transaction also. But given that it's not a database operation why is this so?
#RequestMapping(value="{id}", method=RequestMethod.POST)
public String addComment(#PathVariable String id, Model model, String comment) {
personService.addComment(Long.parseLong(id), comment);
Person person = personService.getPersonById(Long.parseLong(id));
model.addAttribute(person);
List<String> comments = personService.getComments(id);
model.addAttribute(comments);
return "/Review";
}
Service object.
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
public class PersonServiceImpl implements PersonService {
private Workaround personDAO;
public PersonServiceImpl() {
}
#Transactional(propagation=Propagation.REQUIRED, readOnly=false)
public void savePerson(Person person) {
personDAO.savePerson(person);
}
#Transactional(propagation=Propagation.REQUIRED, readOnly=false)
public Person getPersonById(long id) {
return personDAO.getPersonById(id);
}
#Autowired
public void setPersonDAO(Workaround personDAO) {
this.personDAO = personDAO;
}
#Transactional(propagation=Propagation.REQUIRED, readOnly=false)
public List<Person> getAllPeople() {
return personDAO.getAllPeople();
}
#Transactional(propagation=Propagation.REQUIRED, readOnly=false)
public void addComment(Long id, String comment) {
Person person = getPersonById(id);
person.addComment(comment);
savePerson(person);
}
#Transactional(propagation=Propagation.REQUIRED, readOnly=false)
public List<String> getComments(String id) {
return personDAO.getComments(Long.parseLong(id));
}
}
DAO
import java.util.List;
import javax.persistence.ElementCollection;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
#Repository
public class PersonDAO implements Workaround {
private SessionFactory sessionFactory;
#Autowired
public PersonDAO(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
private Session currentSession() {
return sessionFactory.getCurrentSession();
}
public void addPerson(Person person) {
currentSession().save(person);
}
public Person getPersonById(long id) {
return (Person) currentSession().get(Person.class, id);
}
public void savePerson(Person person) {
currentSession().save(person);
}
public List<Person> getAllPeople() {
List<Person> people = currentSession().createQuery("from Person").list();
return people;
}
public List<String> getComments(long id) {
return getPersonById(id).getComments();
}
}
I am relatively new to Hibernate but I'll take a guess at this from my understanding of it.
By default #OneToMany collections are lazily loaded. So when you load your Person object a proxy will be created in place of your actual comments list. This list won't be loaded until you call the getter for that list (ie getComments()) as you are, and even then I don't think the full list is loaded at once, more so one by one (yep multiple db calls) as you iterate through the list or the whole list at one if you call .size().
However, I think the catch here is that you must load the list within the same session that you load the parent (Person) object. When you are loading the Person object you have a reference to the current session and then once you call the getComments() on that Person object the session is closed.
I think to keep the whole process in one session you could manually open and close your session like so in your DAO class.
public List<String> getComments(long id) {
Session session = sessionFactory.openSession();
List<String> comments = getPersonById(id).getComments();
sessionFactory.getCurrentSession().flush();
sessionFactory.getCurrentSession.close();
return comments;
}
Setting the FetchType to EAGER would solve the problem, but from what I have read it is generally not recommended unless you always need the comments loaded with the Person object.
Change a fetch type to FetchType.EAGER.
I think the problem is that you're using
currentSession()
Try to replace it with
private Session currentSession() {
return sessionFactory.openSession();
}
bcause the exception says that there isn't any open session.
You should review the Person class mapping, maybe the Comments field is mapped with the LAZY attribute and so it's not loaded in the DAO. When you call the getComments method Hibernate tries to load the attribute from the database but at that time there is no session, hence the exception. To solve this issue change the mapping attribute to EAGER.