Hi
I am stick bigtime on a Optimistic Locking Failure Exception, when I try to do a simple loadAll() call. I have tried to isolate tre problem as much as I can. So what I have is a simple domain class Category, which maps to a Category table in the database (MySQL 5), this class has a couple of sets, but I have commented them out of the mapping documents during the isolation of the problem, so it should be really simple. On top of the domain class there is a CategoryRepositoryImpl and a jUnit test to go along with it. I have pasted the code, mapping, db-script and logs below. The 2 update statements in the logs looks suspicious, but I am unfortunately not experienced enough to understand where I did something wrong. So if anybody is able to give me a tip or two that would be very apreciated.
I use Hibernate 3.2.6 and Spring 2.0.8
I have pasted a lot of code below but I have tried to hightlight the parts that are importaint to the problem above each code box
Category domain class
Code:
package com.domain.project.category.domain;
import java.util.HashSet;
import java.util.Set;
import com.domain.project.country.domain.Country;
import com.domain.project.question.domain.*;
public class Category {
private Integer id;
private String globalname;
private Set<Category> children;
private Set<CategoryDescription> categoryDescriptions;
private Category parent;
private String type;
private Integer sequence;
private Set<Question> questions;
private String imageUrl;
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
protected Category() {
// hibernate
}
//test constructor
public Category(Category parent, String type, Integer sequence) {
this.parent = parent;
this.type = type;
this.sequence = sequence;
}
// STUB constructor
public Category(Integer id, Set<Category> children, CategoryDescription categoryDescription) {
this.id = id;
this.children = children;
if (categoryDescriptions == null) {
categoryDescriptions = new HashSet<CategoryDescription>();
}
categoryDescriptions.add(categoryDescription);
}
// test constructor
public Category(Integer id, String type, Category parent) {
this.id = id;
this.type = type;
this.parent = parent;
}
public CategoryDescription getCategoryDescription(Country country) {
for(CategoryDescription desc : categoryDescriptions){
if(desc.getCountry().equals(country)){
return desc;
}
}
return null;
}
protected Set<Category> getChildren() {
return children;
}
public Category getParent() {
return parent;
}
public void setParent(Category parent) {
this.parent = parent;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getId() {
return id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Integer getSequence() {
return sequence;
}
public void setSequence(Integer sequence) {
this.sequence = sequence;
}
public void setChildren(Set<Category> children) {
this.children = children;
}
public Set<CategoryDescription> getCategoryDescriptions() {
return categoryDescriptions;
}
public void setCategoryDescriptions(
Set<CategoryDescription> categoryDescriptions) {
this.categoryDescriptions = categoryDescriptions;
}
public Set<Question> getQuestions() {
return questions;
}
public void setQuestions(Set<Question> questions) {
this.questions = questions;
}
public String getGlobalname() {
return globalname;
}
public void setGlobalname(String globalname) {
this.globalname = globalname;
}
public boolean isIndoor() {
boolean indoor;
if(!isStart()) {
return false;
}
if(getGlobalname().equals("Indoor")){
indoor = true;
} else if(getGlobalname().equals("Outdoor")){
indoor = false;
} else {
throw new IllegalStateException("Start category must one of 'Indoor' or 'Outdoor'");
}
return indoor;
}
public boolean isOutdoor(){
return isStart() && !isIndoor();
}
private boolean isStart() {
return getType().equals("START");
}
}
Category database script
Code:
create table Category(
category_id int not null auto_increment primary key,
parent_category_id int,
type varchar(10),
sequence int,
globalname varchar(20),
image_url varchar(100));
create table CategoryDescription(
categorydesc_id int not null auto_increment primary key,
category_id int not null,
country_id int not null,
description varchar(30),
FOREIGN KEY (country_id) REFERENCES Country(country_id),
FOREIGN KEY (category_id) REFERENCES Category(category_id));
create table Question(
question_id int not null auto_increment primary key,
type varchar(10),
category_id int not null,
sequence int not null,
globalname varchar(20),
additional_question boolean,
FOREIGN KEY (category_id) REFERENCES Category(category_id));
Category mapping file, notice that I have commented out the relations to other classes, because the problem occurs with or without them.
Code:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.domain.project.category.domain">
<class name="Category" table="category">
<id name="id" column="category_id" type="java.lang.Integer" unsaved-value="null">
<generator class="native" />
</id>
<!-- many-to-one name="parent" class="Category">
<column name="parent_category_id" />
</many-to-one>
<set name="children" table="Category" lazy="false" inverse="true" order-by="sequence asc">
<key column="parent_category_id" />
<one-to-many class="Category" />
</set>
<set name="categoryDescriptions" table="CategoryDescription" lazy="false" inverse="true">
<key column="category_id" />
<one-to-many class="CategoryDescription" />
</set>
<set name="questions" table="Question" lazy="false" inverse="true" order-by="sequence asc">
<key column="category_id" />
<one-to-many class="com.domain.project.question.domain.Question" />
</set-->
<property name="type" column="type" type="string" length="10" />
<property name="sequence" column="sequence" type="java.lang.Integer" />
<property name="globalname" column="globalname" type="string" length="30" />
<property name="imageUrl" column="image_url" type="string" length="30" />
</class>
</hibernate-mapping>
CategoryRepository class, the method getAllCategories() is where the problem occurs.
Code:
package com.domain.project.category.repository;
import java.util.List;
import com.domain.project..category.domain.Category;
import com.domain.project..category.domain.CategoryDescription;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
public class CategoryRepositoryImpl extends HibernateDaoSupport implements
CategoryRepository {
public Category getById(Integer id) {
Category category = (Category) getHibernateTemplate().get(Category.class, id);
return category;
}
public void saveOrUpdate(Category category) {
getHibernateTemplate().saveOrUpdate(category);
}
public CategoryDescription getDescriptionById(Integer id) {
CategoryDescription category = (CategoryDescription) getHibernateTemplate().get(CategoryDescription.class, id);
return category;
}
public List<Category> getAllCategories() {
List<Category> categories = (List<Category>) getHibernateTemplate().loadAll(Category.class);
return categories;
}
}
package com.domain.project.category.repository;
import java.util.List;
import com.domain.project.category.domain.Category;
import com.domain.project.category.domain.CategoryDescription;
public interface CategoryRepository {
Category getById(Integer id);
CategoryDescription getDescriptionById(Integer id);
List<Category> getAllCategories();
void saveOrUpdate(Category category);
}
JUnit test, the testGetAllCategories() is the one that fails
Code:
package com.domain.project..category.repository;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import com.domain.project..category.domain.Category;
import com.domain.project..category.domain.CategoryDescription;
import com.domain.project..country.domain.Country;
import org.springframework.test.AbstractTransactionalDataSourceSpringContextTests;
public class IntegrationTestCategoryRepositoryImpl extends
AbstractTransactionalDataSourceSpringContextTests {
private static final String[] configFiles = new String[] { "WEB-INF/applicationContext.xml" };
private CategoryRepository categoryRepository = null;
public void setCategoryRepository(CategoryRepository categoryRepository) {
this.categoryRepository = categoryRepository;
}
public void testGetAllCategories() {
List<Category> categories = categoryRepository.getAllCategories();
assertNotNull(categories);
}
@Override
protected void onSetUp() throws Exception {
super.onSetUp();
createTestData();
}
@Override
protected void onTearDown() throws Exception {
super.onTearDown();
}
private void createTestData() {
Category category1 = new Category(34, "START", null);
Category category2 = new Category(77, "ROOM", null);
categoryRepository.saveOrUpdate(category1);
categoryRepository.saveOrUpdate(category2);
}
protected String[] getConfigLocations() {
return configFiles;
}
}
Stacktrace from the jUnit run:
Code:
org.springframework.orm.hibernate3.HibernateOptimisticLockingFailureException: Batch update returned unexpected row count from update [1]; actual row count: 0; expected: 1; nested exception is org.hibernate.StaleStateException: Batch update returned unexpected row count from update [1]; actual row count: 0; expected: 1
Caused by: org.hibernate.StaleStateException: Batch update returned unexpected row count from update [1]; actual row count: 0; expected: 1
at org.hibernate.jdbc.Expectations$BasicExpectation.checkBatched(Expectations.java:61)
at org.hibernate.jdbc.Expectations$BasicExpectation.verifyOutcome(Expectations.java:46)
at org.hibernate.jdbc.BatchingBatcher.checkRowCounts(BatchingBatcher.java:68)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:48)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:246)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:266)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:168)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:298)
at org.hibernate.event.def.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:41)
at org.hibernate.impl.SessionImpl.autoFlushIfRequired(SessionImpl.java:969)
at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1562)
at org.hibernate.impl.CriteriaImpl.list(CriteriaImpl.java:283)
at org.springframework.orm.hibernate3.HibernateTemplate$5.doInHibernate(HibernateTemplate.java:538)
at org.springframework.orm.hibernate3.HibernateTemplate.execute(HibernateTemplate.java:372)
at org.springframework.orm.hibernate3.HibernateTemplate.loadAll(HibernateTemplate.java:534)
at com.domain.project..category.repository.CategoryRepositoryImpl.getAllCategories(CategoryRepositoryImpl.java:28)
at com.domain.project..category.repository.IntegrationTestCategoryRepositoryImpl.testGetAllCategories(IntegrationTestCategoryRepositoryImpl.java:24)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:79)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:618)
at junit.framework.TestCase.runTest(TestCase.java:168)
at junit.framework.TestCase.runBare(TestCase.java:134)
at org.springframework.test.ConditionalTestCase.runBare(ConditionalTestCase.java:69)
at junit.framework.TestResult$1.protect(TestResult.java:110)
at junit.framework.TestResult.runProtected(TestResult.java:128)
at junit.framework.TestResult.run(TestResult.java:113)
at junit.framework.TestCase.run(TestCase.java:124)
at junit.framework.TestSuite.runTest(TestSuite.java:232)
at junit.framework.TestSuite.run(TestSuite.java:227)
at org.junit.internal.runners.OldTestClassRunner.run(OldTestClassRunner.java:76)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:38)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
From the log, notice the 2 update statements, that looks suspicious to me, I cant understand why there is updates running during a loadAll(), so I believe that might be the source of the problem, I just don't understand it....
Code:
10:53:35,438 INFO [XmlBeanDefinitionReader] Loading XML bean definitions from class path resource [WEB-INF/applicationContext.xml]
10:53:35,898 INFO [GenericApplicationContext] Refreshing org.springframework.context.support.GenericApplicationContext@5b085b08: display name [org.springframework.context.support.GenericApplicationContext@5b085b08]; startup date [Thu Jun 19 10:53:35 CEST 2008]; root of context hierarchy
10:53:35,898 INFO [GenericApplicationContext] Bean factory for application context [org.springframework.context.support.GenericApplicationContext@5b085b08]: org.springframework.beans.factory.support.DefaultListableBeanFactory@27f227f2
10:53:36,038 INFO [GenericApplicationContext] Bean 'org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator' is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
10:53:36,058 INFO [DefaultListableBeanFactory] Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@27f227f2: defining beans [org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator,org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor,txInterceptor,categoryRepository,countryRepository,questionRepository,answerRepository,productRepository,categoryService,countryService,productService,projectSession,sessionFactory,transactionManager,dataSource,hibernateProperties,mailSender,templateMessage,emailSender,logonService]; root of factory hierarchy
10:53:37,010 INFO [LocalSessionFactoryBean] Building new Hibernate SessionFactory
10:53:38,832 INFO [HibernateTransactionManager] Using DataSource [org.apache.commons.dbcp.BasicDataSource@11201120] of Hibernate SessionFactory for HibernateTransactionManager
Hibernate: update category set type=?, sequence=?, globalname=?, image_url=? where category_id=?
Hibernate: update category set type=?, sequence=?, globalname=?, image_url=? where category_id=?
10:53:39,203 ERROR [AbstractBatcher] Exception executing batch:
org.hibernate.StaleStateException: Batch update returned unexpected row count from update [1]; actual row count: 0; expected: 1
at org.hibernate.jdbc.Expectations$BasicExpectation.checkBatched(Expectations.java:61)
at org.hibernate.jdbc.Expectations$BasicExpectation.verifyOutcome(Expectations.java:46)
at org.hibernate.jdbc.BatchingBatcher.checkRowCounts(BatchingBatcher.java:68)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:48)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:246)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:266)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:168)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:298)
at org.hibernate.event.def.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:41)
at org.hibernate.impl.SessionImpl.autoFlushIfRequired(SessionImpl.java:969)
at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1562)
at org.hibernate.impl.CriteriaImpl.list(CriteriaImpl.java:283)
at org.springframework.orm.hibernate3.HibernateTemplate$5.doInHibernate(HibernateTemplate.java:538)
at org.springframework.orm.hibernate3.HibernateTemplate.execute(HibernateTemplate.java:372)
at org.springframework.orm.hibernate3.HibernateTemplate.loadAll(HibernateTemplate.java:534)
at com.domain.project.category.repository.CategoryRepositoryImpl.getAllCategories(CategoryRepositoryImpl.java:28)
at com.domain.project.category.repository.IntegrationTestCategoryRepositoryImpl.testGetAllCategories(IntegrationTestCategoryRepositoryImpl.java:24)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:79)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:618)
at junit.framework.TestCase.runTest(TestCase.java:168)
at junit.framework.TestCase.runBare(TestCase.java:134)
at org.springframework.test.ConditionalTestCase.runBare(ConditionalTestCase.java:69)
at junit.framework.TestResult$1.protect(TestResult.java:110)
at junit.framework.TestResult.runProtected(TestResult.java:128)
at junit.framework.TestResult.run(TestResult.java:113)
at junit.framework.TestCase.run(TestCase.java:124)
at junit.framework.TestSuite.runTest(TestSuite.java:232)
at junit.framework.TestSuite.run(TestSuite.java:227)
at org.junit.internal.runners.OldTestClassRunner.run(OldTestClassRunner.java:76)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:38)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
10:53:39,203 ERROR [AbstractFlushingEventListener] Could not synchronize database state with session
org.hibernate.StaleStateException: Batch update returned unexpected row count from update [1]; actual row count: 0; expected: 1
at org.hibernate.jdbc.Expectations$BasicExpectation.checkBatched(Expectations.java:61)
at org.hibernate.jdbc.Expectations$BasicExpectation.verifyOutcome(Expectations.java:46)
at org.hibernate.jdbc.BatchingBatcher.checkRowCounts(BatchingBatcher.java:68)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:48)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:246)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:266)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:168)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:298)
at org.hibernate.event.def.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:41)
at org.hibernate.impl.SessionImpl.autoFlushIfRequired(SessionImpl.java:969)
at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1562)
at org.hibernate.impl.CriteriaImpl.list(CriteriaImpl.java:283)
at org.springframework.orm.hibernate3.HibernateTemplate$5.doInHibernate(HibernateTemplate.java:538)
at org.springframework.orm.hibernate3.HibernateTemplate.execute(HibernateTemplate.java:372)
at org.springframework.orm.hibernate3.HibernateTemplate.loadAll(HibernateTemplate.java:534)
at com.domain.project.category.repository.CategoryRepositoryImpl.getAllCategories(CategoryRepositoryImpl.java:28)
at com.domain.project.category.repository.IntegrationTestCategoryRepositoryImpl.testGetAllCategories(IntegrationTestCategoryRepositoryImpl.java:24)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:79)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:618)
at junit.framework.TestCase.runTest(TestCase.java:168)
at junit.framework.TestCase.runBare(TestCase.java:134)
at org.springframework.test.ConditionalTestCase.runBare(ConditionalTestCase.java:69)
at junit.framework.TestResult$1.protect(TestResult.java:110)
at junit.framework.TestResult.runProtected(TestResult.java:128)
at junit.framework.TestResult.run(TestResult.java:113)
at junit.framework.TestCase.run(TestCase.java:124)
at junit.framework.TestSuite.runTest(TestSuite.java:232)
at junit.framework.TestSuite.run(TestSuite.java:227)
at org.junit.internal.runners.OldTestClassRunner.run(OldTestClassRunner.java:76)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:38)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
10:53:39,333 INFO [GenericApplicationContext] Closing org.springframework.context.support.GenericApplicationContext@5b085b08: display name [org.springframework.context.support.GenericApplicationContext@5b085b08]; startup date [Thu Jun 19 10:53:35 CEST 2008]; root of context hierarchy
10:53:39,333 INFO [DefaultListableBeanFactory] Destroying singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@27f227f2: defining beans [org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator,org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor,txInterceptor,categoryRepository,countryRepository,questionRepository,answerRepository,productRepository,categoryService,countryService,productService,projectSession,sessionFactory,transactionManager,dataSource,hibernateProperties,mailSender,templateMessage,emailSender,logonService]; root of factory hierarchy
10:53:39,353 INFO [LocalSessionFactoryBean] Closing Hibernate SessionFactory
Any tips would be highly apreciated, thank you in advance :)