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!
Related
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
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);
We are using Hibernate and MySQL in our project. We are using saveOrUpdate() method for updating data.
Our problem When We update the existing data first we are checking some conditions. If those conditions are passed we are throwing error like below snippet code.
Else we are saving the data. We put debug and checked at the saveOrUpdate() method. It is not going to saveOrUpdate(). But some how data is saving into database.
This is DAO layer
public List<Student> getOverlappingStudentDetails(String studentId, String classNumber){
Session session = sessionFactory.getCurrentSession();
Criteria criteria = session.createCriteria(Student.class, "student");
criteria.add(Restrictions.eq("student.id", studentId));
criteria.add(Restrictions.eq("student.classNumnber", classNumber));
return criteria.list();
}
public Student save(Student student) {
Session session = sessionFactory.getCurrentSession();
session.saveOrUpdate(student);
session.flush();
return student;
}
This is service implementation
private void checkForOverlappingStudentDetails(Student student) {
List<Student> overlappingStudents = studentDAO.getOverlappingStudentDetails(student.getStudentId(),student.getClassName());
if (overlappingStudents.size() >0 ) {
throw new ValidationException(student.getName() + " is already present." ;
}
}
It is throwing error. But, data is overriding into database. Is there any solution to stop the data to for updating. We tried session.clear() and we are using session.flush() method after saving the method.
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).
I'm new to hibernate and as I researched, I have found out that the HQL insert query gets data from other tables. According to what I've read, I can make use of session.save for the insert functionality.
In my DAO I have this addToCart() method
#Override
public void addToCart(ShoppingCart cart) {
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction transaction = session.beginTransaction();
session.save(cart);
transaction.commit();
session.close();
}
The code above doesn't work. Maybe I am missing something because I'm still new to hibernate.