Hibernate version:3.2.6
Spring 2.5.4:
JTA Transaction Manager: BTM
Name and version of the database you are using:Sybase ASE 12.5.4
I'm having a rather odd issue with Hibernate in only one spot within our application. We have two components to our application: 1) is the Web Application. 2) is the server application. The Web app communicates to the server app via Web Services. All Hibernate objects remain within the server app and are not exposed via the web services. Instead we perform a mapping of the Hibernate objects into DTO's which are passed back to through the Web Service.
I'm allowing Spring to manage our transactions declaratively. We are using BTM as our JTA Transaction Manager as we are communicating to several databases during some of our transactions.
The object I'm having issues with is a Quote. A Quote can have 0-* Line Items and a Quote has a Customer. A Quote can also be publicly visible so many users can see it. The problem I'm seeing is when we load the application with multiple users and during a call to getPublicQuotes() for each user I am receiving the exception:
StaleObjectStateException with the message:
Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.sybase.it.quoting.beans.Customer#622]
I've enabled logging to view what is transpiring and I see my transaction beginning when the method is invoked. Multipe databases are accessed in this method: some using iBatis (which is participating in the tx) and primary database which is accessed using Hibernate. I can see the Session being used by Hibernate and all looks as if it is working correctly. As a matter of fact, when I execute this code with only a single user I don't have any issues. It is only when multiple users access this code at the same time.
The odd part about this is that I am only performing a read of the data. The Quotes get loaded and their child LineItems as well as their associated Customer records. Looking at the Hibernate SQL that is written out in the log, I see the records getting loaded from the SELECT statements and then, directly following, I see the Line Items and Customer records getting updated. (The Customer records are getting loaded with a join statement on the Quote record) The code is not performing any modifications of the data, it is simply performing the Select and then taking those objects and passing them off to Dozer where they get mapping into DTO's. So why the update?
Your help and guidance is greatly appreciated. Thanks...
Here is my Spring configuration:
Code:
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager">
<ref local="bitronixTransactionManager"/>
</property>
<property name="userTransaction">
<ref local="bitronixTransactionManager"/>
</property>
</bean>
<bean id="hibernateSessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="quotingDataSource" />
<property name="mappingResources">
<list>
<value>
com/sybase/it/quoting/dao/hibernate/maps/Quote.hbm.xml
</value>
... Deleted for brevity
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.transaction.factory_class">org.hibernate.transaction.JTATransactionFactory</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.SybaseDialect</prop>
<prop key="hibernate.current_session_context_class">jta</prop>
<prop key="hibernate.transaction.manager_lookup_class">com.sybase.it.quoting.spring.BitronixTransactionManagerLookup</prop>
<prop key="hibernate.jndi.url"></prop> <!-- Leave this blank as it is not used. -->
<prop key="hibernate.jndi.class">bitronix.tm.jndi.BitronixInitialContextFactory</prop>
<prop key="hibernate.jdbc.use_get_generated_keys">false</prop>
<prop key="hibernate.cache.provider_class">com.opensymphony.oscache.hibernate.OSCacheProvider</prop>
<prop key="com.opensymphony.oscache.configurationResourceName">com/sybase/it/quoting/oscache.properties</prop>
<!-- add any other hibernate properties you need -->
<prop key="hibernate.show_sql">false</prop>
</props>
</property>
</bean>
Here is my Customer.hbm.xml
Code:
<hibernate-mapping package="com.sybase.it.quoting.beans">
<class name="com.sybase.it.quoting.beans.Customer" table="customer" lazy="true">
<id name="customerId" column="customer_id" type="java.lang.Integer">
<generator class="native"/>
</id>
<version name="version" column="version" type="java.lang.Short"/>
... additional properties
</class>
</hibernate-mapping>
Here is my Quote.hbm.xml:
Code:
<hibernate-mapping package="com.sybase.it.quoting.beans">
<class name="com.sybase.it.quoting.beans.Quote" table="quote">
<id name="quoteId" column="quote_id" type="java.lang.Integer">
<generator class="native"/>
</id>
<version name="version" column="version" type="java.lang.Short"/>
... other properties
<many-to-one name="customer" column="customer_id" class="com.sybase.it.quoting.beans.Customer"/>
</class>
</hibernate-mapping>
Here is my UserQuote.hbm.xml
Code:
<hibernate-mapping package="com.sybase.it.quoting.beans">
<class name="com.sybase.it.quoting.beans.UserQuote" table="user_quote">
<id name="userQuoteId" column="user_quote_id" type="java.lang.Integer">
<generator class="native"/>
</id>
<property name="owner" column="owner" type="java.lang.Boolean" not-null="true" />
<property name="lastUserId" column="last_user_id" type="java.lang.Integer" />
<property name="createDate" column="create_date" type="java.util.Date" not-null="true" />
<many-to-one name="quote" column="quote_id" class="com.sybase.it.quoting.beans.Quote" not-null="true" />
<many-to-one name="userAcct" column="user_id" class="com.sybase.it.quoting.beans.UserAcct" not-null="true" />
</class>
</hibernate-mapping>
The Hibernate SELECT statement:
Code:
2008-05-29 00:05:15,254 [http-8080-Processor11 : ] [DEBUG,SQL ] select userquote0_.user_quote_id as user1_28_0_, quote1_.quote_id as quote1_21_1_, customer2_.customer_id as customer1_5_2_, customer3_.customer_id as customer1_5_3_, quotestatu4_.quote_status_id as quote1_24_4_, userquote0_.owner as owner28_0_, userquote0_.last_user_id as last3_28_0_, userquote0_.create_date as create4_28_0_, userquote0_.quote_id as quote5_28_0_, userquote0_.user_id as user6_28_0_, quote1_.version as version21_1_, quote1_.quote_name as quote3_21_1_, quote1_.license_model as license4_21_1_, quote1_.support_program as support5_21_1_, quote1_.support_catalog as support6_21_1_, quote1_.usage_id as usage7_21_1_, quote1_.quote_date as quote8_21_1_, quote1_.orig_agreement_date as orig9_21_1_, quote1_.taxable as taxable21_1_, quote1_.tin as tin21_1_, quote1_.ship_carrier as ship12_21_1_, quote1_.customer_ship_carrier as customer13_21_1_, quote1_.ship_account_number as ship14_21_1_, quote1_.public_vis as public15_21_1_, quote1_.order_number as order16_21_1_, quote1_.currency_code as currency17_21_1_, quote1_.total_license_cost as total18_21_1_, quote1_.discounted_license_cost as discounted19_21_1_, quote1_.total_support_cost as total20_21_1_, quote1_.discounted_support_cost as discounted21_21_1_, quote1_.num_line_items as num22_21_1_, quote1_.num_licenses as num23_21_1_, quote1_.mgmt_approval as mgmt24_21_1_, quote1_.ops_center_code as ops25_21_1_, quote1_.fob_code as fob26_21_1_, quote1_.drop_ship as drop27_21_1_, quote1_.end_user_known as end28_21_1_, quote1_.comment as comment21_1_, quote1_.mod_by as mod30_21_1_, quote1_.mod_date as mod31_21_1_, quote1_.owner_login as owner32_21_1_, quote1_.last_owner_login as last33_21_1_, quote1_.expiration_date as expiration34_21_1_, quote1_.order_notes as order35_21_1_, quote1_.customer_id as customer36_21_1_, quote1_.licensee as licensee21_1_, quote1_.quote_status_id as quote38_21_1_, quote1_.payment_id as payment39_21_1_, customer2_.version as version5_2_, customer2_.cosmos_cust_id as cosmos3_5_2_, customer2_.cosmos_cust_name as cosmos4_5_2_, customer2_.cosmos_soldto_id as cosmos5_5_2_, customer2_.cosmos_soldto_name as cosmos6_5_2_, customer2_.customer_class as customer7_5_2_, customer2_.duns_number as duns8_5_2_, customer2_.currency as currency5_2_, customer2_.country as country5_2_, customer2_.address_1 as address11_5_2_, customer2_.address_2 as address12_5_2_, customer2_.address_3 as address13_5_2_, customer2_.county as county5_2_, customer2_.city as city5_2_, customer2_.state as state5_2_, customer2_.zip_code as zip17_5_2_, customer2_.email_address as email18_5_2_, customer2_.home_company_code as home19_5_2_, customer2_.contact_first_name as contact20_5_2_, customer2_.contact_last_name as contact21_5_2_, customer2_.contact_phone as contact22_5_2_, customer2_.contact_fax as contact23_5_2_, customer2_.po_only as po24_5_2_, customer2_.support_pgm_code as support25_5_2_, customer2_.credit_limit as credit26_5_2_, customer2_.temp_customer as temp27_5_2_, customer2_.mod_by as mod28_5_2_, customer2_.mod_date as mod29_5_2_, customer2_.bill_to_id as bill30_5_2_, customer2_.ship_destination_id as ship31_5_2_, customer3_.version as version5_3_, customer3_.cosmos_cust_id as cosmos3_5_3_, customer3_.cosmos_cust_name as cosmos4_5_3_, customer3_.cosmos_soldto_id as cosmos5_5_3_, customer3_.cosmos_soldto_name as cosmos6_5_3_, customer3_.customer_class as customer7_5_3_, customer3_.duns_number as duns8_5_3_, customer3_.currency as currency5_3_, customer3_.country as country5_3_, customer3_.address_1 as address11_5_3_, customer3_.address_2 as address12_5_3_, customer3_.address_3 as address13_5_3_, customer3_.county as county5_3_, customer3_.city as city5_3_, customer3_.state as state5_3_, customer3_.zip_code as zip17_5_3_, customer3_.email_address as email18_5_3_, customer3_.home_company_code as home19_5_3_, customer3_.contact_first_name as contact20_5_3_, customer3_.contact_last_name as contact21_5_3_, customer3_.contact_phone as contact22_5_3_, customer3_.contact_fax as contact23_5_3_, customer3_.po_only as po24_5_3_, customer3_.support_pgm_code as support25_5_3_, customer3_.credit_limit as credit26_5_3_, customer3_.temp_customer as temp27_5_3_, customer3_.mod_by as mod28_5_3_, customer3_.mod_date as mod29_5_3_, customer3_.bill_to_id as bill30_5_3_, customer3_.ship_destination_id as ship31_5_3_, quotestatu4_.quote_status_desc as quote2_24_4_, quotestatu4_.order_flag as order3_24_4_ from user_quote userquote0_ inner join quote quote1_ on userquote0_.quote_id=quote1_.quote_id left outer join customer customer2_ on quote1_.customer_id=customer2_.customer_id left outer join customer customer3_ on quote1_.licensee=customer3_.customer_id left outer join quote_status quotestatu4_ on quote1_.quote_status_id=quotestatu4_.quote_status_id, user_acct useracct5_ where userquote0_.user_id=useracct5_.user_id and useracct5_.geo_location_id=? and useracct5_.local_role_id=? and userquote0_.owner=? and quotestatu4_.order_flag=? order by quote1_.mod_date desc
The Hibernate UPDATE statement:
Code:
2008-05-29 00:05:23,004 [http-8080-Processor11 : ] [DEBUG,SQL ] update customer set version=?, cosmos_cust_id=?, cosmos_cust_name=?, cosmos_soldto_id=?, cosmos_soldto_name=?, customer_class=?, duns_number=?, currency=?, country=?, address_1=?, address_2=?, address_3=?, county=?, city=?, state=?, zip_code=?, email_address=?, home_company_code=?, contact_first_name=?, contact_last_name=?, contact_phone=?, contact_fax=?, po_only=?, support_pgm_code=?, credit_limit=?, temp_customer=?, mod_by=?, mod_date=?, bill_to_id=?, ship_destination_id=? where customer_id=? and version=?
[/code]