I'm new to Hibernate. I've created the SessionFactory and obtaining the Session like below. And I'm using c3p0 connection provider
public static SessionFactory getSessionFactory() {
try {
if (sessionFactory == null) {
Configuration configuration = loadDBConfiguration();
if (configuration != null) {
sessionFactory = configuration.buildSessionFactory();
} else {
log.info("---- Configuration failed ----");
}
}
} catch (Exception ex) {
log.info("---- Initial SessionFactory creation failed ----");
}
return sessionFactory;
}
public static EntityManagerFactory getEntityManagerFactory() {
try {
Session session = getSessionFactory().openSession();
entityManagerFactory = session.getEntityManagerFactory();
} catch (Exception e) {
log.error(e);
}
return entityManagerFactory;
}
public static EntityManager getEntityManager() {
try {
EntityManagerFactory entityManagerFactory = getEntityManagerFactory();
return entityManagerFactory.createEntityManager();
} catch (Exception e) {
log.error(e);
}
return null;
}
And i'm confused with below things.
Should i close the connection after commit(), or c3p0 connection provider will close after commit().
Shall i call getEntityManager() on each request or should i use like singleton
If i'm used as singleton, then does it affect any parallel transaction.begin() or transaction.commit() if server receives multiple request at same time.
Also in singleton the entity still persist in session until it gets closed or clear manually. So what i need to do in this case.
Currently the getEntityManager() gets called in all my EntityManager Classes like UserManager, AccountsManager. After some period of time in RDS it shows like 20connections, even my application not handling any user requests.
You should close your connections. Since your connections are wrapped by the database connection pool, they will not get physically closed, but they will return to the pool. This needs to be done otherwise the connection pool will think the connections are being used and when you hit the pool limit, no new connections will be open.
Keep the session factory as a singleton. You should open a new session for each thread.
Related
I am using hibernate in my automation testing project, to execute a database 'clean-up routine' that is:
disabling constraints for all tables in the database
removing records by ID's that I stored when creating records used for my automation testing
enabling constraints for all tables in the database
Here is my pseudo code:
private SessionFactory sessionFactory;
private void initialize()
{
try
{
Configuration config = createHibernateConfiguration();
addAnnotatedClassesForClientDB(config);
StandardServiceRegistryBuilder serviceRegistryBuilder = new StandardServiceRegistryBuilder();
serviceRegistryBuilder.applySettings(config.getProperties());
serviceRegistry = serviceRegistryBuilder.build();
sessionFactory = config.buildSessionFactory(serviceRegistry);
}
catch (HibernateException e)
{
logger.error("Problem creating session factory!");
e.printStackTrace();
}
}
public Session openSession()
{
Session session = sessionFactory.openSession();
session.beginTransaction();
return session;
}
public void cleanClientDatabase()
{
Session session = openSession();
try
{
logger.info("Client DB cleaning started...");
String combinedQuery = // her comes my SQL query
Query query = session.createNativeQuery(combinedQuery);
query.executeUpdate();
closeSession(session);
}
catch (Exception e)
{
logger.error("Failed cleaning Client DB! " + e.getClass().getSimpleName());
e.printStackTrace();
session.getTransaction().rollback();
session.close();
}
}
Now from time to time, it sticks at query.executeUpdate(); in cleanClientDatabase() method and will hang there forever, until I manually kill transaction in Microsoft SQL Management studio by PID.
For some reason an exception is never thrown so I can't tell what is the error, I suspect some sort of lock, what can I do to avoid this issue and fix my code?
Thank you.
In my web application, when a new user sign-up i need to create a new database in mysql for him. Example: root => db_root, admin => db_admin, user1 => db_user1 and so on. When the user logs into my app, I need to instance a new entityManager based on his new created database and manipulate data only on that db.
One problem is: how am i supposed to know which entityManager instance i should use for each user request. I thought about creating a
Hashmap<String, EntityManager>
save an attribute entityManagerKey on user's session and retrieve the right entityManager by this key. This approach brings along some troubles such as the right time to destroy a non-used entityManager. Moreover, i think it will take all server memory to work.
Though it's seems to be not a great idea, i did't realize another solution. I'm using JPA/Hibernate and Jersey. Any ideas will be appreciated, except creating only one database.
Thanks!
You can use the EntityManagerFactory and pass it the user and password
Map<String, String> properties = new HashMap<String, String>();
properties.put("javax.persistence.jdbc.user", "admin");
properties.put("javax.persistence.jdbc.password", "admin");
EntityManagerFactory emf = Persistence.createEntityManagerFactory(
"some-jdbc-url", properties);
EntityManager entityManager = emf.createEntityManager();
If you think performance is an issue, you can cache the entityManager. Depending on how much traffic you have on your application and the available resources, you can determine the number of cached connections. I wouldn't bother with caching in the beginning, because creating a DB connection is usually not that time consuming, compared to a web request.
Here is the sample code,
private static final Map<String, EntityManagerFactory> ENTITY_FACTORIES = new HashMap<String, EntityManagerFactory>();
public void onStart() {
this.buildEntityManagerFactories();
}
#Override
public void onStop() {
closeEMFactories();
}
private void closeEMFactories() {
try {
if(ENTITY_FACTORIES.size() > 0) {
for(Entry<String, EntityManagerFactory> entityFactoryMgr : ENTITY_FACTORIES.entrySet()) {
EntityManagerFactory entityManagerFactory = entityFactoryMgr.getValue();
if(entityManagerFactory.isOpen()) {
entityManagerFactory.close();
}
}
isLoaded = false;
}
} catch (Exception e) {
Logger.error("Error while shutting down the datasource plugin", e);
}
}
/**
* Create the entityManagerFactory Bean.
* #return entityManagerFactory Bean
*/
public void buildEntityManagerFactories(String userName) {
buildEMFactory(userName, DEFAULT_PERSISTENCE_UNIT);
}
private void buildEMFactory(String userName, String persistenceUnitName) {
if(!isLoaded) {
try{
ENTITY_FACTORIES.put(userName, Persistence.createEntityManagerFactory(persistenceUnitName, XOAPP_DB_PROPERTIES));
}catch(Exception e) {
Logger.error("Error while building the entity manager factory for the persistence unit :" + persistenceUnitName, e);
}
}
}
public EntityManager em(String persistenceName) {
EntityManagerFactory entityManagerFactory = ENTITY_FACTORIES.get(persistenceName);
if(entityManagerFactory != null) {
return entityManagerFactory.createEntityManager();
}
return null;
}
/**
* Get the entityManagerFactory Bean.
* #return entityManagerFactory Bean
*/
public EntityManagerFactory getEntityManagerFactory(String userName) {
EntityManagerFactory entityManagerFactory = ENTITY_FACTORIES.get(userName);
if(entityManagerFactory == null) {
buildEMFactory(userName);
entityManagerFactory = ENTITY_FACTORIES.get(userName);
}
return entityManagerFactory;
}
This is my connection detail in JBoss standalone.xml
<connection-url>
jdbc:oracle:thin:#(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=1521))(ADDRESS=(PROTOCOL=TCP)(HOST=xx.1xx.119.1xx)(PORT=1521))(LOAD_BALANCE=on)(FAILOVER=on))(CONNECT_DATA=(SERVICE_NAME=XE)))
</connection-url>
I want to handle a corner case of failover where post getting EntityManager object during a call of persist(), the connection is lost. Failover option is not switching to next database in the same transaction, it switches to active connection in the next transaction. I attempted something like this: (Catch Exception and get updated bean object)
public EntityManager getEntityManager() {
try {
entityManager = getEntityManagerDao(Constant.JNDI_NFVD_ASSURANCE_ENTITY_MANAGER);
} catch (NamingException e) {
LOGGER.severe("Data could not be persisted.");
throw new PersistenceException();
}
return entityManager.getEntityManager();
}
/**
* Inserts record in database. In case multiple connections/databases exist, one more attempt will be made to
* insert record.
*
* #param entry
*/
public void persist(Object entry) {
try {
getEntityManager().persist(entry);
} catch (PersistenceException pe) {
LOGGER.info("Could not persist data. Trying new DB connection.");
getEntityManager().persist(entry);
}
}
private static Object getJNDIObject(String path) throws NamingException {
Object jndiObject = null;
InitialContext initialContext = new InitialContext();
jndiObject = initialContext.lookup(path);
return jndiObject;
}
private static AssuranceEntityManager getEntityManagerDao(String path) throws NamingException {
return (AssuranceEntityManager) getJNDIObject(path);
}
But this one also is not helping. After catching the exception, getting a new bean with JNDI lookup does not contain an updated new connection and an exception is thrown. This results in loss of data of that transaction.
Please suggest how to handle this corner case of "Connection lost post getting EntityManager and before persisting."
I think it's quite impossible what you want to achieve. The thing is that if internal DB transction is aborted then the JTA transaction is in abort state and you can't continue with it.
I expect it's kind of similar to this case
#Stateless
public class TableCreator {
#Resource
DataSource datasource;
#TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void create() {
try(Connection connection = datasource.getConnection()) {
Statement st = connection.createStatement();
st.execute("CREATE TABLE user (id INTEGER NOT NULL, name VARCHAR(255))");
} catch (SQLException sqle) {
// ignore this as table already exists
}
}
}
#Stateless
public class Inserter {
#EJB
private TableCreator creator;
public void call() {
creator.create();
UserEntity entity = new UserEntity(1, "EAP QE");
em.persist(entity);
}
}
In case that table user exists and you would use annotation #TransactionAttribute(TransactionAttributeType.REQUIRED) then the create call will be part of the same jta global transaction as call of persist. As in such case the transaction was aborted the persist call would fail with exception like (postgresql case)
Caused by: org.postgresql.util.PSQLException: ERROR: current transaction is aborted, commands ignored until end of transaction block
I mean if Oracle jdbc driver is not able to to handle connection fail transparently to JBoss app server and throws the exception upwards then I think that the only possible solution is to repeat the whole update action.
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 have buit a Webservice using these technologies + c3p0 for database handling. It works ok most of the time but I have a 3-5% ratio (sometimes even a 10%) of failed acces due to this error.
I am using Hibernate this way:
-Session Factory
private static SessionFactory buildSessionFactory() {
try {
Configuration configuration = new Configuration();
configuration.configure();
serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
// Create the SessionFactory from hibernate.cfg.xml
return configuration
.buildSessionFactory(serviceRegistry);
} catch (Throwable ex) {
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
//reabrimos la sesion si esta cerrada al liberar los recursos
if(sessionFactory.isClosed())
{
System.out.println("Reopen session");
sessionFactory.openSession();
}
return sessionFactory;
}
Then in my hibernate.cfg.xml I have the following line:
<property name="current_session_context_class">thread</property>
Finally in my endpoints I have defined a hibernate_session class which I use as follows:
#Path("/projects")
public class ProjectServiceImpl {
#Context
SecurityContext security;
Session hibernate_session = null;
#POST
#Path("sync.json")
#Produces(value = {"application/json",
"application/vnd.myapp-v1+json",
"application/vnd.myapp-v2+json"})
public Response syncProjects(
#DefaultValue("") #FormParam("projects") String in_projects_str,
#DefaultValue("0") #FormParam("last_sync") long last_sync,
#Context Request request) {
//...
hibernate_session = HibernateUtil.getSessionFactory()
.getCurrentSession();
if (hibernate_session == null) {
ResponseMessage rm = new ResponseMessage();
rm.setCode(Status.INTERNAL_SERVER_ERROR.getStatusCode());
rm.setMessage("Hibernate Session is Null");
rm.setType("ERROR");
return Response.status(Status.INTERNAL_SERVER_ERROR).entity(rm)
.type("application/json").build();
}
try {
hibernate_session.beginTransaction();
//Database work...
hibernate_session.flush();
hibernate_session.getTransaction().commit();
}catch (RuntimeException | IllegalAccessException
| InvocationTargetException e) {
try {
if (hibernate_session.getTransaction() != null) {
hibernate_session.getTransaction().rollback();
}
} catch (RuntimeException rbe) {
System.err.println("Couldn’t roll back transaction");
}
e.printStackTrace();
ResponseMessage rm = new ResponseMessage();
rm.setCode(Status.INTERNAL_SERVER_ERROR.getStatusCode());
rm.setMessage(e.getMessage());
rm.setType("ERROR");
return Response.status(Status.INTERNAL_SERVER_ERROR).entity(rm)
.type("application/json").build();
}
}
return Response.ok().entity(result_entity)
.type("application/json").build();
}
My hibernate_session is a class attribute, do I have to change it to a local variable?. As far as I know the end points will be executed in different threads so I have assumed that I am working with different instances of my endpoint container class and these class attributes will not get overriden by multiple request.
Any light you can shed on this topic will be appreciated,
Thanks in advance
Thanks all for your replies. I finally managed to solve the problem.
In one of my multiple entries there was a begin transaction (necessary to create criterias) but was not commited. The result was that a reused thread that had called that method before would throw a nested exception. By commiting the transaction the problem was solved :)
You aren't using openSession and getCurrentSession properly.
public static SessionFactory getSessionFactory() {
//reabrimos la sesion si esta cerrada al liberar los recursos
//change this: if(sessionFactory.isClosed()) to this:
if(sessionFactory == null || sessionFactory.isClosed())
{
System.out.println("Reopen session"); // Really setup session factory
//change this: sessionFactory.openSession(); to this:
sessionFactory = buildSessionFactory();
}
return sessionFactory;
}
That's not the problem though, your code there just isn't doing what it's supposed to. You need to change:
hibernate_session = HibernateUtil.getSessionFactory().getCurrentSession();
to
hibernate_session = HibernateUtil.getSessionFactory().openSession();
As per the SessionFactory Javadoc:
Obtains the current session. The definition of what exactly "current" means controlled by the CurrentSessionContext impl configured for use.
It's safe to assume your CurrentSessionContext is not thread safe.
It seems that a transaction is started, and before the transaction gets commited an attempt to start a new transaction is made.
This explains the error message that says that a nested transaction (the second transaction inside the ongoing transaction) is not supported.
This could be caused for example by incorrect error handling, for example starting a transaction, not catching an exception or catch and ignore and then try to begin a second transaction without having done either commit or rollback.
An idiom similar to this one should be used when doing programmatic transaction mananagement:
try {
sess.getTransaction().begin();
// do some work
sess.getTransaction().commit()
}
catch (RuntimeException e) {
sess.getTransaction().rollback();
throw e;
}
Also important to bear mind is that after a rollback the session cannot be reused, as it's in an inconsistent state.
If using a framework like Spring, the use of the annotation #Transactional for declarative transaction management solves most of these problems for us and leads to more maintainable code, EJB3 has also similar functionality.