i am using the following approach to sole lazy initialization problem in hibernate.Pleas tell me whether it will work or not .
I have to implement my transcation in my persistance layer compulsary due to some reasons.
public class CourseDAO {
Session session = null;
public CourseDAO()
{
this.session = this.session = HibernateUtil.getSessionFactory().getCurrentSession();
}
public Course findByID(int cid){
Course crc = null;
Transaction tx = null;
try {
tx = session.beginTransaction();
Query q = session.createQuery("from Course as course where course.cid = "+cid+" ");
crc = (Course) q.uniqueResult();
//note that i am not commiting my transcation here.Because If i do that i will not be able to
//do lazy fetch
}
catch (HibernateException e)
{
e.printStackTrace();
tx.rollback();
throw new DataAccessLayerException(e);
}
finally
{
}
return crc;
}
}
and in the filter i am using the folling code
session = HibernateUtil.getSessionFactory().getCurrentSession();
if(session.isOpen())
HibernateUtil.getSessionFactory().getCurrentSession().getTransaction().commit();
IS this approach right??
Can it can have any problem
could you explain why do you need to have ur transactions in ur repositories? the problem there is that they are going to be so fine-grained, so you are not gonna get any advantage from the session caching
then you are opening the transaction there but closing it in your filter. what happens if you access multiple repositories in your service? Maybe i am not understanding what you mean but i think you need to re-think the reasons that force you to manage your transactions in your repositories
When do you create you CourseDAO? If it is a singleton bean or something else that lives longer than a page view, it will need to keep a SessionFactory and generate a new Session when it needs one rather than keeping a Session.
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 want to use construction
import org.hibernate.Session;
...
try (Session session){
}
How can I do that?
Because "The resource type Session does not implement java.lang.AutoCloseable"
I know, that I need to extend Session and override AutoCloseable method, but when I've try to do that, there is error "The type Session cannot be the superclass of SessionDAO; a superclass must be a class"
Update
I've wrote my own DAO framework, but will be use Spring for that
First, you should use a much more solid session/transaction handling infrastructure, like Spring offers you. This way you can use the Same Session across multiple DAO calls and the transaction boundary is explicitly set by the #Transactional annotation.
If this is for a test project of yours, you can use a simple utility like this one:
protected <T> T doInTransaction(TransactionCallable<T> callable) {
T result = null;
Session session = null;
Transaction txn = null;
try {
session = sf.openSession();
txn = session.beginTransaction();
result = callable.execute(session);
txn.commit();
} catch (RuntimeException e) {
if ( txn != null && txn.isActive() ) txn.rollback();
throw e;
} finally {
if (session != null) {
session.close();
}
}
return result;
}
And you can call it like this:
final Long parentId = doInTransaction(new TransactionCallable<Long>() {
#Override
public Long execute(Session session) {
Parent parent = new Parent();
Child son = new Child("Bob");
Child daughter = new Child("Alice");
parent.addChild(son);
parent.addChild(daughter);
session.persist(parent);
session.flush();
return parent.getId();
}
});
Check this GitHub repository for more examples like this one.
I am fully testing an entity on my unit test, and almost everything worked so far: create, update, list. However, when I try to delete a record, it is not getting deleted. Here is the code I am using:
public void delete (Integer id) {
// This doesnt work even though I know user is set and id is not null
User user = find(id);
getSession().delete(user);
// This will work
// Query query = getSession().createSQLQuery("DELETE FROM users WHERE id = " + id);
// query.executeUpdate();
}
private Session getSession() {
if (session == null) {
try {
session = SessionFactoryUtils.getSession(sessionFactory, Boolean.TRUE);
TransactionSynchronizationManager.bindResource(session.getSessionFactory(), new SessionHolder(session));
} catch (Exception e) {
session = SessionFactoryUtils.getSession(sessionFactory, Boolean.FALSE);
}
}
return session;
}
If I execute the query directly it works but using the delete() method doesnt. I think it may be related to committing the transaction but I already tried something like that and no luck. Any ideas?
I found the problem with this one.
First, find() method was evicting my user model, and probably taking it out of the session.
After delete(), I also needed to session.flush()
First time that I ran into this error I've surrounded my tx.commit() with a if condition but am not sure why I am still receiving this error.
Struts Problem Report
Struts has detected an unhandled exception:
Messages:
Transaction not successfully started
File: org/hibernate/engine/transaction/spi/AbstractTransactionImpl.java
Line number: 200
Stacktraces
org.hibernate.TransactionException: Transaction not successfully started
org.hibernate.engine.transaction.spi.AbstractTransactionImpl.rollback(AbstractTransactionImpl.java:200)
After a product has been selected by user, in my main function I will call two functions as following.
First function to retrieve the object of selected product.
Second function to check if selected user has the product therefore it returns true if client has the product otherwise returns false;
Function 1
....
Product pro = new Product();
final Session session = HibernateUtil.getSession();
try {
final Transaction tx = session.beginTransaction();
try {
pro = (Product) session.get(Product.class, id);
if (!tx.wasCommitted()) {
tx.commit();
}
} catch (Exception e) {
tx.rollback();
e.printStackTrace();
}
} finally {
HibernateUtil.closeSession();
}
.....
Function 2
.....
final Session session = HibernateUtil.getSession();
try {
final Transaction tx = session.beginTransaction();
try {
User user = (User) session.get(User.class, id);
if (!tx.wasCommitted()) {
tx.commit();
}
if(client.hasProduct(proId)){
return client.getProduct(proId);
}
return false;
} catch (Exception e) {
tx.rollback(); <<<Error is on this line
e.printStackTrace();
}
} finally {
HibernateUtil.closeSession();
}
....
Take a look at Transaction.isActive() method. You can wrap call to rollback() method with condition, checking whether transaction is still active. And the second, I'd prefer the following code:
final Session session = HibernateUtil.getSession();
try {
final Transaction tx = session.beginTransaction();
// do things
tx.commit();
} finally {
if (tx.isActive()) {
try {
tx.rollback();
} catch (Exception e) {
logger.log("Error rolling back transaction", e);
}
}
try {
session.close();
} catch (Exception e) {
logger.log("Error closing session", e);
}
}
Of course, code in the finally section better to wrap into public static method and just call it in every finally.
BTW, why are you doing something outside tranaction? I usually commit after all things get done, to achieve a better consistency and avoid LazyInitializationException.
One possibility is that the exception you are catching in the second functions is from the code after the commit(), so you end up trying to rollback a transaction that is already committed, which is not allowed.
You could try reorganizing your code to make sure that rollback is never called after commit. Maybe even something simple like reducing the scope of the inner try-catch:
final Session session = HibernateUtil.getSession();
try {
final Transaction tx = session.beginTransaction();
try {
User user = (User) session.get(User.class, id);
if (!tx.wasCommitted()) {
tx.commit();
}
} catch (Exception e) {
tx.rollback();
e.printStackTrace();
}
if(client.hasProduct(proId)){
return client.getProduct(proId);
}
return false;
} finally {
HibernateUtil.closeSession();
}
The error indicates the transaction wasn't started at the time tried to roll back - and the problem may be that you are trying to wrap a get, which does not alter the db state and does not leave behind garbage that needs to be committed or rolled back. Nothing changes when you perform select *.
In addition to this, you may want to extract this transaction handling into a common method that is independent of the work being done, so you don't have to write this over and over again, that leaves your code open for bugs. Basically, it seems like you are getting DB objects but then intermingling some business logic withing the same method. Perhaps consider doing something like below:
DB Handling Function
public static <T> T getDBObject( Class<T> clazz, Serializable id )
throws SQLException
{
Session session = null;
try
{
session = HibernateUtil.getSession();
return (T)session.get( clazz, id );
}
finally
{
if ( session != null )
{
session.close();
}
}
}
Now that you can pull object of the DB (note that they will be detached, but still valid), you can then perform work on the objects. I many not have captured exactly what you need to check, but it seems like it is something like:
Example Comparison Function
public boolean doesUserHaveProduct(Serializable userId, Serializable productId)
{
try
{
User user = getDBObject(User.class, userId);
Product product = getDBObject( Product.class, productId );
return user.hasProduct( product );
}
catch (SQLException e)
{
e.printStackTrace();
return false;
}
}
My web application used hibernate mysql. I can add records to database without having any issue. But if i going to update latest adding record It will not updating. But if I re-start the server(tomcat) and then try to update It's working.
To update the record following condition should be satisfied.
//Check record aready exist
public boolean idExists(String id) {
Session session = (Session) HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
List<Officer> list = (List<Officer>) session.createQuery("from Officer as p where p.idno =" + "\'" + id.trim() + "\'").list();
return (list.size() > 0) ;
}
Immediate(return 1) adding records It will returns 0. But one restart the server and update the record It will work. I also verify after adding record it's successfully commit to DB.
public class HibernateUtil {
private static final SessionFactory sessionFactory;
static{
try{
sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
}catch(Throwable ex){
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}
Let me know if something wrong in my code?
Hibernate does not interact with database immediately after transaction or it does at the time of flushing session, it manages and updates its record to increase system performance in its own way, If you want immediate reflection use :
session.flush();
After updating records.
You could try to flush the session. Entities are not immediately persisted into the database.
session.flush();
I think better you use Transactions here.
You put session.biginTranasaction();
Instead do something like,
public boolean idExists(String id) {
Session session = (Session) HibernateUtil.getSessionFactory().getCurrentSession();
Transaction tx = session.beginTransaction();
List<Officer> list = (List<Officer>) session.createQuery("from Officer as p where p.idno =" + "\'" + id.trim() + "\'").list();
tx.commit();
if(session != null){
session.close();
}
return (list.size() > 0) ;
}
On other end you should generate new session and do other stuffs.