I am developing an application using hibernate and I save Entities as usual inside hibernate transactions. I want to 'get Feedback' from a transaction if it has completed sucessfully or not and according to that to excecute next code. Here is the trivial method I use to update the entity:
public boolean updateDepartment(Department s) {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
Transaction tx = HibernateUtil.getTransaction(session);
boolean success = false;
try
{
tx.begin();
session.update(s);
tx.commit();
success = true;
}
catch (Exception e)
{
tx.rollback();
e.printStackTrace();
success = false;
}
return success;
}
Invocation of the method from other code:
boolean b = dao.updateDepartment(d);
if(b)
{
doStuff();
}
else
{
showMessage("Save not usccessful. Try again");
}
My question is whether this approach with the boolean variable is the optimal way, or could it be carried out in a better way. If my approach is OK, would it be better if the return statement would be surrounded with finally?
session.getTransaction().wasCommitted(); does return false even if the transaction was committed. Please take a look at
wasCommitted returns false
what worked for me is
// close database connection
public boolean closeDBConnection() {
boolean successful = false;
try {
session.getTransaction().commit();
successful = true;
} catch (HibernateException r) {
//log exception here
} finally {
session.close();
session = null;
}
return successful;
}
We can use:
session.getTransaction().getStatus() == TransactionStatus.COMMITTED
Similarly
TransactionStatus.ROLLED_BACK
But with hibernate 4 only
you can confirm whether the transaction has committed or not with session.getTransaction().wasCommitted(); statement. If tx committed it returns TRUE.
Related
I am trying to do a simple saveOrUpdate operation using a Transaction Manager in Hibernate but, the context is not flushed to the DB tables. I suspect, the Transaction Manager is not able to resolve the end of the transaction and hence, doesn't flush the changes.
The changes are reflected in the DB Table if a session.flush() is done explicitly. I am using the Default flush mode, which is AUTO.
There are a couple of other questions on SO that talk about the same, but none of them have helped in my case.
#Transactional(transactionManager="txManager", readOnly=false)
public Integer setNamesTable(Names names) {
session = getSession();
Integer id = null;
if (session != null) {
try {
id = (Integer) session.save(names);
} catch (Exception ex) {
System.out.println(ex);
}
LOGGER.info("Exiting setNmlAggrTcaPhn");
}
return id;
}
Have Autowired SessionFactory as below,
#Autowired
#Qualifier("hibernateTestAnnotatedSessionFactory")
SessionFactory sfc;
private synchronized Session getSession() {
try {
session = sf.getCurrentSession();
if (session != null) {
return session;
} else {
session = sf.openSession();
}
} catch (HibernateException ex) {
session = sf.openSession();
}
return session;
}
I'm using a Data Access Object (DAO) pattern in Java and I have the same piece of code repeated all over my files. The thing is somethimng like this:
public User getById(int id) throws BDException {
Session session = sessionFactory.getCurrentSession();
Transaction tx = session.getTransaction();
try {
tx.begin();
Query query = session.createQuery("SELECT u FROM User u WHERE u.id=:id");
query.setString("id", id);
User user = (User) query.uniqueResult();
tx.commit();
return user;
}
catch(javax.validation.ConstraintViolationException | org.hibernate.exception.ConstraintViolationException cve) {
try {
if(tx.getStatus() == TransactionStatus.ACTIVE) {
tx.rollback();
}
}
catch(Exception exc) {
LOGGER.error("Error rollback in method='" + getMethodName() + "'");
}
throw new BDException(cve);
}
catch(RuntimeException ex) {
try {
if(tx.getStatus() == TransactionStatus.ACTIVE) {
tx.rollback();
}
}
catch(Exception exc) {
LOGGER.error("Error rollback in method='" + getMethodName() + "'");
}
throw ex;
}
catch(Exception ex) {
try {
if(tx.getStatus() == TransactionStatus.ACTIVE) {
tx.rollback();
}
}
catch(Exception exc) {
LOGGER.error("Error rollback in method='" + getMethodName() + "'");
}
throw new RuntimeException(ex);
}
}
Well, I want you to look at the catch's part. I have it repeated in every method I have. If it was simple code, I could create a method, put all that code inside and call the method instead of repeat the code. The problem is that it is not normal code, they are exceptions.
So, is there any solution to reuse code and not to repeat (copy-pasting) the code in every method?
Thanks!
is there any solution to reuse code and not to repeat (copy-pasting) the code in every method?
There is.
The "meat" of your function is here
Query query = session.createQuery("SELECT u FROM User u WHERE u.id=:id");
query.setString("id", id);
User user = (User) query.uniqueResult();
If you squint very carefully, you may see that this is a "function" that accepts a Session as an argument, and returns a User. What you can then do is make this function an argument to the thing that does all of the exception handling.
In Java, that usually means expressing the function as an "object"
User MyCrazyFunctionThing::uniqueResult(Session session) {
Query query = session.createQuery(this.sql);
query.setString("id", this.id);
return query.uniqueResult();
}
User DatabaseGateway::execute(MyCrazyFunctionThing q) {
Session session = sessionFactory.getCurrentSession();
Transaction tx = session.getTransaction();
try {
tx.begin();
User user = q.uniqueResult(session)
tx.commit();
return user;
} catch (...) {
// ...
}
}
Right away, you can turn that into logic that can be run any time you try to fetch a unique user from a session.
You can make that even more general with generics
interface MyCrazyGenericThing<T> {
T uniqueResult(Session session);
}
class MyCrazyFunctionThing implements MyCrazyGenericThing<User> {
User uniqueResult(Session session) {
Query query = session.createQuery(this.sql);
query.setString("id", this.id);
return query.uniqueResult();
}
}
<T> T DatabaseGateway::execute(MyCrazyGenericThing<T> q) {
Session session = sessionFactory.getCurrentSession();
Transaction tx = session.getTransaction();
try {
tx.begin();
T result = q.uniqueResult(session)
tx.commit();
return result;
} catch (...) {
// ...
}
}
What you are seeing here is the Strategy Pattern being used to specify what code should run inside the transaction logic.
Looks like a job for the Execute Around idiom.
Place the specialised code in a lambda expression. Pass the specialised code to a method with the general code that executes the object holding the lambda expression at the appropriate point.
For your code, depending on exactly what you want to factor out, usage may look something like:
public User getById(int id) throws BDException {
return query(
"SELECT u FROM User u WHERE u.id=:id",
query -> {
query.setString("id", id);
return (User) query.uniqueResult();
}
);
}
With this session handler:
public class SessionHandler {
private static SessionFactory DBContext;
static {
try {
DBContext = HibnerateConfiguration.config().buildSessionFactory();
}
catch(Throwable t) {
throw new ExceptionInInitializerError(t);
}
}
/*
* Returns a session anyway. If currently no session exist, open a new one;
* If there is a current session, use the existing one.
*/
#Override
public Session getSession() {
try {
return DBContext.getCurrentSession();
}
catch (HibernateException he) {
logger.error("session already exist.");
return DBContext.getCurrentSession();
}
}
public void close() {
DBContext.close();
}
}
and the following create and get methods:
public Serializable create(T type_entity) {
Session session = getSessionHandler().getSession();
Transaction tx = null;
try {
tx = session.beginTransaction();
Serializable result = session.save(type_entity);
tx.commit();
return result;
} catch (HibernateException ex) {
ex.printStackTrace();
if (tx!=null) tx.rollback();
throw ex;
} finally {
getSessionHandler().close();
}
}
#SuppressWarnings("unchecked")
public T get(Serializable id) throws InvalidRequestException {
Session session = getSessionHandler().getSession();
Transaction tx = session.beginTransaction();
tx.commit();
try {
Object obj = session.get(_classtype, id);
if (obj == null) {
throw new InvalidRequestException(String.format("requested object with id %s does not exist.", id));
} else {
return (T)obj;
}
} catch(HibernateException ex) {
ex.printStackTrace();
if (tx!=null) tx.rollback();
throw ex;
} finally {
getSessionHandler().close();
}
}
When I create an object that returns me id = 4, and if immediately I make a request on browser that eventually ask for the new object of id 4, I have to wait for a few seconds (last time I tried is > 3 seconds).
When the id is returned from the create, the data should already exist. However the get returns null. I highly suspect the get is using the old cache which then is updated every a few seconds, but I have no idea how to fix it.
Let me know if any info is required and I am happy to provide them.
im trying to test this method to remove an entity from the h2 database:
public boolean delete(T entity) {
if (entity == null) {
throw new IllegalArgumentException();
}
boolean ret = true;
EntityManager em = entityManager();
try {
EntityTransaction tx = em.getTransaction();
tx.begin();
em.remove(em.merge(entity));
tx.commit();
} catch (RollbackException ex) {
ret = false;
} finally {
em.close();
}
return ret;
}
that method is returning true if the entity is in the database and removes it, but it also returns true if the entity given is not in database. Can someone explain me why? thx.
merge will persist an entity if it doesn't already exist. Thus, you are creating an entity (with merge) and then deleting it right away (with remove). Thus no exception is thrown.
If you want to remove an entity and return a boolean whether you removed it or not then you could do...
public boolean delete(T entity) {
if (entity == null) {
throw new IllegalArgumentException();
}
EntityManager em = entityManager();
EntityTransaction tx = em.getTransaction();
try {
tx.begin();
em.refresh(entity);
em.remove(entity);
tx.commit();
return true;
} catch (EntityNotFoundException ex) {
tx.rollback();
return false;
} catch (RuntimeException ex) {
tx.rollback();
throw ex;
} finally {
em.close();
}
}
In my application i have a situation where i need to do a recursive call to a method to achieve requirement.
But when i call the method I am getting
_Exception in thread "main" org.hibernate.SessionException: Session was already closed
at org.hibernate.internal.SessionImpl.close(SessionImpl.java:410)
at com.cerner.core.dao.oracleImpl.test.TestRecurssionSessionClose.fact(TestRecurssionSessionClose.java:40)
at com.cerner.core.dao.oracleImpl.test.TestRecurssionSessionClose.main(TestRecurssionSessionClose.java:49)
_
I have test code for this
public class TestRecurssionSessionClose {
private SessionFactory factory;
private Session session;
private Transaction transaction;
public TestRecurssionSessionClose() {
HibernateUtil.configureSessionFactory();
factory = HibernateUtil.getFactory();
}
public int fact(int n) {
System.out.println(factory.isClosed());
session = factory.openSession();
transaction = session.beginTransaction();
try {
if (n == 1) {
return 1;
} else {
System.out.println(n);
return (n * fact(n - 1));
}
} catch (
HibernateException ex) {
if (transaction != null)
transaction.rollback();
return 0;
} finally {
if (session != null) {
session.close();
}
}
}
public static void main(String[] args) {
TestRecurssionSessionClose testRecurssionSessionClose = new TestRecurssionSessionClose();
System.out.println(testRecurssionSessionClose.fact(3));
}
}
I am new to hibernate. please tell me what I am missing here?
By default some hibernate versiones have hibernate.transaction.auto_close_session to true. This makes that the session is closed automatically without requirement to close.
Disable the auto close session or better use automatic session context management
See: Session configuration