-->
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.  [ 4 posts ] 
Author Message
 Post subject: Need help dealing with stale data
PostPosted: Thu Aug 04, 2005 12:44 pm 
Newbie

Joined: Fri Oct 17, 2003 11:31 am
Posts: 19
I'm having the following problem, and I'm not 100% sure why it's happening. I've looked through the forums but haven't found a resolution. I'm running with Struts 1.2, Postgresql 8.0.3, and Hibernate 2.1 on Tomcat 5.0 with JDK 1.5.

I have a page that displays 'round' information. The user has the ability to change the data associated with this Round. When they enter the page the associated Round object has been loaded (see code below), and the page is displayed. The user has the ability to modify data on this page. If they modify data on this page, and then save the data, the Round data is persisted in the database, which is correct. The problem is that when they come back into the page, the data displayed is the old data (prior to the save). If I close the window (i.e close out my HttpSession) and come back into the page, the data is correct.

It seems like version of my Round object is stale. Somehow the old copy of the Round object is in my cache and is being picked up when I do a session.load().

I'm using the Open Session in View pattern, so I should get a new Session after the page has been displayed, and after the Round has been saved to the database.

The only thing I can think of is that somehow after my Round data is persisted, and I forward on to the InitEnterRoundsAction class, that I'm still in the same HttpRequest, and therefore the same Hibernate Session. If this were the case, when I do my session.load() I'd pick up data from the cache (which might be stale) and not re-read from the DB. (just a thought).

Below is the Filter I'm using for the Open Session in View pattern, along with the appropriate section of my mapping.hbm.xml file, as well as the code that is doing the session.load() before the page loads, and the session.saveOrUpdate() when the user hits the 'save' button.

I'm sure I'm just doing something wrong, but I haven't been able to find the problem.

Thanks in advance for any help you may be able to offer.

Quenten

Code follows :

HibernateFilter.java :
Code:
import java.io.IOException;
import java.sql.SQLException;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import net.sf.hibernate.FlushMode;
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Session;
import net.sf.hibernate.SessionFactory;
import net.sf.hibernate.Transaction;
import net.sf.hibernate.cfg.Configuration;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
* Filter which manages a ThreadLocal hibernate session.  Obtain the session
* by calling HibernateFilter.getSession().  Define the JNDI name of the
* hibernate session factory as an init param to the filter in your web.xml.
*
* @author <a href="mailto:jeff@infohazard.org">Jeff Schnitzer</a>
*/
public class HibernateFilter implements Filter
{
    static final Log log = LogFactory.getLog(HibernateFilter.class);
    /**
     * Filter init param which defines the JNDI name for the hibernate factory
     */
    public static final String HIBERNATE_FACTORY_JNDI_PARAM = "hibernateFactory";

    /**
     * Default value if no init param is set.
     */
    public static final String HIBERNATE_FACTORY_JNDI_DEFAULT = "hibernate/tsSessionFactory";

    /**
     * Holds the current hibernate session, if one has been created.
     */
    protected static ThreadLocal<Session> hibernateHolder = new ThreadLocal<Session>();

    /**
     */
    protected static SessionFactory factory;

    /**
     */
     public void init(FilterConfig filterConfig) throws ServletException
     {
        // Initialize hibernate
        try
        {
            new Configuration().configure().buildSessionFactory();
        }
        catch (HibernateException ex) { throw new ServletException(ex); }

        // As good a place as any to initialize the factory
        String factoryJndiName = filterConfig.getInitParameter(HIBERNATE_FACTORY_JNDI_PARAM);
        if (factoryJndiName == null)
            factoryJndiName = HIBERNATE_FACTORY_JNDI_DEFAULT;

        try
        {
            Context ctx = new InitialContext();
            factory = (SessionFactory)ctx.lookup(factoryJndiName);
        }
        catch (NamingException ex) { throw new ServletException(ex); }
     }

    /**
     */
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                   throws IOException, ServletException
    {
        log.debug("doFilter() : Entry.");
        if (hibernateHolder.get() != null)
        {
            throw new IllegalStateException(
                "A session is already associated with this thread!  "
                + "Someone must have called getSession() outside of the context "
                + "of a servlet request.");
        }

        try
        {
            chain.doFilter(request, response);
        }
        finally
        {
            Session sess = hibernateHolder.get();
            log.debug("ThreadLocal Session : "+sess);
            log.debug("ThreadLocal SessionFlush : "+sess.getFlushMode());
            if (sess != null)
            {
                hibernateHolder.set(null);
        log.debug("======= SETTING HOLDER TO NULL ");
                log.debug("Set ThreadLocal Session to NULL");

                try
                {
        log.debug("======= CLOSING SESSION NOW");
                    sess.connection().commit();
                    sess.flush();
                    sess.close();
                } catch (HibernateException ex) {
                    throw new ServletException(ex);
                } catch (SQLException ex) {
                    throw new ServletException(ex);
                }
            }
        }
    }

    /**
     * ONLY ever call this method from within the context of a servlet request
     * (specifically, one that has been associated with this filter).  If you
     * want a Hibernate session at some other time, call getSessionFactory()
     * and open/close the session yourself.
     *
     * @return an appropriate Session object
     */
    public static Session getSession() throws HibernateException
    {
        Session sess = hibernateHolder.get();

        if (sess == null) {
            sess = factory.openSession();
            // Set the flushmode to AUTO so we don't return
            //  stale data.
            sess.setFlushMode(FlushMode.COMMIT);
            hibernateHolder.set(sess);
            log.debug("======= CREATED NEW SESSION : "+sess.getFlushMode());
        }
        log.debug("======= GETTING SESSION NOW");
        return sess;
    }

    /**
     * @return the hibernate session factory
     */
    public static SessionFactory getSessionFactory()
    {
        return factory;
    }

    /**
     * This is a simple method to reduce the amount of code that needs
     * to be written every time hibernate is used.
     */
    public static void rollback(Transaction tx)
    {
        if (tx != null)
        {
            try
            {
                tx.rollback();
            }
            catch (HibernateException ex)
            {
                // Probably don't need to do anything - this is likely being
                // called because of another exception, and we don't want to
                // mask it with yet another exception.
            }
        }
    }

    /**
     */
    public void destroy()
    {
        // Nothing necessary
    }
}


Here are the sections from the mapping file for the Round object :

Code:
   <class name="com.tourneysoft.persistence.businessobjects.PersonalRound" table="personal_round">
      <id column="id" name="id" type="java.lang.Long" unsaved-value="null">
         <generator class="sequence"/>
      </id>
      <many-to-one name="member" class="com.tourneysoft.persistence.businessobjects.Member" column="member_id" />
      <property column="date" length="4" name="date" not-null="true" type="java.util.Date"/>
      <many-to-one name="golfCourseTeeBox" not-null="true" class="com.tourneysoft.persistence.businessobjects.GolfCourseTeeBox" column="golf_course_tee_box_id" />
      <property column="status" length="10" name="status" not-null="true" type="java.lang.String"/>
      <property column="attest" length="40" name="attest" type="java.lang.String"/>
      <property column="comments" length="256" name="comments" type="java.lang.String"/>
      <set name="personalScores" inverse="true" cascade="all">
         <key column="personal_round_id"/>
         <one-to-many class="com.tourneysoft.persistence.businessobjects.PersonalScore" />
      </set>
      <many-to-one name="group" not-null="false" class="com.tourneysoft.persistence.businessobjects.Group" column="group_id" />
   </class>

   <class name="com.tourneysoft.persistence.businessobjects.Group" table="groups">
      <id column="id" name="id" type="java.lang.Long" unsaved-value="null">
         <generator class="sequence"/>
      </id>
      <many-to-one name="promotionalCode" class="com.tourneysoft.persistence.businessobjects.PromotionalCode" column="promotional_code_id" />
      <property column="name" length="64" name="name" not-null="true" type="java.lang.String"/>
      <property column="description" name="description" type="java.lang.String"/>
      <property column="logo_filespec" length="128" name="logoFilespec" type="java.lang.String"/>
      <property column="bylaws_filespec" length="128" name="bylawsFilespec" type="java.lang.String"/>
      <property column="info_filespec" length="128" name="infoFilespec" type="java.lang.String"/>
      <property column="group_viewable_rankings" length="1" name="groupViewableRankings" not-null="true" type="java.lang.String"/>
      <property column="group_viewable_charts" length="1" name="groupViewableCharts" not-null="true" type="java.lang.String"/>
      <property column="group_viewable_reports" length="1" name="groupViewableReports" not-null="true" type="java.lang.String"/>
      <property column="validation_code" length="20" name="validationCode" not-null="true" type="java.lang.String"/>
   </class>


Here is the section of the code that loads the Round information prior to the page being displayed (InitEnterPersonalRoundAction.java)

Code:
...
Session session = HibernateFilter.getSession();
Object obj = session.load(classToLoad, roundId);
...


The page is then displayed with the data retrieved from this load.

The data is modified, and saved in the browser, and the following action is called (ProcessEnterRoundsAction.java) :


Code:
      
...
                Transaction tx = null;
      Session session = null;
      try {
         session = HibernateFilter.getSession();
         tx = session.beginTransaction();
         obj = session.saveOrUpdateCopy(personalRound);
         tx.commit();
      } catch (HibernateException he) {
         if (tx != null) {
            try {
               tx.rollback();
            } catch (HibernateException e) {
               e.printStackTrace();
               throw e;
            }
         }
         throw he;
      }
...
   


The data is persisted to the DB correctly at this point. I then come back into the page via the (InitEnterPersonalRoundAction.java) class listed above, and the data is stale.

Any help / suggestions would be greatly appreciated.

Thanks,

Quenten


Top
 Profile  
 
 Post subject:
PostPosted: Thu Aug 04, 2005 1:39 pm 
Senior
Senior

Joined: Wed Jul 13, 2005 4:31 pm
Posts: 142
Location: Seattle, WA
I can think of 2 things to try...

first, check that the old object is not cached in the HttpSession. (I've done that one before)

secondly, try turning off any second-level cache if there and see if you have the problem.

Post your hibernate.properties or hibernate.cfg.xml. That might give some clues.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Aug 04, 2005 1:44 pm 
Regular
Regular

Joined: Thu May 26, 2005 2:08 pm
Posts: 99
Quote:
The only thing I can think of is that somehow after my Round data is persisted, and I forward on to the InitEnterRoundsAction class, that I'm still in the same HttpRequest, and therefore the same Hibernate Session. If this were the case, when I do my session.load() I'd pick up data from the cache (which might be stale) and not re-read from the DB. (just a thought).


If this is really what's going on, you should be able to prevent it by setting redirect="true" in the <forward> tag in your struts mapping file. Your HTTP session will remain the same, but it should force your servlet filter to finish up as the redirect is sent to the browser. Then when the new request comes in, you'll be dealing with a fresh HTTP request object and Hibernate session.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Aug 04, 2005 1:57 pm 
Newbie

Joined: Fri Oct 17, 2003 11:31 am
Posts: 19
I realized that after saving off my data, I go to a different Action class than stated before, so I'm sure that I don't have the same HttpRequest when I come back into the page.

Below are listed my hibernate.properties and hibernate.cfg.xml files...

hibernate.properties

Code:
######################
### Query Language ###
######################

## define query language constants / function names

#hibernate.query.substitutions true 1, false 0, yes 'Y', no 'N'

## package imports

hibernate.query.imports cirrus.hibernate.test



#################
### Platforms ###
#################

## JNDI Datasource

#hibernate.connection.datasource jdbc/test
#hibernate.connection.username db2
#hibernate.connection.password db2


## PostgreSQL

#hibernate.dialect cirrus.hibernate.sql.PostgreSQLDialect
#hibernate.connection.driver_class org.postgresql.Driver
#hibernate.connection.url jdbc:postgresql:tourneysoft
#hibernate.connection.username postgres
#hibernate.connection.password postgres
#hibernate.query.substitutions yes 'Y', no 'N'


## DB2

#hibernate.dialect cirrus.hibernate.sql.DB2Dialect
#hibernate.connection.driver_class COM.ibm.db2.jdbc.app.DB2Driver
#hibernate.connection.url jdbc:db2:test
#hibernate.connection.username db2
#hibernate.connection.password db2


## MySQL

#hibernate.dialect cirrus.hibernate.sql.MySQLDialect
#hibernate.connection.driver_class org.gjt.mm.mysql.Driver
#hibernate.connection.driver_class com.mysql.jdbc.Driver
#hibernate.connection.url jdbc:mysql:///test
#hibernate.connection.username mysql
#hibernate.connection.password mysql


## Oracle

#hibernate.dialect cirrus.hibernate.sql.OracleDialect
#hibernate.connection.driver_class oracle.jdbc.driver.OracleDriver
#hibernate.connection.username ora
#hibernate.connection.password ora
#hibernate.connection.url jdbc:oracle:thin:@localhost:1521:test


## Sybase

#hibernate.dialect cirrus.hibernate.sql.SybaseDialect
#hibernate.connection.driver_class com.sybase.jdbc2.jdbc.SybDriver
#hibernate.connection.username sa
#hibernate.connection.password sasasa
#hibernate.connection.rl jdbc:sybase:Tds:co3061835-a:5000/tempdb


## HypersonicSQL

#hibernate.dialect cirrus.hibernate.sql.HSQLDialect
#hibernate.connection.driver_class org.hsqldb.jdbcDriver
#hibernate.connection.username sa
#hibernate.connection.password
#hibernate.connection.url jdbc:hsqldb:hsql://localhost
#hibernate.connection.url jdbc:hsqldb:test


## Mckoi SQL

#hibernate.dialect cirrus.hibernate.sql.MckoiDialect
#hibernate.connection.driver_class com.mckoi.JDBCDriver
#hibernate.connection.url jdbc:mckoi:///
#hibernate.connection.username admin
#hibernate.connection.password nimda


## SAP DB

#hibernate.dialect cirrus.hibernate.sql.SAPDBDialect
#hibernate.connection.driver_class com.sap.dbtech.jdbc.DriverSapDB
#hibernate.connection.url jdbc:sapdb://localhost/TST
#hibernate.connection.username TEST
#hibernate.connection.password TEST
#hibernate.query.substitutions yes 'Y', no 'N'


## MS SQL Server

#hibernate.dialect cirrus.hibernate.sql.SybaseDialect
#hibernate.connection.username sa
#hibernate.connection.password

## JSQL Driver

#hibernate.connection.driver_class com.jnetdirect.jsql.JSQLDriver
#hibernate.connection.url jdbc:JSQLConnect://CO3061835-A:1433

## JTURBO Driver

#hibernate.connection.driver_class com.newatlanta.jturbo.driver.Driver
#hibernate.connection.url jdbc:JTurbo://CO3061835-A:1433/master

## WebLogic Driver

#hibernate.connection.driver_class weblogic.jdbc.mssqlserver4.Driver
#hibernate.connection.url jdbc:weblogic:mssqlserver4:CO3061835-A:1433

## Microsoft Driver (not supported!)

#hibernate.connection.driver_class com.microsoft.jdbc.sqlserver.SQLServerDriver
#hibernate.connection.url jdbc:microsoft:sqlserver://CO3061835-A:1433;SelectMethod=cursor


## Interbase

#hibernate.dialect cirrus.hibernate.sql.InterbaseDialect
#hibernate.connection.username sysdba
#hibernate.connection.password masterkey

## DO NOT specify hibernate.connection.sqlDialect

## InterClient

#hibernate.connection.driver_class interbase.interclient.Driver
#hibernate.connection.url jdbc:interbase://localhost:3060/C:/firebird/test.gdb

## Pure Java

#hibernate.connection.driver_class org.firebirdsql.jdbc.FBDriver
#hibernate.connection.url jdbc:firebirdsql:localhost/3050:/firebird/test.gdb


## Pointbase

#hibernate.dialect cirrus.hibernate.sql.PointbaseDialect
#hibernate.connection.driver_class com.pointbase.jdbc.jdbcUniversalDriver
#hibernate.connection.url jdbc:pointbase:embedded:sample
#hibernate.connection.username PBPUBLIC
#hibernate.connection.password PBPUBLIC



#################################
### Hibernate Connection Pool ###
#################################

#hibernate.connection.pool_size 2
#hibernate.statement_cache.size 100



###########################
### C3P0 Connection Pool###
###########################

#hibernate.c3p0.max_size 2
#hibernate.c3p0.min_size 2
#hibernate.c3p0.timeout 5000
#hibernate.c3p0.max_statements 100



###################################
### Apache DBCP Connection Pool ###
###################################

# connection pool

#hibernate.dbcp.maxActive 100
#hibernate.dbcp.whenExhaustedAction 1
#hibernate.dbcp.maxWait 120000
#hibernate.dbcp.maxIdle 10

## prepared statement cache

#hibernate.dbcp.ps.maxActive 100
#hibernate.dbcp.ps.whenExhaustedAction 1
#hibernate.dbcp.ps.maxWait 120000
#hibernate.dbcp.ps.maxIdle 100



#################################
### Plugin ConnectionProvider ###
#################################

## use a custom ConnectionProvider (if not set, Hibernate will choose a built-in ConnectionProvider using hueristics)

#hibernate.connection.provider_class cirrus.hibernate.connection.DriverManagerConnectionProvider
#hibernate.connection.provider_class cirrus.hibernate.connection.DatasourceConnectionProvider
#hibernate.connection.provider_class cirrus.hibernate.connection.C3P0ConnectionProvider
#hibernate.connection.provider_class cirrus.hibernate.connection.DBCPConnectionProvider



#######################
### Transaction API ###
#######################

## the Transaction API abstracts application code from the underlying JTA or JDBC transactions

#hibernate.transaction.factory_class cirrus.hibernate.transaction.JTATransactionFactory

#hibernate.transaction.factory_class cirrus.hibernate.transaction.JDBCTransactionFactory


## to use JTATransactionFactory, Hibernate must be able to locate the UserTransaction in JNDI

#jta.UserTransaction jta/usertransaction


## to use JTATransactionFactory with JCS caching, Hibernate must be able to obtain the JTA TransactionManager

#hibernate.transaction.manager_class cirrus.hibernate.transaction.JBossTransactionManagerLookup
#hibernate.transaction.manager_class cirrus.hibernate.transaction.WeblogicTransactionManagerLookup
#hibernate.transaction.manager_class cirrus.hibernate.transaction.WebSphereTransactionManagerLookup



##############################
### Miscellaneous Settings ###
##############################

## specify a JDBC isolation level

#hibernate.connection.isolation 4


## print all generated SQL to the console

#hibernate.show_sql true


## set the JDBC fetch size

#hibernate.jdbc.fetch_size 25


## set the maximum JDBC 2 batch size (a nonzero value enables batching)
#hibernate.jdbc.batch_size 0


## enable use of JDBC 2 scrollable ResultSets (specifying a Dialect will cause Hibernate to use a sensible default)

#hibernate.jdbc.use_scrollable_resultset true


## use streams when writing binary types to / from JDBC

#hibernate.jdbc.use_streams_for_binary true


## specify a default schema for unqualified tablenames

#hibernate.default_schema test


## use a custom stylesheet for XML generation (if not specified, hibernate-default.xslt will be used)

#hibernate.xml.output_stylesheet C:/Hibernate/cirrus/hibernate/hibernate-default.xslt


## enable outerjoin fetching (specifying a Dialect will cause Hibernate to use sensible default)

#hibernate.use_outer_join false



############
### JNDI ###
############

## specify a JNDI name for the SessionFactory

#hibernate.session_factory_name hibernate/session_factory


## Hibernate uses JNDI to bind a name to a SessionFactory and to look up the JTA UserTransaction;
## if hibernate.jndi.* are not specified, Hibernate will use the default InitialContext() which
## is the best approach in an application server

#file system
#hibernate.jndi.class com.sun.jndi.fscontext.RefFSContextFactory
#hibernate.jndi.url file:/

#WebSphere
#hibernate.jndi.class com.ibm.websphere.naming.WsnInitialContextFactory
#hibernate.jndi.url iiop://localhost:900/


hibernate.cfg.xml

Code:
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-2.0.dtd">

<hibernate-configuration>

    <!-- a SessionFactory instance listed as hibernate/tsSessionFactory -->
    <session-factory name="hibernate/tsSessionFactory">

        <property name="show_sql">true</property>
        <property name="use_outer_join">false</property>
        <property name="transaction.factory_class">net.sf.hibernate.transaction.JDBCTransactionFactory</property>
        <property name="jdbc.use_streams_for_binary">true</property>
        <property name="query.substitutions">true 1, false 0, yes 'Y', no 'N'</property>
        <property name="connection.datasource">java:comp/env/jdbc/postgres</property>
        <property name="dialect">net.sf.hibernate.dialect.PostgreSQLDialect</property>

        <!-- mapping files -->
        <mapping resource="mapping.hbm.xml"/>

    </session-factory>

</hibernate-configuration>


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 4 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.