I'm building an application that reads information from a log file and saves those data into a database. The information is a set of packages with some users added to the package, like below:
Package: (Total: 14, Used: 11)
CSHC, 11/11
CTQ8, 11/11
CTQ8, 11/11
Every time I end reading one package from the log I make a call to save the package into the database. The problem is that after saving the 4th package, the application freezes. Using the Debug I saw that the hibernate is creating a single session to each saves operation and not closing it after each save, and I think that is the problem, but I'm not sure if that is the cause of the problem.
boolean erasePack = false;
Package pack = new Package();
//System.out.println("Zerei? "+ petrelLicensesInUse);
while (i < reportContent.size()){
phrase = reportContent.get(i);
if(erasePack){
pack = new Package();
erasePack = false;
}
if(phrase.contains(Constants.licenseUsageIdentifier)){
licenseUsage = true;
licenseUser = false;
licenseIssued = phrase.substring((phrase.indexOf(Constants.totalLicenseAvailableId) + 10),phrase.indexOf(Constants.endLicensesIssued));
licenseUsed = phrase.substring((phrase.indexOf(Constants.totalLicenseUsedId) + 12),phrase.indexOf(Constants.endLicensesUsed));
licenseIssuedNum = Integer.parseInt(licenseIssued);
licenseUsedNum = Integer.parseInt(licenseUsed);
licenseUsageList.add(phrase.replaceAll("; Total of ", ", Used: ").replaceAll("Users of ", "")
.replaceAll(" licenses issued", "").replaceAll(" licenses in use", "").replaceAll("Total of", "Total:")
.replaceAll(" license in use", "").replaceAll(" license issued", "").replace(" ", " "));
if(licenseUsedNum != 0){
pack.setUsers(new ArrayList<PbrUser>());
}
}
if(phrase.contains(Constants.licenseUserIdentifier)){
licenseUsage = false;
licenseUser = true;
currPckg = phrase.substring((phrase.indexOf(Constants.licenseUserIdentifier) + 1),phrase.indexOf(Constants.licenseUserIdentifier + " "));
version = phrase.substring((phrase.indexOf(Constants.version) + 3),phrase.indexOf(Constants.endVersion));
vendorDaemon = phrase.substring((phrase.lastIndexOf(' ') + 1));
pack.setNamePackage(currPckg);
pack.setVersion(version);
pack.setVendorDaemon(vendorDaemon);
pack.setNumberOfPackageLicenses(licenseIssuedNum);
//PackageController.create(pack);
}
if(licenseUser && phrase.contains(Constants.userStartDateId)){
//System.out.println(phrase.indexOf(Constants.userStartDateId));
currDate = transformDate(phrase.substring((phrase.indexOf(Constants.userStartDateId)+Constants.userStartDateId.length()),phrase.length()));
//System.out.println(phrase.substring(Constants.spaceUntilUser +1,phrase.length()).indexOf(" "));
currName = phrase.substring(Constants.spaceUntilUser, (Constants.spaceUntilUser + phrase.substring(Constants.spaceUntilUser +1,phrase.length()).indexOf(" ")+1));
PbrUser pbrUser = new PbrUser(currName);
//PbrUserController.create(pbrUser);
reportMetadataList.add(new ReportMetadata(currName, currPckg, currDate));
if(licenseUsedNum != 0){
//PbrUser pbrUser = new PbrUser(currName);
pack.getUsers().add(pbrUser);
}
contSave++;
}
if(licenseUser && contSave == licenseUsedNum){
PackageController.create(pack);
contSave=0;
erasePack = true;
}
i++;
}
Insertion code:
static protected void insert(Object object) {
Transaction tx = null;
Session session = SessionFactoryUtil.getSessionFactory().getCurrentSession();
try {
tx = session.beginTransaction();
session.saveOrUpdate(object);
tx.commit();
} catch (RuntimeException e) {
if (tx != null && tx.isActive()) {
try {
// Second try catch as the rollback could fail as well
tx.rollback();
} catch (HibernateException e1) {
logger.debug("Error rolling back transaction");
}
// throw again the first exception
throw e;
}
}
}
Hibernate Session factory:
public class SessionFactoryUtil {
private static SessionFactory sessionFactory;
private static ServiceRegistry serviceRegistry;
/**
* disable constructor to guaranty a single instance
*/
private SessionFactoryUtil() {
}
public static SessionFactory createSessionFactory() {
Configuration configuration = new Configuration();
configuration.configure();
serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
sessionFactory = configuration.configure().buildSessionFactory(serviceRegistry);
return sessionFactory;
}
public static SessionFactory getSessionFactory() {
return createSessionFactory();
}
/**
* Opens a session and will not bind it to a session context
* #return the session
*/
public Session openSession() {
return sessionFactory.openSession();
}
/**
* Returns a session from the session context. If there is no session in the context it opens a session,
* stores it in the context and returns it.
* This factory is intended to be used with a hibernate.cfg.xml
* including the following property <property
* name="current_session_context_class">thread</property> This would return
* the current open session or if this does not exist, will create a new
* session
*
* #return the session
*/
public Session getCurrentSession() {
return sessionFactory.getCurrentSession();
}
/**
* closes the session factory
*/
public static void close(){
if (sessionFactory != null)
sessionFactory.close();
sessionFactory = null;
}
}
The problem is your call to SessionFactoryUtil.getSessionFactory() is not a singleton. It creates a new Session object every time. Modify that method to truly be a singleton so that it only creates a new instance the first time it's called.
You will still have the issue of not properly closing that session when your code is done so you will have to call session.close() at the end of your application.
EDIT
The first thing to change is the call to getSessionFactory() to return the same instance:
public static SessionFactory getSessionFactory() {
//return createSessionFactory();
return sessionFactory;
}
Now we just have to make sure the sessionFactory is created before any calls to getSessionFactory(). There are lots of ways to do this, one of the more easier ways is to use a static block which will be called at class load time (the first time any of your code calls any SessionFactoryUtil methods)
public class SessionFactoryUtil {
...fields declared the same
static{
createSessionFactory();
}
...
}
Without seeing the rest of your code, I can't be sure, but it's probably a good idea to make createSessionFactory() private so no one else can overwrite your sessionFactory instance.
Related
Im working on a java standAlone project. I need to use hibernate in a MultiThread application but i just cant figure it out how to set up this correctly.
Each Thread deals with the same process of the others.
Everything goes Ok when i run it in a Non-Async way, but when i call the same thing using threads, hibernate just don't work fine.
Can anyone please explain me what's the correct way to use Hibernate in a multiThread Java Stand-Alone App?
Hibernate Util
public class HibernateUtil {
private static final Session session;
static {
try {
SessionFactory sessionFactory;
Properties properties = new Properties();
properties.load(new FileInputStream("middleware.properties"));
Configuration cfg = new Configuration().configure();
cfg.addProperties(properties);
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
.applySettings(cfg.getProperties()).build();
sessionFactory = cfg.buildSessionFactory(serviceRegistry);
session = sessionFactory.openSession();
} catch (IOException | HibernateException he) {
JOptionPane.showMessageDialog(null, DataBaseMessage.CONNECTION_ERROR.getMessage(), DataBaseMessage.CONNECTION_ERROR.getTitle(),JOptionPane.ERROR_MESSAGE);
throw new ExceptionInInitializerError(he);
}
}
public static Session getSession() {
return session;
}
The Error comes here
TbHistoDespachos despacho = Dao.findDespachoByTagId(element.getChild("tagID").getText());
public synchronized List<TbHistoDespachos> ExractDespachoAndNotify(String data, String nombreConexion) {
List<TbHistoDespachos> despachos = new ArrayList<>();
String nombreConexionUpp = nombreConexion.toUpperCase();
try {
Document doc = convertStringToDocument(data);
if (!doc.getRootElement().getChild("reply").getChild("readTagIDs")
.getChildren().isEmpty()) {
for (Element element : doc.getRootElement().getChild("reply").
getChild("readTagIDs").getChild("returnValue")
.getChildren()) {
TbHistoDespachos despacho = Dao.findDespachoByTagId(element.getChild("tagID").getText());
if (despacho != null) {
if(evaluateDespacho(nombreConexionUpp, despacho)){
despachos.add(despacho);
}
}
}
}
} catch (JDOMException | IOException ex) {
JOptionPane.showMessageDialog(null, FilesMessageWarnings.NOTIFICATION_SAP_WARNING.
getMessage().replace("&nombreConexion", nombreConexion).replace("&tagID", ""),
FilesMessageWarnings.NOTIFICATION_SAP_WARNING.getTitle(), JOptionPane.WARNING_MESSAGE);
}
return despachos;
}
Here is the DAO
public class Dao {
private static Session sesion;
public static TbHistoDespachos findDespachoByTagId(String tagId) {
TbHistoDespachos despacho = null;
try {
startTransmission();
despacho = (TbHistoDespachos)sesion.createQuery("FROM TbHistoDespachos WHERE TAG_ID =:tagId")
.setParameter("tagId", tagId)
.uniqueResult();
stopTransmission();
} catch (HibernateException he) {
System.out.println("error: " + he.getMessage());
JOptionPane.showMessageDialog(null, DataBaseMessage.QUERY_ERROR.getMessage(),
DataBaseMessage.QUERY_ERROR.getTitle(), JOptionPane.ERROR_MESSAGE);
}
return despacho;
}
private static void startTransmission() {
sesion = HibernateUtil.getSession();
sesion.getTransaction().begin();
}
private static void stopTransmission() {
sesion.getTransaction().commit();
sesion.getSessionFactory().getCurrentSession().close();
sesion.clear();
}
ANY IDEAS?
The problem stems from static Session variables. A SessionFactory is thread-safe and, generally speaking, you only need one (static) instance per database. A Session, on the other hand, is not thread-safe and is usually created (using a SessionFactory) and discarted/closed on the fly.
To solve your immediate problem, remove the static Session sesion variable from your Dao and also 'inline' the startTransmission and stopTransmission methods in the findDespachoByTagId method. This will ensure that each thread calling findDespachoByTagId creates and uses its own session instance. To analyze the current problem, imagine two threads calling findDespachoByTagId at the same time. Now the static session variable will be assigned a value twice by the startTransmission method. This means one session instance is lost almost immediatly after it was created while the other one is used by two threads at the same time. Not a good thing.
But there are other problems too: there are no finally blocks that guarantee transactions are closed and database connections are released (via the closing of sessions). Also, you will probably want to use a database pool as the one provided by Hibernate is not suitable for production. I recommend you have a look at HibHik: I created this project to show a minimal stand-alone Java application using Hibernate with a database pool (HikariCP) that uses the recommended patterns and practices (mostly shown in TestDbCrud.java). Use the relevant parts in your application, than write multi-threaded unit-tests to verify your database layer (DAO) is working properly, even in the case of failure (e.g. when the database is suddenly no longer available because the network-cable was unplugged).
I created a program using Hibernate.
The program reaches the main function end, nevertheless the program is running.
I wonder if it happens when SessionFactory is configured using Hibernate Version 4.x.
Is the way to configure wrong?
manual1_1_first_hibernate_apps.java
public static void main(String[] args) {
args[0] ="list";
if (args.length <= 0) {
System.err.println("argement was not given");
return;
}
manual1_1_first_hibernate_apps mgr = new manual1_1_first_hibernate_apps();
if (args[0].equals("store")) {
mgr.createAndStoreEvent("My Event", new Date());
}
else if (args[0].equals("list")) {
mgr.<Event>listEvents().stream()
.map(e -> "Event: " + e.getTitle() + " Time: " + e.getDate())
.forEach(System.out::println);
}
Util.getSessionFactory().close();
}
private <T> List<T> listEvents() {
Session session = Util.getSessionFactory().getCurrentSession();
session.beginTransaction();
List<T> events = Util.autoCast(session.createQuery("from Event").list());
session.getTransaction().commit();
return events;
}
Util.java
private static final SessionFactory sessionFactory;
/**
* build a SessionFactory
*/
static {
try {
// Create the SessionFactory from hibernate.cfg.xml
// hibernate version lower than 4.x are as follows
// # it successful termination. but buildSessionFactory method is deprecated.
// sessionFactory = new Configuration().configure().buildSessionFactory();
// version 4.3 and later
// # it does not terminate. I manually terminated.
Configuration configuration = new Configuration().configure();
StandardServiceRegistry serviceRegistry =
new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
}
catch (Throwable ex) {
// Make sure you log the exception, as it might be swallowed
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
/**
* #return built SessionFactory
*/
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
The following console log snippets when program terminate and use buildSessionFactory method.
2 08, 2014 8:42:25 org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl stop
INFO: HHH000030: Cleaning up connection pool [jdbc:derby:D:\Java\jdk1.7.0_03(x86)\db\bin\testdb]
but if do not use deprecated buildSessionFactory method and terminated(program is running), the above two lines do not appear.
ENVIRONMENT:
Hibernate 4.3.1
DERBY
JRE 1.8
IntelliJ IDEA 13
I met this problem also today, and I found the solution is, in the end of your main method (or thread), you should close your Session Factory, like:
sessionFactory.close();
And then, your program will terminate normally.
If You use JavaFX 8 in main method add:
#Override
public void stop() throws Exception {
sessionFactory.close();
}
This method will close session factory and destroy thread on program exit.
I had the same problem today, but I found another similar solution:
I inserted at the end of my code the following line:
StandardServiceRegistryBuilder.destroy(serviceRegistry);
And Ta-dah! the program ends.
Same problem in 4.3.4.Final.
Now after adding following code, the problem is gone.
public class Service {
private SessionFactory factory;
private ServiceRegistry serviceRegistry;
public void initialize() throws Exception{
Configuration configuration = new Configuration();
configuration.configure("com/jeecourse/config/hibernate.cfg.xml");
serviceRegistry = new StandardServiceRegistryBuilder().applySettings(
configuration.getProperties()).build();
factory = configuration.buildSessionFactory(serviceRegistry);
}
public void close() throws Exception{
if(serviceRegistry!= null) {
StandardServiceRegistryBuilder.destroy(serviceRegistry);
}
}
.....
maybe, I solved this problem.
I saw the thread dump after Util.getSessionFactory().close() called, a thread named "pool-2-thread-1" state was TIMED_WAITING (parking).
The following snippets dump
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.0-b69 mixed mode):
"DestroyJavaVM" #16 prio=5 os_prio=0 tid=0x00000000020b9000 nid=0x3684 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"pool-2-thread-1" #15 prio=5 os_prio=0 tid=0x000000001bc27000 nid=0x3f0 waiting on condition [0x000000001ce6f000]
java.lang.Thread.State: TIMED_WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x0000000080be30a0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)
at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1093)
at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1067)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1127)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:744)
"derby.rawStoreDaemon" #14 daemon prio=5 os_prio=0 tid=0x000000001b059000 nid=0xa3c in Object.wait() [0x000000001ba1f000]
java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000805f6190> (a org.apache.derby.impl.services.daemon.BasicDaemon)
at org.apache.derby.impl.services.daemon.BasicDaemon.rest(Unknown Source)
- locked <0x00000000805f6190> (a org.apache.derby.impl.services.daemon.BasicDaemon)
at org.apache.derby.impl.services.daemon.BasicDaemon.run(Unknown Source)
at java.lang.Thread.run(Thread.java:744)
"Timer-0" #13 daemon prio=5 os_prio=0 tid=0x000000001b08e800 nid=0x2160 in Object.wait() [0x000000001b6af000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x0000000080608118> (a java.util.TaskQueue)
at java.lang.Object.wait(Object.java:502)
at java.util.TimerThread.mainLoop(Timer.java:526)
- locked <0x0000000080608118> (a java.util.TaskQueue)
at java.util.TimerThread.run(Timer.java:505)
I thought the cause is thread named "pool-2-thread-1" that created by buildSessionFactory method.
As a result of comparing the two buildSessionFactory method, I noticed that ServiceRegistry resources has not released.
Program successfully terminated by releasing it.
The following code, I adding.
Util.java
configuration.setSessionFactoryObserver(
new SessionFactoryObserver() {
#Override
public void sessionFactoryCreated(SessionFactory factory) {}
#Override
public void sessionFactoryClosed(SessionFactory factory) {
((StandardServiceRegistryImpl) serviceRegistry).destroy();
}
}
);
thanks.
It seems that Hibernate 4.3.1 introduced a bug. I create the connection in my application with:
EntityManagerFactory connection = Persistence.createEntityManagerFactory(...)
but even if the createEntityManagerFactory method fails with an exception, the service registry remains open. However, as you could see from the above code, I cannot terminate my application because as the method didn't succeed the variable connection wasn't assigned (it is null), so I cannot call connection.close() that would destroy the service registry. It seems that this is really a bug, because how will I be able to release resources without resorting to a hack, like using specific Hibernate APIs from a JPA application?
I met this problem also today, and I found that the solution like:
sessionFactory.close();
will work if you have
<property name="connection.pool_size">1</property>
I am using hibenate 5.2.12 with sqlite 3.20.1, managing the connection manually. In my case the problem was that not only the entity manager had to be closed but also the entity manager factory.
With these attributes:
EntityManager entityManager;
EntityTransaction entityTransaction;
This snippet is used when opening the DB and starting a transaction:
EntityManagerFactory emf = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME, map);
entityManager = emf.createEntityManager(map);
entityTransaction = entityManager.getTransaction();
entityTransaction.begin();
This snipped is used to commit the transaction and close the DB:
entityTransaction.commit();
if ( entityManager.isOpen() ) {
entityManager.close();
}
EntityManagerFactory emf = entityManager.getEntityManagerFactory();
if ( emf.isOpen() ) {
emf.close();
}
Now with emf.close(); my application terminates as is should be.
I have just had the same problem. I was using Hibernate 4.1.1 and everything was working fine. Today I upgraded to Hibernate 4.3.1 and suddenly my application didn't terminate anymore. I investigated a little further and noticed that version 4.1.1 didn't have any problem with an open EntityManagerFactory. That's why my application always terminated. That's not the case with version 4.3.1 anymore. So I checked my application and made sure that the EntityManagerFactory was closed at the end (indeed I was not really closing it). Problem solved to me. Are you really sure there's nothing left open in your application? Hope this helps.
Marcos
i had the same problem, the solution is so simple , you have to add this property to configuration's file
<property name="hibernate.c3p0.timeout">0</property>
My answer is for Hibernate 4.3+ version, and I use it in my way.
A example of Spring Annotation configured with Hibernate:
//Using AutoCloseable 1.7 feature here to close context and
//suppress warning Resource leak: 'context' is never closed
//Creating AutoClosebale AbstractApplicationContext Object context
try (AbstractApplicationContext context = new AnnotationConfigApplicationContext(SpringAppConfig.class)) {
SnacksMenu snacks = context.getBean(SnacksMenu.class);
System.out.println(snacks.toString());
//Creating AutoClosebale SessionFactory Object factory
try (SessionFactory factory = getStandardServiceEnabledSessionFactory()){
//Creating AutoClosebale Session Object session
try (Session session = factory.openSession()) {
SessionCounter.analysisSession(session);
System.out.println("1: Opened Sessions under factory : " + SessionCounter.getOpenSessionsCount());
Transaction transaction = session.beginTransaction();
session.persist(snacks);
transaction.commit();
System.out.println(session.isConnected());
}//Session Object session resource auto closed
}//SessionFactory Object factory resource auto closed
System.out.println("2: Opened Sessions under factory : " + SessionCounter.getOpenSessionsCount());
}//AbstractApplicationContext Object context resource auto closed
We have multiple connections to database.
class ConnectionProviderFactory implements DataBaseConnectionProvider {
private EnumMap<SystemInstance, Properties> connectionsConfigs = new EnumMap<>(SystemInstance.class);
private Map<SystemInstance, EntityManager> entityManagers = new HashMap<>();
private Map<SystemInstance, ConnectionPerSystemInstance> connections = new HashMap<>();
#Getter
private static class ConnectionPerSystemInstance {
private String uuid = UUID.randomUUID().toString();
private final SessionFactory sessionFactory;
private final SystemInstance systemInstance;
private ConnectionPerSystemInstance(final SessionFactory sessionFactory, SystemInstance systemInstance){
this.sessionFactory = sessionFactory;
this.systemInstance = systemInstance;
}
static ConnectionPerSystemInstance createConnection(Properties properties, SystemInstance systemInstance) {
StandardServiceRegistryBuilder registryBuilder = new StandardServiceRegistryBuilder();
registryBuilder.applySettings(toMap(properties));
StandardServiceRegistry registry = registryBuilder.build();
MetadataSources sources = new MetadataSources(registry);
Metadata metadata = sources.getMetadataBuilder().build();
SessionFactory sessionFactory = metadata.getSessionFactoryBuilder().build();
return new ConnectionPerSystemInstance(sessionFactory, systemInstance);
}
private static Map<String, String> toMap(Properties properties) {
Map<String, String> map = new HashMap<>();
for (String name : properties.stringPropertyNames()) {
map.put(name, properties.getProperty(name));
}
return map;
}
EntityManager getEntityManager() {
if(sessionFactory == null) {
throw new IllegalStateException("Connection not initialized!");
}
return sessionFactory.createEntityManager();
}
void close() {
if(sessionFactory == null) {
throw new IllegalStateException("Connection not initialized!");
}
sessionFactory.close();
}
#Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final ConnectionPerSystemInstance that = (ConnectionPerSystemInstance) o;
return uuid.equals(that.uuid);
}
#Override
public int hashCode() {
return uuid.hashCode();
}
}
#PostConstruct
private void init() {
String testQuery = "select sysdate from dual";
EntityManager e1TEST = connect(SystemInstance.TEST);
EntityManager e1PROD = connect(SystemInstance.PROD);
log.info("" + e1TEST.createNativeQuery(testQuery).getSingleResult());
log.info("" + e1PROD.createNativeQuery(testQuery).getSingleResult());
}
#PreDestroy
private void clean() {
entityManagers.forEach((key, value) -> value.close());
connections.forEach((systemInstance, connectionPerSystemInstance) -> {
connectionPerSystemInstance.close();
});
}
#Override
public EntityManager connect(final SystemInstance systemInstance) {
if (Optional.ofNullable(entityManagers.get(systemInstance)).isPresent()) {
entityManagers.get(systemInstance);
}
Properties properties = loadConnectionProperties(systemInstance);
ConnectionPerSystemInstance connection = ConnectionPerSystemInstance.createConnection(properties, systemInstance);
connections.put(systemInstance, connection);
entityManagers.put(systemInstance, connection.getEntityManager());
return entityManagers.get(systemInstance);
}
#Override
public void closeAllConnection() {
clean();
}
private Properties loadConnectionProperties(SystemInstance systemInstance) {
if (Optional.ofNullable(connectionsConfigs.get(systemInstance)).isPresent()) {
return connectionsConfigs.get(systemInstance);
}
return tryLoadConnectionProperties(systemInstance);
}
private Properties tryLoadConnectionProperties(final SystemInstance systemInstance) {
final String nameOfPropertyFile = getNameOfPropertyFile(systemInstance);
ClassPathResource classPathResource = new ClassPathResource(nameOfPropertyFile);
Properties properties = new Properties();
try {
properties.load(classPathResource.getInputStream());
addAdditionalConnectionSettings(properties);
connectionsConfigs.put(systemInstance, properties);
} catch (IOException e) {
log.error(e.getMessage(), e);
throw new RuntimeException(e.getMessage()); //TODO chanfe exception
}
return properties;
}
private String getNameOfPropertyFile(SystemInstance systemInstance) {
if (systemInstance == SystemInstance.TEST)
return "db/db-test.properties";
if (systemInstance == SystemInstance.PROD)
return "db/db-prod.properties";
throw new IllegalArgumentException("Incorrect configuration");
}
private void addAdditionalConnectionSettings(Properties properties) {
properties.putIfAbsent("hibernate.dialect", "org.hibernate.dialect.Oracle10gDialect");
properties.putIfAbsent("hibernate.c3p0.timeout", "0");
}
}
In this example we have multiple databases so we can easily close all connections.
I faced the same issue, the reason is we need to close the resources properly.
try closing the resources in finally block
try{
Configuration cfg = new Configuration().configure("hibernate.cfg.xml");
sessionFactory = cfg.buildSessionFactory();;
session = sessionFactory.openSession();
transaction = session.beginTransaction();
session.save(person);
transaction.commit();
}catch(Exception e){
System.out.println("Exception occured. "+e.getMessage());
}finally{
if(session.isOpen()){
System.out.println("Closing session");
session.close();
}
if(!sessionFactory.isClosed()){
System.out.println("Closing SessionFactory");
sessionFactory.close();
}
}
Ensure the same SessionFactory instance you are dealing with through out the program. i.e. The program must terminate if you are closing the same SessionFactory that you built and opened the Session in the program. The source code in the question part doesn't guaranty this and hence the trouble.
Please have a look at the following simple and straight forward solution,
//your class definition
public static void main(String args[]) {
SessionFactory sessionFactory = getSessionFactory();
Session session = sessionFactory.openSession();
session.beginTransaction().begin();
session.save(yourEntity);
session.beginTransaction().commit();
session.close();
sessionFactory.close();
}
public static SessionFactory getSessionFactory() {
Configuration cfg = new Configuration();
cfg.configure("com/srees/hibernate.cfg.xml");
SessionFactory sessionFactory = cfg.buildSessionFactory();
return sessionFactory;
}
//your class def ends here
My requirement is as follows:
I need to restart(or rebuild) hibernate session factory in my Spring web application at frequent intervals with the new HBM files I get from outside.
Currently my Sessionfactory class is as follows with a SessionFactory Proxy to intercept 'OpenSession' call.
There I am checking for a condition to restart and rebuilding sessionFactory.
My problem here is , in concurrent environment the other users who are in middle of other transaction is getting affected during this restart.
Is there anyway to carryout the restart by checking all the transactions and open sessions and carry out rebuilding session factory once all other completes?
or any other solution exists.
Code:
public class DataStoreSessionFactory extends LocalSessionFactoryBean
{
private boolean restartFactory = false;
#Override
protected void postProcessConfiguration(Configuration config) throws HibernateException
{
super.postProcessConfiguration(config);
updateHBMList(config);
}
private void updateHBMList(final Configuration config)
{
config.addXML(modelRegistry.generateMapping());
}
#Override
public SessionFactory getObject()
{
Object obj = super.getObject();
/*
* Invocation handler for the proxy
*/
SessionFactoryProxy proxy = new SessionFactoryProxy(this, (SessionFactory) obj);
/**
* All the methods invoked on the returned session factory object will pass through this proxy's invocation
* handler
*/
SessionFactory sessionFactory = (SessionFactory) Proxy.newProxyInstance(getClass().getClassLoader(),
new Class[] { SessionFactory.class },
proxy);
return sessionFactory;
}
static class SessionFactoryProxy implements InvocationHandler
{
private SessionFactory sessionFactory;
private LocalSessionFactoryBean factoryBean;
public SessionFactoryProxy(LocalSessionFactoryBean factoryBean, SessionFactory sessionFactory)
{
this.factoryBean = factoryBean;
this.sessionFactory = sessionFactory;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
/**
* Only if the method invoked is openSession - check if the session factory should be restarted, and only then
* invoke the requested method
*/
if (method.getName().equals("openSession"))
{
restartSessionFactoryIfNecessary();
}
return method.invoke(sessionFactory, args);
}
private void restartSessionFactoryIfNecessary()
{
restartSessionFactory();
/*if (((DataStoreSessionFactory) factoryBean).isRestartFactory())
{
restartSessionFactory();
}*/
}
private synchronized void restartSessionFactory()
{
log.info("Restarting session...");
factoryBean.destroy();
try
{
factoryBean.afterPropertiesSet();
sessionFactory = factoryBean.getObject();
}
catch (Exception e)
{
log.error("Error while restarting session: " + e.getMessage());
throw new RuntimeException(e);
}
}
}
Thanks,
Appasamy
You can following SessionFactoryUtils to determine whether a transaction is taking place in Session factory and then decide to restart the session factory or not:
You will need to import--> org.springframework.orm.hibernate.SessionFactoryUtils in you file, and use the following API.
static boolean hasTransactionalSession(SessionFactory sessionFactory);
Above API returns whether there is a transactional Hibernate Session for the current thread, that is, a Session bound to the current thread by Spring's transaction facilities.There is also another API just in case if you need to check if a session is transactional in session factory currently:
static boolean isSessionTransactional(Session session,SessionFactory sessionFactory);
Above API returns whether the given particular Hibernate Session is transactional, that is, bound to the current thread by Spring's transaction facilities.
I am new to the Java / Hibernate / Seam way of development but I appear to have a strange issue with Hibernate and concurrent threads.
I have a application scoped Seam component which is executed via EJB timers at a set interval (Orchestrator.java) calling the method startProcessingWorkloads.
This method has a injected EntityManager which it uses to check the database for a collection of data, and if it finds a work collection it creates a new Asynchronous Seam component (LoadContoller.java) and executes the start() method on the Controller
LoadController has EntityManager injected and use it to perform a very large transaction (About one hour)
Once the LoadController is running as a separate thread, the Orchestrator is still being executed as a thread at a set interval, so for example
1min
Orchestrator - Looks for work collection (None found) (thread 1)
2min
Orchestrator - Looks for work collection (finds one, Starts LoadController) (thread 1)
LoadController - Starts updating database records (thread 2)
3min
Orchestrator - Looks for work collection (None found) (thread 1)
LoadController - Still updating database records (thread 2)
4min
Orchestrator - Looks for work collection (None found) (thread 1)
LoadController - Still updating database records (thread 2)
5min
Orchestrator - Looks for work collection (None found) (thread 1)
LoadController - Done updating database records (thread 2)
6min
Orchestrator - Looks for work collection (None found) (thread 1)
7min
Orchestrator - Looks for work collection (None found) (thread 1)
However, I am receiving a intermittent error (See below) when the Orchestrator runs concurrently with the LoadController.
5:10:40,852 WARN [AbstractBatcher]
exception clearing
maxRows/queryTimeout
java.sql.SQLException: Connection is
not associated with a managed
connection.org.jboss.resource.adapter.jdbc.jdk6.WrappedConnectionJDK6#1fcdb21
This error is thrown after the Orchestrator has completed its SQl query and as the LoadController attempts to execute its next SQl query.
I did some research I came to the conclusion that the EntityManager was being closed hence the LoadController was unable to use it.
Now confused as to what exactly closed the connection I did some basic object dumps of the entity manager objects used by the Orchestrator and the LoadController when each of the components are called and I found that just before I receive the above error this happens.
2010-07-30 15:06:40,804 INFO
[processManagement.LoadController]
(pool-15-thread-2)
org.jboss.seam.persistence.EntityManagerProxy#7e3da1
2010-07-30 15:10:40,758 INFO
[processManagement.Orchestrator]
(pool-15-thread-1)
org.jboss.seam.persistence.EntityManagerProxy#7e3da1
It appears that during one of the Orchestrator execution intervals it obtains a reference to the same EntityManager that the LoadController is currently using. When the Orchestrator completes its SQL execution it closes the connection and than LoadController can no longer execute its updates.
So my question is, does any one know of this happening or having I got my threading all mucked up in this code?
From my understanding when injecting a EntityManager a new instance is injected from the EntityManagerFactory which remains with that particualr object until object leaves scope (in this case they are stateless so when the start() methods ends), how could the same instance of a entity manager be injected into two separate threads?
Orchestrator.java
#Name("processOrchestrator")
#Scope(ScopeType.APPLICATION)
#AutoCreate
public class Orchestrator {
//___________________________________________________________
#Logger Log log;
#In EntityManager entityManager;
#In LoadController loadController;
#In WorkloadManager workloadManager;
//___________________________________________________________
private int fProcessInstanceCount = 0;
//___________________________________________________________
public Orchestrator() {}
//___________________________________________________________
synchronized private void incrementProcessInstanceCount() {
fProcessInstanceCount++;
}
//___________________________________________________________
synchronized private void decreaseProcessInstanceCount() {
fProcessInstanceCount--;
}
//___________________________________________________________
#Observer("controllerExceptionEvent")
synchronized public void controllerExceptionListiner(Process aProcess, Exception aException) {
decreaseProcessInstanceCount();
log.info(
"Controller " + String.valueOf(aProcess) +
" failed with the error [" + aException.getMessage() + "]"
);
Events.instance().raiseEvent(
Application.ApplicationEvent.applicationExceptionEvent.name(),
aException,
Orchestrator.class
);
}
//___________________________________________________________
#Observer("controllerCompleteEvent")
synchronized public void successfulControllerCompleteListiner(Process aProcess, long aWorkloadId) {
try {
MisWorkload completedWorklaod = entityManager.find(MisWorkload.class, aWorkloadId);
workloadManager.completeWorkload(completedWorklaod);
} catch (Exception ex) {
log.error(ex.getMessage(), ex);
}
decreaseProcessInstanceCount();
log.info("Controller " + String.valueOf(aProcess) + " completed successfuly");
}
//___________________________________________________________
#Asynchronous
public void startProcessingWorkloads(#IntervalDuration long interval) {
log.info("Polling for workloads.");
log.info(entityManager.toString());
try {
MisWorkload pendingWorkload = workloadManager.getNextPendingWorkload();
if (pendingWorkload != null) {
log.info(
"Pending Workload found (Workload_Id = " +
String.valueOf(pendingWorkload.getWorkloadId()) +
"), starting process controller."
);
Process aProcess = pendingWorkload.retriveProcessIdAsProcess();
ControllerIntf controller = createWorkloadController(aProcess);
if (controller != null) {
controller.start(aProcess, pendingWorkload.getWorkloadId());
workloadManager.setWorkloadProcessing(pendingWorkload);
}
}
} catch (Exception ex) {
Events.instance().raiseEvent(
Application.ApplicationEvent.applicationExceptionEvent.name(),
ex,
Orchestrator.class
);
}
log.info("Polling complete.");
}
//___________________________________________________________
private ControllerIntf createWorkloadController(Process aProcess) {
ControllerIntf newController = null;
switch(aProcess) {
case LOAD:
newController = loadController;
break;
default:
log.info(
"createWorkloadController() does not know the value (" +
aProcess.name() +
") no controller will be started."
);
}
// If a new controller is created than increase the
// count of started controllers so that we know how
// many are running.
if (newController != null) {
incrementProcessInstanceCount();
}
return newController;
}
//___________________________________________________________
}
LoadController.java
#Name("loadController")
#Scope(ScopeType.STATELESS)
#AutoCreate
public class LoadController implements ControllerIntf {
//__________________________________________________
#Logger private Log log;
#In private EntityManager entityManager;
//__________________________________________________
private String fFileName = "";
private String fNMDSFileName = "";
private String fAddtFileName = "";
//__________________________________________________
public LoadController(){ }
//__________________________________________________
#Asynchronous
synchronized public void start(Process aProcess, long aWorkloadId) {
log.info(
LoadController.class.getName() +
" process thread was started for WorkloadId [" +
String.valueOf(aWorkloadId) + "]."
);
log.info(entityManager.toString());
try {
Query aQuery = entityManager.createQuery(
"from MisLoad MIS_Load where Workload_Id = " + String.valueOf(aWorkloadId)
);
MisLoad misLoadRecord = (MisLoad)aQuery.getSingleResult();
fFileName =
misLoadRecord.getInitiatedBy().toUpperCase() + "_" +
misLoadRecord.getMdSourceSystem().getMdState().getShortName() + "_" +
DateUtils.now(DateUtils.FORMAT_FILE) + ".csv"
;
fNMDSFileName = "NMDS_" + fFileName;
fAddtFileName = "Addt_" + fFileName;
createDataFile(misLoadRecord.getFileContents());
ArrayList<String>sasCode = generateSASCode(
misLoadRecord.getLoadId(),
misLoadRecord.getMdSourceSystem().getPreloadFile()
);
//TODO: As the sas password will be encrypted in the database, we will
// need to decrypt it before passing to the below function
executeLoadSASCode(
sasCode,
misLoadRecord.getInitiatedBy(),
misLoadRecord.getSasPassword()
);
createWorkloadContentRecords(aWorkloadId, misLoadRecord.getLoadId());
//TODO: Needs to remove password from DB when complete
removeTempCSVFiles();
Events.instance().raiseEvent(
Application.ApplicationEvent.controllerCompleteEvent.name(),
aProcess,
aWorkloadId
);
log.info(LoadController.class.getName() + " process thread completed.");
} catch (Exception ex) {
Events.instance().raiseEvent(
Application.ApplicationEvent.controllerExceptionEvent.name(),
aProcess,
ex
);
}
}
//__________________________________________________
private void createDataFile(byte[] aFileContent) throws Exception {
File dataFile =
new File(ECEConfig.getConfiguration().sas_tempFileDir() + "\\" + fFileName);
FileUtils.writeBytesToFile(dataFile, aFileContent, true);
}
//__________________________________________________
private ArrayList<String> generateSASCode(long aLoadId, String aSourceSystemPreloadSasFile) {
String sasTempDir = ECEConfig.getConfiguration().sas_tempFileDir();
ArrayList<String> sasCode = new ArrayList<String>();
sasCode.add("%let sOracleUserId = " + ECEConfig.getConfiguration().oracle_username() + ";");
sasCode.add("%let sOraclePassword = " + ECEConfig.getConfiguration().oracle_password() + ";");
sasCode.add("%let sOracleSID = " + ECEConfig.getConfiguration().oracle_sid() + ";");
sasCode.add("%let sSchema = " + ECEConfig.getConfiguration().oracle_username() + ";");
sasCode.add("%let sECESASSourceDir = " + ECEConfig.getConfiguration().sas_sourceDir() + ";");
sasCode.add("libname lOracle ORACLE user=&sOracleUserId pw=&sOraclePassword path=&sOracleSID schema=&sSchema;");
sasCode.add("%let sCommaDelimiter = %str(" + ECEConfig.getConfiguration().dataload_csvRawDataFileDelimiter() + ");");
sasCode.add("%let sPipeDelimiter = %nrquote(" + ECEConfig.getConfiguration().dataload_csvNMDSDataFileDelimiter() + ");");
sasCode.add("%let sDataFileLocation = " + sasTempDir + "\\" + fFileName + ";");
sasCode.add("%let sNMDSOutputDataFileLoc = " + sasTempDir + "\\" + fNMDSFileName + ";");
sasCode.add("%let sAddtOutputDataFileLoc = " + sasTempDir + "\\" + fAddtFileName + ";");
sasCode.add("%let iLoadId = " + String.valueOf(aLoadId) + ";");
sasCode.add("%include \"&sECESASSourceDir\\ECE_UtilMacros.sas\";");
sasCode.add("%include \"&sECESASSourceDir\\" + aSourceSystemPreloadSasFile + "\";");
sasCode.add("%include \"&sECESASSourceDir\\ECE_NMDSLoad.sas\";");
sasCode.add("%preload(&sDataFileLocation, &sCommaDelimiter, &sNMDSOutputDataFileLoc, &sAddtOutputDataFileLoc, &sPipeDelimiter);");
sasCode.add("%loadNMDS(lOracle, &sNMDSOutputDataFileLoc, &sAddtOutputDataFileLoc, &sPipeDelimiter, &iLoadId);");
return sasCode;
}
//__________________________________________________
private void executeLoadSASCode(
ArrayList<String> aSasCode, String aUserName, String aPassword) throws Exception
{
SASExecutor aSASExecutor = new SASExecutor(
ECEConfig.getConfiguration().sas_server(),
ECEConfig.getConfiguration().sas_port(),
aUserName,
aPassword
);
aSASExecutor.execute(aSasCode);
log.info(aSASExecutor.getCompleteSasLog());
}
//__________________________________________________
/**
* Creates the MIS_UR_Workload_Contents records for
* the ECE Unit Record data that was just loaded
*
* #param aWorkloadId
* #param aMisLoadId
* #throws Exception
*/
private void createWorkloadContentRecords(long aWorkloadId, long aMisLoadId) throws Exception {
String selectionRule =
" from EceUnitRecord ECE_Unit_Record where ECE_Unit_Record.loadId = " +
String.valueOf(aMisLoadId)
;
MisWorkload misWorkload = entityManager.find(MisWorkload.class, aWorkloadId);
SeamManualTransaction manualTx = new SeamManualTransaction(
entityManager,
ECEConfig.getConfiguration().manualSeamTxTimeLimit()
);
manualTx.begin();
RecordPager oPager = new RecordPager(
entityManager,
selectionRule,
ECEConfig.getConfiguration().recordPagerDefaultPageSize()
);
Object nextRecord = null;
while ((nextRecord = oPager.getNextRecord()) != null) {
EceUnitRecord aEceUnitRecord = (EceUnitRecord)nextRecord;
MisUrWorkloadContents aContentsRecord = new MisUrWorkloadContents();
aContentsRecord.setEceUnitRecordId(aEceUnitRecord.getEceUnitRecordId());
aContentsRecord.setMisWorkload(misWorkload);
aContentsRecord.setProcessOutcome('C');
entityManager.persist(aContentsRecord);
}
manualTx.commit();
}
/**
* Removes the CSV temp files that are created for input
* into the SAS server and that are created as output.
*/
private void removeTempCSVFiles() {
String sasTempDir = ECEConfig.getConfiguration().sas_tempFileDir();
File dataInputCSV = new File(sasTempDir + "\\" + fFileName);
File nmdsOutputCSV = new File(sasTempDir + "\\" + fNMDSFileName);
File addtOutputCSV = new File(sasTempDir + "\\" + fAddtFileName);
if (dataInputCSV.exists()) {
dataInputCSV.delete();
}
if (nmdsOutputCSV.exists()) {
nmdsOutputCSV.delete();
}
if (addtOutputCSV.exists()) {
addtOutputCSV.delete();
}
}
}
SeamManualTransaction.java
public class SeamManualTransaction {
//___________________________________________________________
private boolean fObjectUsed = false;
private boolean fJoinExistingTransaction = true;
private int fTransactionTimeout = 60; // Default: 60 seconds
private UserTransaction fUserTx;
private EntityManager fEntityManager;
//___________________________________________________________
/**
* Set the transaction timeout in milliseconds (from minutes)
*
* #param aTimeoutInMins The number of minutes to keep the transaction active
*/
private void setTransactionTimeout(int aTimeoutInSecs) {
// 60 * aTimeoutInSecs = Timeout in Seconds
fTransactionTimeout = 60 * aTimeoutInSecs;
}
//___________________________________________________________
/**
* Constructor
*
* #param aEntityManager
*/
public SeamManualTransaction(EntityManager aEntityManager) {
fEntityManager = aEntityManager;
}
//___________________________________________________________
/**
* Constructor
*
* #param aEntityManager
* #param aTimeoutInSecs
*/
public SeamManualTransaction(EntityManager aEntityManager, int aTimeoutInSecs) {
setTransactionTimeout(aTimeoutInSecs);
fEntityManager = aEntityManager;
}
//___________________________________________________________
/**
* Constructor
*
* #param aEntityManager
* #param aTimeoutInSecs
* #param aJoinExistingTransaction
*/
public SeamManualTransaction(EntityManager aEntityManager, int aTimeoutInSecs, boolean aJoinExistingTransaction) {
setTransactionTimeout(aTimeoutInSecs);
fJoinExistingTransaction = aJoinExistingTransaction;
fEntityManager = aEntityManager;
}
//___________________________________________________________
/**
* Starts the new transaction
*
* #throws Exception
*/
public void begin() throws Exception {
if (fObjectUsed) {
throw new Exception(
SeamManualTransaction.class.getCanonicalName() +
" has been used. Create new instance."
);
}
fUserTx =
(UserTransaction) org.jboss.seam.Component.getInstance("org.jboss.seam.transaction.transaction");
fUserTx.setTransactionTimeout(fTransactionTimeout);
fUserTx.begin();
/* If entity manager is created before the transaction
* is started (ie. via Injection) then it must join the
* transaction
*/
if (fJoinExistingTransaction) {
fEntityManager.joinTransaction();
}
}
//___________________________________________________________
/**
* Commit the transaction to the database
*
* #throws Exception
*/
public void commit() throws Exception {
fObjectUsed = true;
fUserTx.commit();
}
//___________________________________________________________
/**
* Rolls the transaction back
*
* #throws Exception
*/
public void rollback() throws Exception {
fObjectUsed = true;
fUserTx.rollback();
}
//___________________________________________________________
}
In general, injecting an entityManager in a Seam component of scope APPLICATION is not right. An entity manager is something you create, use and close again, in a scope typically much shorter than APPLICATION scope.
Improve by choosing smaller scopes with a standard entityManager injection, or if you need the APPLICATION scope, inject an EntityManagerFactory instead, and create, use and close the entityManager yourself.
Look in your Seam components.xml to find the name of your EntityManagerFactory compoment.
Well, my first is advice is
If you are using an EJB application, prefer To use a Bean Managed Transaction instead of your custom SeamManualTransaction. When you use a Bean Managed Transaction, you, as a developer, Take care of calling begin and commit. You get this feature by using an UserTransaction component. You can create a Facade layer which begins and commit your Transaction. Something like
/**
* default scope when using #Stateless session bean is ScopeType.STATELESS
*
* So you do not need to declare #Scope(ScopeType.STATELESS) anymore
*
* A session bean can not use both BEAN and CONTAINER Transaction management at The same Time
*/
#Stateless
#Name("businessFacade")
#TransactionManagement(TransactionManagerType.BEAN)
public class BusinessFacade implements BusinessFacadeLocal {
private #Resource TimerService timerService;
private #Resource UserTransaction userTransaction;
/**
* You can use #In of you are using Seam capabilities
*/
private #PersistenceContext entityManager;
public void doSomething() {
try {
userTransaction.begin();
userTransaction.setTransactionTimeout(int seconds);
// business logic goes here
/**
* To enable your Timer service, just call
*
* timerService.createTimer(15*60*1000, 15*60*1000, <ANY_SERIALIZABLE_INFO_GOES_HERE>);
*/
userTransaction.commit();
} catch (Exception e) {
userTransaction.rollback();
}
}
#Timeout
public void doTimer(Timer timer) {
try {
userTransaction.begin();
timer.getInfo();
// logic goes here
userTransaction.commit();
} catch (Exception e) {
userTransaction.rollback();
}
}
}
Let's see UserTransaction.begin method API
Create a new transaction and associate it with the current thread
There is more:
The lifetime of a container-managed persistence context (injected Through #PersistenceContext annotation) corresponds to the scope of a transaction (between begin and commit method call) when using transaction-scoped persistence context
Now Let's see TimerService
It is a container-provided service that allows enterprise beans to be registered for
timer callback methods to occur at a specified time, after a specified elapsed time, or after specified intervals. The bean class of an enterprise bean that uses the timer
service must provide a timeout callback method. Timers can be created for stateless session beans, message-driven beans
I hope It can be useful To you
In Hibernate when i save() an object in a transaction, and then i rollback it, the saved object still remains in the DB. It's strange because this issue doesn't happen with the update() or delete() method, just with save().
Here is the code i'm using:
DbEntity dbEntity = getDbEntity();
HibernateUtil.beginTransaction();
Session session = HibernateUtil.getCurrentSession();
session.save(dbEntity);
HibernateUtil.rollbackTransaction();
And here is the HibernateUtil class (just the involved functions, i guarantee the getSessionFactory() method works well - there is an Interceptor handler, but it doesn't matter now):
private static final ThreadLocal<Session> threadSession = new ThreadLocal<Session>();
private static final ThreadLocal<Transaction> threadTransaction = new ThreadLocal<Transaction>();
/**
* Retrieves the current Session local to the thread.
* <p/>
* If no Session is open, opens a new Session for the running thread.
*
* #return Session
*/
public static Session getCurrentSession()
throws HibernateException {
Session s = (Session) threadSession.get();
try {
if (s == null) {
log.debug("Opening new Session for this thread.");
if (getInterceptor() != null) {
log.debug("Using interceptor: " + getInterceptor().getClass());
s = getSessionFactory().openSession(getInterceptor());
} else {
s = getSessionFactory().openSession();
}
threadSession.set(s);
}
} catch (HibernateException ex) {
throw new HibernateException(ex);
}
return s;
}
/**
* Start a new database transaction.
*/
public static void beginTransaction()
throws HibernateException {
Transaction tx = (Transaction) threadTransaction.get();
try {
if (tx == null) {
log.debug("Starting new database transaction in this thread.");
tx = getCurrentSession().beginTransaction();
threadTransaction.set(tx);
}
} catch (HibernateException ex) {
throw new HibernateException(ex);
}
}
/**
* Rollback the database transaction.
*/
public static void rollbackTransaction()
throws HibernateException {
Transaction tx = (Transaction) threadTransaction.get();
try {
threadTransaction.set(null);
if ( tx != null && !tx.wasCommitted() && !tx.wasRolledBack() ) {
log.debug("Tyring to rollback database transaction of this thread.");
tx.rollback();
}
} catch (HibernateException ex) {
throw new HibernateException(ex);
} finally {
closeSession();
}
}
Thanks
Check if your database supports a roll back i.e. if you're using InnoDB tables and not MyISAM (you can mix transactional and non-transactional tables but in most cases, you want all your tables to be InnoDB).
MySQL by default uses the MyIsam storage engine. As the MyISAM does not support transactions, insert, update and delete statements are directly written to the database. The commit and rollback statements are ignored.
In order to use transaction you need to change the storage engine of you tables. Use this command:
ALTER TABLE table_name ENGINE = InnoDB;
(note how ever, that the two storage engines are different and you need to test you're application if it still behaves as expected)