I have just learnt how to use Hibernate today, and I am messing with a small database.
public class HibernateQuery {
private static final SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
private static final Session session = sessionFactory.getCurrentSession();
public static void main(String[] args) {
queryA();
queryB();
session.close();
sessionFactory.close();
}
public static void queryA() {
Transaction tx = session.beginTransaction();
List<GradesJPA> lst = session.createQuery("from GradesJPA", GradesJPA.class).list();
for(GradesJPA each : lst) {
System.out.println(each);
}
tx.commit();
}
public static void queryB() {
Transaction tx = session.beginTransaction();
List<String> lst = session.createQuery("select g.className from GradesJPA g", String.class).list();
for(String each : lst) {
System.out.println(each);
}
tx.commit();
}
}
I get the error Session/EntityManager is closed, after successfully finishing the first query. The solution to the problem is simple. Declare a session for each query(), instead of for the class. However, can someone please explain to me why? (Also, can you please tell me if I need to close the individual sessions created in each method?)
From what I have read, committing a transaction does not close the session: Does committing transaction close session?
From the docs, the only thing I understand is the session-per-operation anti-pattern, but somehow, I am forced to open a new session after each query().
There is also this suggestion, session/entitymanager is closed, and another on the Hibernate forum that advises against class-wide session because of concurrency problems. But I suspect that my code is not running in parallel with anything else.
This is the whole error printed:
Exception in thread "main" java.lang.IllegalStateException: Session/EntityManager is closed
at org.hibernate.internal.AbstractSharedSessionContract.checkOpen(AbstractSharedSessionContract.java:360)
at org.hibernate.engine.spi.SharedSessionContractImplementor.checkOpen(SharedSessionContractImplementor.java:139)
at org.hibernate.internal.AbstractSharedSessionContract.beginTransaction(AbstractSharedSessionContract.java:465)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at org.hibernate.context.internal.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:350)
at com.sun.proxy.$Proxy33.beginTransaction(Unknown Source)
at hibernate.HibernateQuery.queryB(HibernateQuery.java:38)
at hibernate.HibernateQuery.main(HibernateQuery.java:18)
Thank you.
Related
I'm using a PostgreSQL DB and I would like to start VACUUM FULL using JPA EntityManager.
Version 1
public void doVacuum(){
entityManager.createNativeQuery("VACUUM FULL").executeUpdate()
}
throws TransactionRequiredException
Version 2
#Transactional
public void doVacuum(){
entityManager.createNativeQuery("VACUUM FULL").executeUpdate()
}
throws PersistenceException "VACUUM cannot run inside a transaction block"
Version 3
public void doVacuum(){
entityManager.createNativeQuery("VACUUM FULL").getResultList()
}
vacuum is performed but after that I get PersistenceException "No results"
What is the correct way to start this sql command?
As Alay Hay mentioned, using the underlying connection will work:
public void doVacuum(){
org.hibernate.Session session = entityManager.unwrap(org.hibernate.Session);
org.hibernate.internal.SessionImpl sessionImpl = (SessionImpl) session; // required because Session doesn't provide connection()
java.sql.Connection connection = sessionImpl.connection();
connection.prepareStatement("VACUUM FULL").execute();
}
Here is a solution that does not require cast to internal implementation of Hibernate Session. Please keep in mind that VACUUM cannot be run in transaction block, this is why you need to set autoCommit to true.
Session session = entityManager.unwrap(Session.class);
session.doWork(new Work() {
#Override
public void execute(Connection connection) throws SQLException {
connection.setAutoCommit(true);
connection.prepareStatement("VACUUM FULL").execute();
connection.setAutoCommit(false);
}
});
Consider this two classes: EmployeeDetailDAOImpl and EmployeeDAOImpl. Assume if I want to create a new employee, I should also create a new record for EmployeeDetail.
Given the below implementation, I wonder if the outer transaction(EmployeeDAOImpl's tx) is rolled back due to any exceptions happened after the detailDAO.create(employeeId) call, will the transaction of new EmployeeDetail be rolled back as well?
public class SessionHandler {
public static getSession() {
return Configuration.buildSessionFactory().openSession(); //ignore the isConnected or other exception handling for now
}
}
public class EmployeeDetailDAOImpl {
public void create(Serializable employeeId) {
Session session = SessionHandler().getSession();
Transaction tx = session.beginTransaction();
try {
EmployeeDetail detail = new EmployeeDetail(employeeId);
session.save(detail );
} catch (Exception e) {
if (tx!= null) {
tx.rollback;
}
}
session.close();
}
}
public class EmployeeDAOImpl {
public void add(String name) {
Session session = SessionHandler().getSession();
Transaction tx = session.beginTransaction();
try {
Employee employee = new Employee(name);
Serializable employeeId= session.save(employee);
EmployeeDetailDAOImpl detailDAO = new EmployeeDetailDAOImpl();
detailDAO.create(employeeId);
//more things here, that may through exceptions.
} catch (Exception e) {
if (tx!= null) {
tx.rollback;
}
}
session.close();
}
}
Actually, none of the given answers is 100% correct.
It depends on the calling party/service.
If you are calling the methods from an EJB, you will have 1 transaction covering both method calls. That way, the transaction will roll back both operations in case of an exception. Reason behind this is that every method in EJB is transaction Required, unless specified otherwise in the annotation or ejb deployment descriptor.
If you are using spring or any other DI framework, then it depends on your configuration. In a normal setup, your calling transaction will be suspended, since the JPA EJB will create its own transaction. You can however use the JTATransactionManager (As specified here) to make sure that both your EJB and your Spring bean share the same transaction.
If you call the JPA methods from a POJO, then you will have to take care of the JTA transaction handling yourself.
Yes, it will rollback the entity Employee as well. It doesn't even depend on whether the other entities are related.
It depends on the scope of the transaction, which here includes both Employee and EmployeeDetails
You are creating two different transaction for each method.Hence rollback can not happen.
To rollback the transaction you require the propogation in Transaction.
You need to write the code like below::
#Transactional(propagation=Propagation.REQUIRED)
public void testRequired(User user) {
testDAO.insertUser(user);
try{
innerBean.testRequired();
} catch(RuntimeException e){
// handle exception
}
}
Below is link for more information of Propogation.
http://docs.spring.io/spring-framework/docs/2.5.6/api/org/springframework/transaction/annotation/Propagation.html
http://www.byteslounge.com/tutorials/spring-transaction-propagation-tutorial
I have a hibernate session that retrieved from sessionFactory.openSession(), And some complex computing to the Entities and also I want to persistent(UPDATE, INSERT, DELETE) some Entities during the computing.
Here is a case:
Consider that I have a ProductEntity that represents a product, and an OrderEntity which represents the order record of the product, and a UserEntity which represents the user who book the order of the product.
I know I can process an booking operation this way:
public void addOrder(UserEntity userEntity, ProductEntity productEntity, int quantity){
session = sf.openSession();
Transaction tx = session.beginTransaction();
//do some compution and generate the orderEntity and persistent it to the db.
try{
tx.commit();
}catch(Exception e){
tx.rollback();
}finnaly{
session.close();
}
}
Now I have to add more action to the procedure, such as (Maybe) making a NotifyEntity and stored in db which represents the notify records to the Merchant who owns the product. this notify record could be generated by the orderEntity and has nothing to do with the productEntity or UserEntity, in fact, I want this notifyMerchantByOrderEntity method separated-out of the addOrder procedure so that I can reuse this method and clarify the code( I really don't want to mess-up with a bulk of codes in the same method, in fact if the checking logic is complex enough, the code of the addOrder method could be very-very loooong).
Anyhow, I want to :
Separating a very long transaction to several methods
But these methods should be considered together as a whole transaction(ie, they should rollback together when Exception occurs)
Something like this:
public void addOrder(UserEntity userEntity, ProductEntity productEntity, int quantity){
Session session = sf.openSession();
session.beginTransaction();
invokeOtherMethod(); //invoking other method which also contains some db operation.
try{
tx.commit()
}catch(Exception e){
tx.rollback(); //this should rollback the operation in invokeOtherMethod() too.
}finally{
session.close();
}
}
I found a alternative way to do this which pass the session to the method that will be invoked. And test demos show I can also rollback the db operations in the invoked method when Exception occurs, Although the answer #Vlad given may worth a trial. I post my way of doing this here hoping it may inspire guys meet with similar issue.
public OrderEntity addOrder(UserEntity userEntity, ProductEntity, int quantity){
Session session = sf.openSession();
try{
Transaction tx = session.beginTransaction();
OrderEntity orderEntity = new OrderEntity(userEntity, productEntity, quantity);
session.save(orderEntity);
notifyMerchant(orderEntity, session); //here we will invoke another method with session together.
tx.commit();
return orderEntity;
}catch(Exception e){
e.printStack();
tx.rollback(); // if Exception occurs all the operations making to the db will be rollback, as well as those operations in method notifyMerchant();
return null;
}finally{
session.close();
}
}
private void notifyMerchant(OrderEntity orderEntity, Session session){
NotifyEntity notifyEntity = new NotifyEntity(orderEntity);
notifyEntity.notifyMerchant();
session.save(notifyEntity); // this will save the notifyEntity to db as a sort of log stuff;
}
#Vlad may give a better way of doing this, currently the above implement is the way with less change of my project.
You should switch to using Spring TransactionManager as it allows you to group multiple DAO calls into the same database transaction.
If you don't want to use Spring, you can still implement the transaction management logic using a Service Layer method interceptor (maybe a Dynamic Proxy or Javassist) that employs a ThreadLocal storage to bind the current Thread to the same Hibernate Session
When you enter the transaction boundary you open the transaction and when you leave the transaction boundary, you commit the transaction if there is no exception being thrown or roll it back if you caught any Exception:
private SessionFactory sf;
private final ThreadLocal<Session> sessionStorage =
new ThreadLocal<Session>();
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Session session = sessionStorage.get();
if(session == null) {
session = sf.openSession();
}
Transaction tx = session.beginTransaction();
try{
return method.invoke(target, args);
tx.commit();
}catch(Exception e){
tx.rollback();
throw new ServiceException(e);
}finally{
session.close();
sessionStorage.remove();
}
}
Now you'll have to weave this interceptor to all Service method calls.
In a Stateless Session Bean an EntityManager is injected but I would like to get hold of a Connection object in order to invoke a DB Procedure.
Is there any solution to this ?
This is going to be JPA provider specific code. Typically this is done by invoking unwrap() on the EntityManager class.
If you are using EclipseLink, the following code (from the EclipseLink wiki) will be useful (in the case you are using an application-managed EntityManager) :
JPA 2.0
entityManager.getTransaction().begin();
java.sql.Connection connection = entityManager.unwrap(java.sql.Connection.class); // unwraps the Connection class.
...
entityManager.getTransaction().commit();
JPA 1.0
entityManager.getTransaction().begin();
UnitOfWork unitOfWork = (UnitOfWork)((JpaEntityManager)entityManager.getDelegate()).getActiveSession();
unitOfWork.beginEarlyTransaction();
Accessor accessor = unitOfWork.getAccessor();
accessor.incrementCallCount(unitOfWork.getParent());
accessor.decrementCallCount();
java.sql.Connection connection = accessor.getConnection();
...
entityManager.getTransaction().commit();
Note, that the solution provided for JPA 2.0 will fail for Hibernate 3.6.5 with a PersistenceException containing the message
Hibernate cannot unwrap interface java.sql.Connection
Use the code provided by Skaffman to get it to work against Hibernate (verified to work under 3.6.5 even for container managed persistence contexts).
However, the EclipseLink wiki points out one useful bit of info - if you are using JTA managed datasources, you should be injecting it using the #Resource annotation or retrieving it using a JNDI lookup. As long as you need to perform transactional work against the database, it is immaterial as to whether you are obtaining a new connection from the data source or an existing one; most connection pools will anyway provide the same connection that is associated with the current thread (i.e. the one already used by the entity manager). You would therefore avoiding unwrapping the entity manager this way, and also perform transactional activity against the database; do remember that the persistence context cache, and a second-level cache may not be synchronized if you do this.
In Hibernate, the solution posted by skaffman resulted in the following error message:
Hibernate cannot unwrap class org.hsqldb.Session
I did get it to work using SessionImpl rather than Session:
Connection connection = entityManager().unwrap(SessionImpl.class).connection();
An example of solving the problem using Session.doWork() is as follows:
private void executeNative(final String query) {
Session session = entityManager.unwrap(Session.class);
session.doWork(new Work() {
#Override
public void execute(Connection connection) throws SQLException {
Statement s = null;
try {
s = connection.createStatement();
s.executeUpdate(query);
}
finally {
if (s != null) {
s.close();
}
}
}
});
}
The JPA API itself doesn't seem to offer this, not surprisingly, but if you're willing to couple your code to a specific implementation, then you can use something like this (Hibernate):
Session hibernateSession = entityManager.unwrap(Session.class);
Connection jdbcConnection = hibernateSession.connection();
Note that Session.connection() is deprecated for removal in Hibernate 4. Consider using Session.doWork() instead.
You must take the underlying delegate using entitymanager.getDelegate() or entitymanager.unwrap(which is the better way), cast it to the specific implementation(in Hibernate it is called Session). Then you can call the connection() method. Be aware this is deprecated, use the Work class instead. Read more here.
In JPA2.0, if need JDBC is por DTO nomodel or entity for query more
complex. Sometimes JPA is not all...
I hope this will help you:
Statement statement = null;
EntityManager em = null;
em = emf.createEntityManager();
EntityTransaction et = em.getTransaction();
if(!et.isActive()) {
et.begin();
}
java.sql.Connection connection = em.unwrap(java.sql.Connection.class);
String qquerry="SELE ...
try {
statement = connection.createStatement();
ResultSet rs = statement.executeQuery(qquerry);
if (!rs.next()) {
return null;
}
else{
wwwwas=rs.getString(4);
}
statement.close();
}
catch (SQLException e) {
System.out.println("\n b-03:"+e);
throw new RuntimeException(e.getMessage(), e);
}
finally {
try {
// em.getTransaction().commit();
if(connection != null )
connection.close();
}
catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}
This works awesomely and you can use the connection object elsewhere if needed
SessionImpl sessionImpl = (SessionImpl) session;
Connection conn = sessionImpl.connection();
Where session is the name of the Hibernate Session object
Below is the code that worked for me. We use jpa 1.0, Apache openjpa implementation.
import java.sql.Connection;
import org.apache.openjpa.persistence.OpenJPAEntityManager;
import org.apache.openjpa.persistence.OpenJPAPersistence;
public final class MsSqlDaoFactory {
public static final Connection getConnection(final EntityManager entityManager) {
OpenJPAEntityManager openJPAEntityManager = OpenJPAPersistence.cast(entityManager);
Connection connection = (Connection) openJPAEntityManager.getConnection();
return connection;
}
}
I'm working on a webapp and I have connection errors after Hibernate throws exceptions :
com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: No operations allowed after connection closed.
It gave me this exception each time I try to access my db after an exception occured.
I now Hibernate's not supposed to throw errors if my application is well coded but if something happens with the connection to the db, I don't want my application to be stuck with this error.
Here's my HibernateUtil class :
public class HibernateUtil {
private static Logger log = Logger.getLogger(HibernateUtil.class);
private static org.hibernate.SessionFactory sessionFactory;
private static String confFile = "hibernate-test.properties";
private static final ThreadLocal<Session> threadSession = new ThreadLocal<Session>();
private HibernateUtil() {
}
public static void buildSessionFactory(){
Configuration configuration = new Configuration();
synchronized(HibernateUtil.class){
if(sessionFactory == null){
try {
Properties properties = new Properties();
properties.load(HibernateUtil.class.getClassLoader().getResourceAsStream(confFile));
configuration.setProperties(properties);
} catch (Exception e) {
log.fatal("cannot load the specified hibernate properties file: " + confFile);
throw new RuntimeException("cannot load the specified hibernate properties file : " + confFile, e);
}
sessionFactory = configuration.configure().buildSessionFactory();
}
HibernatePBEEncryptorRegistry registry = HibernatePBEEncryptorRegistry.getInstance();
if(registry.getPBEStringEncryptor("strongHibernateStringEncryptor") == null) {
StandardPBEStringEncryptor strongEncryptor = new StandardPBEStringEncryptor();
strongEncryptor.setAlgorithm("PBEWithMD5AndDES"); // not really needed as it is the default
strongEncryptor.setPassword("aStrongPassword");
registry.registerPBEStringEncryptor("strongHibernateStringEncryptor", strongEncryptor);
}
}
}
public static SessionFactory getSessionFactory() {
if(sessionFactory == null){
buildSessionFactory();
}
return sessionFactory;
}
public static Session getCurrentSession(){
if(!getSessionFactory().getCurrentSession().isOpen())
getSessionFactory().openSession();
return getSessionFactory().getCurrentSession();
}
}
Here's my BaseAction class where initialization and closing of sessions is set :
public class BaseAction extends ActionSupport {
public Session hib_session;
public void initHibSession() {
hib_session = HibernateUtil.getCurrentSession();
hib_session.beginTransaction();
hib_session.clear();
}
public void closeHibSession() {
hib_session.getTransaction().commit();
}
}
Here's an example of an action:
Transaction transaction = new Transaction(user, Transaction.Type.REGISTRATION, new HashSet(domains));
initHibSession();
hib_session.save(transaction);
closeHibSession();
transaction_id = transaction.getId();
Is there a way to avoid the exception above ?
It gave me this exception each time I try to access my db after an exception occurred.
I'm not sure to understand the exact condition. Anyway, after an exception, you should rollback the transaction, close the session and start over. That being said, I have some remarks about your code.
About your HibernateUtil:
why do you have a ThreadLocal, the Session#getCurrentSession() method handle that for you (you don't seem to use the thread local though).
in HibernateUtil.getCurrentSession(), why do you mess with getCurrentSession() and openSession()? Firstly, there is no need to do what you do, getCurrentSession() will return a new session if no session is associated to the current thread. Secondly, both approaches are different and have different semantics (you need to close the session yourself when using openSession()), you should use one or the other.
About your BaseAction:
I wonder why you clear() the session after Session#beginTransaction(). In case you didn't committed an ongoing transaction, you'll loose all the pending changes. Is this really what you want?
PS: I would consider using the Open Session in View pattern to remove all this burden from your code.
Resources
Sessions and transactions
Open Session in View