-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 3 posts ] 
Author Message
 Post subject: collection returning stale data
PostPosted: Mon Jun 16, 2008 8:59 am 
Newbie

Joined: Mon Jun 16, 2008 6:11 am
Posts: 2
Hi everybody,

I have read many threads on this already, but I still can't get a proper solution working. Basically when each thread has it's own short lived session a collection element returns stale data some of the time and and up-to-date data other times. That is, a Hibernate mapped one-to-many List is returning stale data in the list using ThreadLocal short-life sessions, but a singleton session returns the correct data in the list. I am using:

Java 1.6.0_02
Hibernate core 3.2.6, annotations 3.3.1
Tomcat 5.5.26
MySql 5.0.5

The entity and field that causing all the trouble:

Code:
@Entity
public class PhotoSet
{
   private List<PhotoDescriptor> photoDescriptors = new ArrayList<PhotoDescriptor>();

   @Cache(usage = CacheConcurrencyStrategy.NONE)
   @OneToMany(cascade = {CascadeType.ALL}, fetch = FetchType.LAZY)
   @JoinColumn(name = "photoSet_id")
   List<PhotoDescriptor> getPhotoDescriptors()
   {
      Collections.sort(photoDescriptors);
      return photoDescriptors;
   }

   public void addPhotoDescriptor(PhotoDescriptor photoDescriptor)
   {
      List<PhotoDescriptor> descriptors = getPhotoDescriptors();
      photoDescriptor.setPhotoSetIndex(!(descriptors.isEmpty()) ? descriptors.size() + 1 : 1);
      descriptors.add(photoDescriptor);
   }
}


Phase 1: uploading a photo. I have an applet that creates an HTTP request and sends a photo to be added to an existing PhotoSet. The photo and associated objects are always correctly created in the DB. Most of the time the number of photos already in the set is reported correctly but occasionally not. This is important because each new photo is assigned an index based on the number of photos already in the collection; sometimes this collection is returned with a stale incomplete copy.

Code:
public class UploadImageServlet extends HttpServlet
{
   @Override
   protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
   {
      ObjectInputStream in = new ObjectInputStream(req.getInputStream());
      Transaction tx = HibernateHelper.getNewSession().beginTransaction();
      PhotoDescriptorDAO photoDescriptorDao = new PhotoDescriptorDAO();
      PhotoSetDAO photoSetDao = new PhotoSetDAO();
         /* recieve data */

         PhotoSet photoSet = photoSetDao.getInstance(photoSetId);

                        ...

            photoSetDao.refresh(photoSet);
            PhotoDescriptor photoDescriptor = photoDescriptorDao.createInstance(/* transmitted data*/);
            photoSet.addPhotoDescriptor(photoDescriptor);
            photoSetDao.update(photoSet);

            tx.commit();

                        /* write a response object in the response stream */

      HibernateHelper.closeSession();
}



Phase 2. showing the web page. The response back to the applet instructs it to show a JSP page. When the page loads it should contain all the photos in the set so far. This rarely happens correctly and most frequently has a stale version of that photo list.

Code:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
   HibernateHelper.getNewSession();
   PhotoSetDAO photoSetDao = new PhotoSetDAO();
   PhotoSet photoSet = null;
   photoSet = photoSetDao.getInstance(Long.parseLong(request.getParameter("photoSetId")));

   // some JDBC debug stuff
        Connection con = null;
   int i = 0;
   try
   {
      Class.forName("com.mysql.jdbc.Driver").newInstance();
      con = DriverManager.getConnection("jdbc:mysql://localhost/Photo",
         "raj", "raj");
      Statement statement = con.createStatement();
      ResultSet rs = statement.executeQuery("select id from PhotoDescriptor where photoSet_id = " + photoSet.getId());
      while (rs.next())
      {
         i++;
      }
   }
   catch (Exception e)
   {
   }
   finally
   {
      try
      {
         if (con != null)
            con.close();
      }
      catch (Exception e)
      {
      }
   }

   Session hsession = HibernateHelper.getCurrentSession();
   Criteria criteria = hsession.createCriteria(PhotoSet.class);
   criteria.setCacheable(false);
   SQLQuery query = hsession.createSQLQuery("select id from PhotoDescriptor where photoSet_id = ?");
   query.setLong(0, photoSet.getId());
   List results = query.list();
%>
<p>found photo set with id=<%=photoSet.getId()%> <b>has <%=results.size()%> items </b> at <%= new Date().toString()%></p>

<p>JDBC query returns <b><%=i%> items </b></p>

<%
   for (PhotoDescriptor photoDescriptor : photoSet.getPhotoDescriptors())
   {
%>
      /* get the actual photo and display */
<%
   }
%>

  <% HibernateHelper.closeSession(); %>



As you can see, I have a little SQL call and current time call to see what Hibernate thinks is in the collection, and it returns the same incorrect number of photos in the list as the entity mapping, even though the same query executed in my SQL query browser returns the correct number of photos. I have MySQL query caching turned off too. So I added the direct JDBC call above to see what that returned and it returns the correct number of photos every time.

The final bit is the session manager:
Code:
public class HibernateHelper
{
   private static final ThreadLocal<Session> session = new ThreadLocal<Session>();
   private static final SessionFactory sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();

   private HibernateHelper()
   {
   }

   public static Session getNewSession()
   {
      closeSession();
      Session session = getCurrentSession();
      session.clear();
      //System.out.print("entities="+ session.getStatistics().getEntityCount());
      //System.out.println(", collections=" + session.getStatistics().getCollectionCount());
      return session;
   }
   public static Session getCurrentSession()
   {
      Session session = HibernateHelper.session.get();
      if ((session == null) || (!session.isConnected()))
      {
         session = sessionFactory.openSession();
         HibernateHelper.session.set(session);
      }
      return session;
   }

   public static void closeSession()
   {
      Session session = HibernateHelper.session.get();
      if (session != null)
      {
         session.flush();
         session.close();
         HibernateHelper.session.set(null);
      }
   }
}


For some reason when those getEntityCount()/getCollectionCount() debug lines are enabled the JSP becomes more robust, but the image uploading still doesn't always behave correctly. They both report 0 every time.

When I replace the ThreadLocal implementation of session above with a singleton it works perfectly. I realize that is a bad design to follow, which is why I am trying very hard to make the threadsafe version work.

I really am stumped on this. My sessions are local individual transactions and they not overlap timewise. Any ideas?


Top
 Profile  
 
 Post subject:
PostPosted: Wed Oct 08, 2008 7:54 am 
Newbie

Joined: Wed Jul 02, 2008 10:04 am
Posts: 1
Hello ray_n, I currently use entityInManagerView filter that relies on ThreadLocal pattern. I had a similiar problem and solved it by using Set instead of List.

I'm still browsing trough various hibernate documentation to confirm that switching from List to Set solved my problem. My belief is that iterate method that I used over set triggered loading data against DB, and that the same code over list looked up data from session's first level cache.

I know that you probably solved your problem, but if not and it turns out that switching to set helped, please let me know.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Oct 09, 2008 3:58 am 
Newbie

Joined: Mon Jun 16, 2008 6:11 am
Posts: 2
Hi,

Thanks for this answer. I eventually got sick of this problem and started looking around for alternative ORM solutions. Eventually I settled on ibatis, and given how much more flexible it is I'm glad I dumped hibernate.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 3 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.