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();
}
}
Related
I'm trying to implement his Java code with Hibernate in order to make SQL queries:
public List<AttendeeModel> getListOfCds(int firstRow, int rowCount) {
List<AttendeeModel> cdList = null;
try {
session.beginTransaction();
Criteria criteria = session.createCriteria(AttendeeModel.class);
criteria.setFirstResult(firstRow);
criteria.setMaxResults(rowCount);
if (criteria != null) {
cdList = (List<AttendeeModel>) criteria.list();
}
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}
return cdList;
}
public int countRows() {
try {
session.beginTransaction();
Criteria criteria = session.createCriteria(AttendeeModel.class);
if (criteria != null) {
return criteria.list().size();
}
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}
return 0;
}
But for some reason I get this error:
java.lang.IllegalStateException: Session/EntityManager is closed
Do you know how I can fix this issue?
From your code it is unclear where session is decleared and how it is populated. Given that this is done somehow correctly, I suspect that missing transaction commit/rollback before return in countRow leads to the exception, try:
public int countRows() {
int size = 0;
try {
session.beginTransaction();
Criteria criteria = session.createCriteria(AttendeeModel.class);
if (criteria != null) {
size = criteria.list().size();
}
session.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
session.getTransaction().rollback();
}
return size;
}
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.
I develop a java web app using Hibernate. Here are some code :
hibernate.cfg.xml
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=UTF-8&connectionCollation=utf8mb4_general_ci</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">123456</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">true</property>
<!--<property name="hibernate.connection.autocommit">true</property>-->
<property name="hibernate.current_session_context_class">thread</property>
BaseDAO.class
public class BaseDAO {
public Session getSession() {
return HibernateUtil.getSessionFactory().openSession();
}
}
I write a generic class for insert, update, delete, find
public class GenericDAO<T, K extends Serializable> extends BaseDAO implements IGenericDAO<T, K> {
private Class<T> type;
protected Class<T> getType() {
return this.type;
}
protected String getClassName() {
return type.getName();
}
#SuppressWarnings("unchecked")
public GenericDAO() {
Type t = getClass().getGenericSuperclass();
ParameterizedType pt = (ParameterizedType) t;
type = (Class<T>) pt.getActualTypeArguments()[0];
}
#Override
public K save(T t) {
Session session = getSession();
Transaction tran = session.beginTransaction();
try {
K k = (K) session.save(t);
tran.commit();
return k;
} catch (Exception e) {
if (tran.isActive()) {
tran.rollback();
}
System.out.println("Sql Error : " + e.getMessage());
}
return null;
}
#Override
public void saveOrUpdate(T t) {
Session session = getSession();
Transaction tran = session.beginTransaction();
try {
session.saveOrUpdate(t);
tran.commit();
} catch (Exception e) {
if (tran.isActive()) {
tran.rollback();
}
System.out.println("Sql Error : " + e.getMessage());
}
}
#Override
public void update(T t) {
Session session = getSession();
Transaction tran = session.beginTransaction();
try {
session.update(t);
tran.commit();
} catch (Exception e) {
if (tran.isActive()) {
tran.rollback();
}
System.out.println("Sql Error : " + e.getMessage());
}
}
#Override
public void delete(T t) {
Session session = getSession();
Transaction tran = session.beginTransaction();
try {
session.delete(t);
tran.commit();
} catch (Exception e) {
if (tran.isActive()) {
tran.rollback();
}
System.out.println("Sql Error : " + e.getMessage());
}
}
#Override
public void delete(K k) {
Session session = getSession();
Transaction tran = session.beginTransaction();
try {
T t = (T) session.get(type, k);
session.delete(t);
tran.commit();
} catch (Exception e) {
if (tran.isActive()) {
tran.rollback();
}
System.out.println("Sql Error : " + e.getMessage());
}
}
#SuppressWarnings("unchecked")
#Override
public T find(K id) {
Session session = getSession();
Transaction tran = session.beginTransaction();
try {
T t = (T) session.get(type, id);
tran.commit();
return t;
} catch (Exception e) {
if (tran.isActive()) {
tran.rollback();
}
System.out.println("Sql Error : " + e.getMessage());
}
return null;
}
#SuppressWarnings("unchecked")
#Override
public List<T> findAll() {
Session session = getSession();
Transaction tran = session.beginTransaction();
try {
Query query = session.createQuery("from " + type.getSimpleName());
List<T> list = query.list();
tran.commit();
return list;
} catch (Exception e) {
if (tran.isActive()) {
tran.rollback();
}
System.out.println("Sql Error : " + e.getMessage());
}
return new ArrayList<>();
}
#SuppressWarnings("unchecked")
#Override
public List<T> findAllWithOrder(String column, String orderType) {
Session session = getSession();
Transaction tran = session.beginTransaction();
try {
if (orderType == null && orderType.equals("")) {
orderType = "ASC";
}
Query query = session.createQuery(
String.format("from %s order by %s %s", type.getSimpleName(),
column, orderType));
tran.commit();
return query.list();
} catch (Exception e) {
if (tran.isActive()) {
tran.rollback();
}
System.out.println("Sql Error : " + e.getMessage());
}
return new ArrayList<>();
}
}
You can see, I don't close session after each method. This code work fine for insert, update and find . But when I want to delete a object :
TblUser user = (TblUser)find(id);
delete(user);
It's throw exception because I use two session for this function.
Then try to close session in each method :
finally{
session.close();
}
Now I can delete object but I can't use lazy load because session has closed for each transaction.
So, how I can manage session to overcome these situation !!!
Since you have activated the property:
<property name="hibernate.current_session_context_class">thread</property>
why don't you use:
public Session getSession() {
return HibernateUtil.getSessionFactory().getCurrentSession();
}
Instead of opening a new session? By doing this, you don't need to worry about closing your session since Hibernate will close it when you close your sessionFactory.
But be aware that this is not thread safe so if you are working in a multi-thread environment, this is not a good option.
I have one method :
// methods
private void transform(TransformList transformList) {
TableA tA = transformList.getTableA();
TableB tB = transformList.getTableB();
TableC tC = transformList.getTableC();
daoObj.saveA(tA);
daoObj.saveB(tB);
daoObj.saveC(tC);
}
Now,
//saveA mehtod
public void saveA(TableA tA) {
Session session = Context.getHibernateSession();
try {
if(session != null) {
Transaction tx = session.beginTransaction();
tx.begin();
session.merge(tA);
tx.commit();
}
}catch (Exception e) {
tx.rollback();
throw new SomeException(e.getMessage());
}
finally{
if (session != null) {
session.clear();
}
}
}
Similarly,
//saveB mehtod
public void saveB(TableB tB) {
Session session = Context.getHibernateSession();
try {
if(session != null) {
Transaction tx = session.beginTransaction();
tx.begin();
session.merge(tB);
tx.commit();
}
}catch (Exception e) {
tx.rollback();
throw new SomeException(e.getMessage());
}
finally{
if (session != null) {
session.clear();
}
}
}
And,
//saveC mehtod
public void saveC(TableC tC) {
Session session = Context.getHibernateSession();
try {
if(session != null) {
Transaction tx = session.beginTransaction();
tx.begin();
session.merge(tC);
tx.commit();
}
}catch (Exception e) {
tx.rollback();
throw new SomeException(e.getMessage());
}
finally{
if (session != null) {
session.clear();
}
}
}
Now while calling 'transform' method, if saveA and saveB method are success and then some
exception occurs in saveC method, is it possible to rollback TableA and TableB records
(which are committed already) along with TableC records?
No. You'll have move your transaction handling at least one level higher: create a transaction, and call saveA(), saveB() and saveC() inside that transaction. Don't handle transactions inside the save methods.
Of course, this is only one possibility. There are frameworks, like Spring, that greatly help you manage your transactions.