dimanche 22 février 2015

JPA entity gets updated on Ubuntu but throws Optimistic Locking Exception on Windows

Consider this example in which I have created two JPA entities and used Spring Data JPA repositories to perform simple CRUD -



import java.sql.Timestamp;
import javax.persistence.Version;

@MappedSuperclass
public class AbstractValueObject {
@Id
@GeneratedValue
private Long id;

@Version
@Column(name = "time_stamp")
private Timestamp version;

public Long getId() {
return id;
}

@Override
public String toString() {
if (id == null) {
return "";
}

return id.toString();
}
}

@Entity
@Table(name = "demo")
public class Demo extends AbstractValueObject {
private String name;

@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "demo")
private List<Owner> owners;

public Demo() {
owners = new ArrayList<>();
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public List<Owner> getOwners() {
return Collections.unmodifiableList(owners);
}

public void addOwner(Owner owner) {
this.owners.add(owner);
owner.setDemo(this);
}

public void addAllOwners(List<Owner> owners) {
this.owners.addAll(owners);

for (Owner owner : owners) {
owner.setDemo(this);
}
}

public void update(Demo demo) {
this.setName(demo.getName());
this.owners.clear();
this.addAllOwners(demo.getOwners());
}
}

@Entity
@Table(name = "owner")
public class Owner extends AbstractValueObject {
private String attribute;

@ManyToOne(cascade = CascadeType.ALL, optional = false)
@JoinColumn(name = "demo_id", nullable = false)
private Demo demo;

public String getAttribute() {
return attribute;
}

public void setAttribute(String attribute) {
this.attribute = attribute;
}

public Demo getDemo() {
return demo;
}

public void setDemo(Demo demo) {
this.demo = demo;
}
}


After that, I have created a JPA repository for the Demo entity, extending from JpaRepository -



import org.springframework.data.jpa.repository.JpaRepository;

public interface DemoRepository extends JpaRepository<Demo, Long> {}


Corresponding service implementation -



import javax.annotation.Resource;
import org.springframework.transaction.annotation.Transactional;

public class DemoServiceImpl implements DemoService {
@Resource
private DemoRepository demoRepository;

@Override
@Transactional
public Demo create(Demo demo) {
return demoRepository.save(demo);
}

@Override
@Transactional
public Demo update(long id, Demo demo) {
Demo dbDemo = demoRepository.findOne(id);
if (dbDemo == null) {
return demo;
}

dbDemo.update(demo);
return dbDemo;
}

@Transactional
public void testRun() {
Owner owner = new Owner();
owner.setAttribute("attribute");

Demo demo = new Demo();
demo.setName("demo");
demo.addOwner(owner);

this.create(demo);

demo.setName("another");
this.update(demo.getId(), demo);
}
}


persistence.xml file -



<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://ift.tt/1cKbVbQ" xmlns:xsi="http://ift.tt/ra1lAU"
xsi:schemaLocation="http://ift.tt/1cKbVbQ http://ift.tt/1kMb4sd"
version="2.1">

<persistence-unit name="jpa-optimistic-locking" transaction-type="RESOURCE_LOCAL">
</persistence-unit>
</persistence>


Spring app-context.xml -



<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://ift.tt/GArMu6" xmlns:xsi="http://ift.tt/ra1lAU"
xmlns:context="http://ift.tt/GArMu7" xmlns:jdbc="http://ift.tt/18IIlo0"
xmlns:tx="http://ift.tt/OGfeU2" xmlns:jpa="http://ift.tt/1iMF6wA"
xsi:schemaLocation="http://ift.tt/18IIlo0 http://ift.tt/1elhrQW
http://ift.tt/GArMu6 http://ift.tt/1cnl1uo
http://ift.tt/1iMF6wA http://ift.tt/1jZdjKs
http://ift.tt/OGfeU2 http://ift.tt/KC395X
http://ift.tt/GArMu7 http://ift.tt/1ldEMZY">

<context:component-scan base-package="com.keertimaan.example.jpaoptimisticlocking" />
<jpa:repositories base-package="com.keertimaan.example.jpaoptimisticlocking.repository" />

<bean id="demoService" class="com.keertimaan.example.jpaoptimisticlocking.service.DemoServiceImpl" />

<!-- JPA/Database/Transaction Configuration -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test" />
<property name="user" value="root" />
<property name="password" value="admin123" />

<property name="minPoolSize" value="1" />
<property name="maxPoolSize" value="2" />
<property name="acquireIncrement" value="1" />
<property name="maxStatements" value="5" />
<property name="idleConnectionTestPeriod" value="500" />
<property name="maxIdleTime" value="1000" />
<property name="loginTimeout" value="800" />
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="jpa-optimistic-locking" />
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
<property name="persistenceProvider">
<bean class="org.hibernate.jpa.HibernatePersistenceProvider" />
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.hbm2ddl.auto">validate</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

<tx:annotation-driven transaction-manager="transactionManager" />
</beans>


Now whenever I try to update an entity like this on Windows 7 -



public class App {
public static void main(String[] args) {
DemoService demoService = (DemoService) SpringHelper.INSTANCE.getBean("demoService");
demoService.testRun();
}
}


I get an exception like this -



Exception in thread "main" org.springframework.orm.ObjectOptimisticLockingFailureException: Object of class [com.keertimaan.example.jpaoptimisticlocking.domain.Demo] with identifier [4]: optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.keertimaan.example.jpaoptimisticlocking.domain.Demo#4] at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:228) at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:155) at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:519) at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:757) at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:726) at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:478) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:272) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207) at com.sun.proxy.$Proxy31.testRun(Unknown Source) at com.keertimaan.example.jpaoptimisticlocking.App.main(App.java:9) Caused by: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.keertimaan.example.jpaoptimisticlocking.domain.Demo#4] at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:2541) at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3285) at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:3183) at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3525) at org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:159) at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:463) at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:349) at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:350) at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:56) at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1222) at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:425) at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101) at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:177) at org.hibernate.jpa.internal.TransactionImpl.commit(TransactionImpl.java:77) at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:515) ... 9 more



If I run the same example in Ubuntu, then I get no exception at all and my application completes successfully. Why is that?


I am using Windowsw 7 64-bit edition -



OS Name: Microsoft Windows 7 Enterprise


OS Version: 6.1.7601 Service Pack 1 Build 7601



and my Ubuntu version is 12.04.5 64-bit edition.


JDK used in Windows: jdk7 update 75


JDK used in Ubuntu: jdk7 update 51


MySQL Server version in Windows: 5.6.23-log MySQL Community Server (GPL)


MySQL Server version in Ubuntu: 5.5.41-0ubuntu0.12.04.1 (Ubuntu)


Aucun commentaire:

Enregistrer un commentaire