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).
Related
I working with PostgreSQL in multithread mode by Hibernate sessions. Init:
<property name="connection.driver_class">org.postgresql.Driver</property>
<property name="hibernate.current_session_context_class">thread</property>
(other strings is connection URL and entity xml describe))
HibernateUtil:
public class HibernateUtil {
private static SessionFactory sessionFactory;
static {
try {
Configuration configuration = new Configuration();
configuration.configure();
sessionFactory = configuration.buildSessionFactory();
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
public static Session getSession() {
return sessionFactory.getCurrentSession();
}
public static void close() {
sessionFactory.close();
}
public static void setSessionFactory(SessionFactory factory) {
sessionFactory = factory;
}
}
All CRUD actions with Entities i do in separate class, where every action execute in one transaction.
public class SessionService {
public static <T> T get(Class<T> classvar, int id) {
Session session = HibernateUtil.getSession();
session.beginTransaction();
T result = session.get(classvar, id);
session.getTransaction().commit();
return result;
}
// Some other code
public static void update(Object obj) {
Session session = HibernateUtil.getSession();
session.beginTransaction();
session.update(obj);
session.getTransaction().commit();
}
}
There is no problems with multithreading work (or i not see it). But, some time ago i seen strange bug. I got one record from db by SessionService.get(Entity.class, 1). Change one field of this entity and save it by SessionService.update(entity). Update was successfull done without exceptions (logging approved). But changes not seen in database. In runtime entity has this changes, but not in db. I wait some time (for flush and others) but nothing changed.
Tried to repeat bug - all good, bug not found. In one day i catch this bug on production server with 19 objects from 200, but in other days i not seen this bug.
Last importan moment: i dont catched this bug with other entities (or i dont see them).
What may be reason of it? May be better use entity manager instead sessions?
Can you please post the entire hibernate.cfg.xml file (delete the username and password of course).
Also are you 100% sure that you do not get an error like (wild guess) :
A different object with the same identifier value was already associated with the session ?
There are errors while running Hibernate, that literary flashes in the console for half a second when running queries.
Had this problem a while back.
We are developing a new desktop application in JavaFx wherein for offline storage we are using SQLite and for orm we are using ormlite.
I want to implement DB connection pooling wherein a fixed number of connections should be set at the start and should be used, released and reused as required. Also, it would be good if we can make use of "readonly" and "writeonly" connections appropriately to maximize performance.
This is what we have written so far.
public class DAO {
private static JdbcPooledConnectionSource connectionSource;
private static DAO instance = null;
private DAO() throws SQLException {
try {
final File path = SystemUtils.getDatabaseFile();
final String DATABASE_URL = Constants.DATABASE_URL + path.getAbsolutePath();
Class.forName(Constants.DATABASE_DRIVER);
connectionSource = new JdbcPooledConnectionSource(DATABASE_URL);
//connectionSource.setMaxConnectionAgeMillis(5 * 60 * 1000);
connectionSource.setCheckConnectionsEveryMillis(5000);
connectionSource.setMaxConnectionsFree(5);
connectionSource.initialize();
init();
} catch (ClassNotFoundException cnfe) {
cnfe.printStackTrace();
} catch (IOException ex) {
ex.printStackTrace();
}
}
private void init() throws ClassNotFoundException, SQLException {
TableUtils.createTableIfNotExists(connectionSource, Customer.class);
TableUtils.createTableIfNotExists(connectionSource, Address.class);
TableUtils.createTableIfNotExists(connectionSource, Location.class);
TableUtils.createTableIfNotExists(connectionSource, City.class);
TableUtils.createTableIfNotExists(connectionSource, Area.class);
TableUtils.createTableIfNotExists(connectionSource, Category.class);
TableUtils.createTableIfNotExists(connectionSource, Product.class);
TableUtils.createTableIfNotExists(connectionSource, AddonCategory.class);
TableUtils.createTableIfNotExists(connectionSource, ProductAddon.class);
}
public synchronized <D extends Dao<T, ?>, T> D getDao(Class<T> cls) throws SQLException {
Dao<T, ?> dao = DaoManager.createDao(connectionSource, cls);
D daoImpl = (D) dao;
return daoImpl;
}
public synchronized static DAO getInstance() throws SQLException {
if (instance == null) instance = new DAO();
return instance;
}
}
The problem here is everytime we are creating table (TableUtils.createTableIfNotExists) the pooled connection source is making a new connection and not reusing the one earlier used/created.
Not finding enough code examples on Internet on how to correctly use JdbcPooledConnectionSource.
JdbcPooledConnectionSource correct usage
Which SQLite driver are you using? The Xerial driver has the Sqlite code actually compiled into the Jar. This means that you really aren't "connecting" to another database just making calls to the database directly even though it is fronted by a JDBC interface.
This means that you really don't need a JdbcPooledConnectionSource. The pooled connections really only help when you are making connections over the network to a database server. If you look at your application, you should see file-descriptors (in /prod/#/fd if in Linux) which show open FDs to the database but not to sockets.
The problem here is everytime we are creating table (TableUtils.createTableIfNotExists) the pooled connection source is making a new connection and not reusing the one earlier used/created.
Hrm. I have some good coverage in unit tests around the pooled connection source. I'm surprised to hear that it isn't reusing connections. Can you get me a unit test to demonstrate it?
I'm currently facing the following known issue : https://jira.spring.io/browse/SWF-1525
I using Oracle9 and Ikaricp as connection pool. (At first i thought it was an issue with apache dbcp, this is why i switched to ikaricp)
I'm not using JPA but i tried to adapt one of the given workaround to the HibernateFlowExecutionListener.
Here is the code :
public class FixedHibernateFlowExecutionListener extends HibernateFlowExecutionListener {
private final Logger logger = LoggerFactory.getLogger(getClass());
public FixedHibernateFlowExecutionListener(SessionFactory sessionFactory, PlatformTransactionManager transactionManager) {
super(sessionFactory, transactionManager);
}
#Override
public void paused(RequestContext context) {
super.paused(context);
if (isPersistenceContext(context.getActiveFlow())) {
final Session session = getSession(context.getFlowExecutionContext().getActiveSession());
if (session != null && session.isConnected()) {
session.disconnect();
if (session.isConnected()) {
logger.error("Couldn't disconnect the connection from the session");
}
}
}
}
private boolean isPersistenceContext(FlowDefinition flow) {
return flow.getAttributes().contains(PERSISTENCE_CONTEXT_ATTRIBUTE);
}
private Session getSession(FlowSession session) {
return (Session) session.getScope().get(PERSISTENCE_CONTEXT_ATTRIBUTE);
}}
The problem (beside the bug in SWF) is that calling 'session.disconnect();' never disconnects the session from the connection so the connection remains in use.
The lazy init is triggered in a subflow in 10% of the cases, in the 'on-start' tag of the subflow using Hibernate.initialize() on each collection item.
I have to find a fix for this because this is a very heavy operation that must not necessarily be done.
My hibernate properties :
hibernate.connection.release_mode=after_transaction
hibernate.temp.use_jdbc_metadata_defaults=false
hibernate.default_schema=*****
hibernate.dialect=org.hibernate.dialect.Oracle9iDialect
hibernate.id.new_generator_mappings=true
hibernate.event.merge.entity_copy_observer=allow
Has anyone found a solution for this?
Note : there was a similar question but related to jpa Database connections not being closed with jpaFlowExecutionListener
Thanks for help.
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.
I'm working on a Neo4J database, using it in a little social network. The idea is:
As a user, you connect to the website, and it connects you to a N4J db.
I tried to avoid problem related to multiple instances of the db by creating it through a Singleton. However, I think my users must be able to "connect" and "disconnect", so I created method for that, but when I use disconnect() on a user, the others run through a NullPointerException due to the fact that they can't access the db anymore (which I thought was handled by the Singleton).
Here is the code, which I believe will make things clearer :
The main that I use to test my code :
public static void main(String[] args) {
Node root;
N4JAdmin admin = new N4JAdmin();
Transaction tx = GraphDB.getGraphDb().beginTx();
try {
root = GraphDB.getGraphDb().createNode();
root.setProperty("Nom", "aname");
root.setProperty("Prenom", "afirstname");
tx.success();
} finally {
tx.finish();
}
N4JUser sr = admin.addUser("Ribeiro", "Swen", 1);
//14 more addUser, it creates nodes in the db with the 3 properties, works fine
// The following connect() calls are commented because the errors occurs with or without them
// sr.connect();
// the 14 others...
sr.addAcquaintance(cw.getUserNode());
sr.addAcquaintance(fc.getUserNode());
sr.disconnect();
System.out.println("+++++++++ sr disconnected ++++++++++");
bm.addAcquaintance(cm.getUserNode());
// and more operations...
GraphDB.getGraphDb().shutdown();
}
the addAquaintance code :
public void addAcquaintance(Node target) {
Transaction tx = GraphDB.getGraphDb().beginTx();
try {
this.userNode.createRelationshipTo(target, RelTypes.CONNAIT);
System.out.println(this.userNode.getProperty("Nom")+" "+this.userNode.getProperty("Prenom")+" est maintenant ami avec : "+ target.getProperty("Nom")+" "+target.getProperty("Prenom"));
tx.success();
} finally {
tx.finish();
}
}
And the GraphDB class, which is my Singleton :
public final class GraphDB {
private static final String DB_PATH = "/Workties/database/workties.db";
private static GraphDatabaseService graphDb;
private static boolean instanciated = false;
private GraphDB(){
GraphDB.graphDb = new GraphDatabaseFactory().newEmbeddedDatabase(DB_PATH);
registerShutdownHook(graphDb);
GraphDB.instanciated = true;
}
private static void registerShutdownHook( final GraphDatabaseService graphDb ){
Runtime.getRuntime().addShutdownHook( new Thread(){
#Override
public void run(){
graphDb.shutdown();
}
} );
}
public static GraphDatabaseService getGraphDb(){
if (!GraphDB.instanciated) {
new GraphDB();
System.out.println("New connection : " +GraphDB.graphDb.toString());
}
return GraphDB.graphDb;
}
}
Note : the instanciated attribute was added later, but even without it it didn't work.
My problem comes from the fact that I thought this type of error wouldn't happen using a Singleton, so I'm a bit stuck...
Thanks in advance for your help !
EDIT : the disconnect method :
public void disconnect(){
GraphDB.getGraphDb().shutdown();
}
You should not have to call shutdown for every user- once the DB is instantiated for the first time, it is enough to last you for all your querying etc. Why do you want to connect and disconnect?
The nullpointer is because shutdown shuts down Neo4j but your singleton still thinks instanciated=true, so it returns a handle to a graphDb that isn't really there. Even without the instanciated variable which you added, the singleton maintains a reference to graphDb after it's shutdown.
Strongly advise against doing the shutdown at all unless your app is terminating.