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.
Related
When using hibernate, I would like to see if SessionFactory and Session objects can work with "try-with-resource", so that I can't ignore invoking their close() methods:
try (SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
Session session = sessionFactory.openSession()){
session.beginTransaction();
Customer customer = new Customer();
customer.setFirstName("James");
customer.setLastName("Bond");
customer.setSsn(999998);
customer.setAddressLine1("1111 S St");
customer.setCity("London");
customer.setState("LDN");
customer.setCountry("UK");
session.save(customer);
session.getTransaction().commit();
// session.close();
// sessionFactory.close();
} catch (Exception e) {
e.printStackTrace();
}
I get the errors however:
CustomerTest.java:12: error: incompatible types: try-with-resources not applicable to variable type
SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
^
(SessionFactory cannot be converted to AutoCloseable)
CustomerTest.java:18: error: incompatible types: try-with-resources not applicable to variable type
Session session = sessionFactory.openSession()){
^
(Session cannot be converted to AutoCloseable)
2 errors
Does it mean that SessionFactory and Session objects can't work with "try-with-resource", because the two classes don't implement AutoCloseable interface?
Thanks.
Does it mean that SessionFactory and Session objects can't work with
"try-with-resource", because the two classes don't implement
AutoCloseable interface?
Yes, that's exactly what it means.
If you take a look at newer versions of Hibernate, however, you'll find that both SessionFactory and Session do implement the AutoCloseable interface there.
I think the change was made in Hibernate 5, so upgrading your version of Hibernate could be a potential solution.
This has been fixed in hibernate version 5. If you can upgrade to version 5, please use this. Supported Jira ticket
https://hibernate.atlassian.net/browse/HHH-8898
For project which cannot upgrade, for that we can implement our own CloseableSession interface.
public class CloseableSession implements AutoCloseable {
private final Session session;
public CloseableSession(Session session) {
this.session = session;
}
public Session getSession() {
return session;
}
#Override
public void close() {
session.close();
}
}
Usage
try (CloseableSession session = new CloseableSession(
sessionFactory.openSession())) {
}
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'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.
While developing a web application using spring and hibernate i am getting the following execption.
java.sql.SQLException: ORA-02289: sequence does not exist
While i am trying to insert the data into a table i am usign sequence to increment the value of request_id.
I am using the following coding for inserting
#Override
public void postRequest(RequestInfo requestInfo)
{
Session session = null;
Transaction trans = null;
SessionFactory sessionFactory = null;
sessionFactory=HibernateConfig.getSessionFactory();
session= sessionFactory.openSession();
trans = session.beginTransaction();
session.save(requestInfo);
trans.commit();
session.close();
}
public class HibernateConfig
{
public static SessionFactory sessionFactory;
public static SessionFactory getSessionFactory()
{
sessionFactory = new Configuration().configure().buildSessionFactory();
return sessionFactory;
}
}
In hibernate mapping i have mentioned like this
<id name="requestId" type="int" column="request_id" >
<generator class="sequence">
<param name="sequence">REQUEST_INFO_SEQ</param>
</generator>
</id>
I tried with native also but i couldn't get the values to be get inserted.
i am using oracle 11g.
Can any one give me solution for this.
The insert operation is working fine after giving the sequence name with the schema name like schemaName.sequenceName in the hbm.xml file.
Thank you all for responding the query.
Does the sequence exist? To check, do:
select * from all_sequences where sequence_name = 'REQUEST_INFO_SEQ'
i think there is something wrong with the POJO object comming in parameter, you should create session first the do 3 things , making POJO object (transient state) then calling persistent methods save,update,delete(Persistent State) and trx.commit(Detached state of hibernate object). and should do this work in try catch block and catch HibernateException and then call finally which will close the session, and use buildSessionFactory();
sessionFactory = new Configuration().configure().buildSessionFactory();