2013-10-25 5 views
0

Я использую Hibernate 4.2.5 и Spring 3.2.4 с аннотациями JPA.Параллелизм с JPA, Hibernate и Spring

У меня есть следующий метод внутри службы

@Override 
@Transactional 
public Foo getFoo(String fooName) { 
    Foo foo = fooDao.findByName(fooName); 
    if (foo == null) 
     return null; 
    foo.setBar(new Date()); 
    return fooDao.save(foo); 
} 

где fooDao это просто autowired interface FooDao extends JpaRepository<Foo, Long>

Если теперь несколько потоков доступа к методу getFoo происходит следующее:

Thread 1: Foo foo1 = getFoo("name1"); 
Thread 2: Foo foo2 = getFoo("name2"); 

но foo1.getName() == foo2.getName() == "name2" (или "name1"). Вероятно, это проблема параллелизма, когда потоки переключаются между save и select last_update_id

Я попытался добавить транзакционную изоляцию, но это тоже не помогает. Как я могу предотвратить это? (synchronized по методу не поможет, очевидно)

EDIT: мой ApplicationContext:

<?xml version="1.0" encoding="UTF-8"?> 
<beans ..."> 
    <!-- ======== BoneCP ======== --> 
    <bean id="mainDataSource" class="com.jolbox.bonecp.BoneCPDataSource" destroy-method="close"> 
     <!-- ... --> 
    </bean> 
    <!-- ======== SPRING ======== --> 
    <context:annotation-config /> 
    <context:component-scan base-package="foo.bar" /> 
    <jpa:repositories base-package="foo.bar" /> 
    <bean id="dataSource" class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy"> 
     <property name="targetDataSource"> 
      <ref local="mainDataSource" /> 
     </property> 
    </bean> 
    <!-- ======== HIBERNATE/JPA ======== --> 
    <bean id="hibernateJpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> 
     <property name="generateDdl" value="false" /> 
     <property name="showSql" value="false" /> 
     <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" /> 
     <property name="database" value="MYSQL" /> 
    </bean> 
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> 
     <property name="dataSource" ref="dataSource" /> 
     <property name="packagesToScan" value="de.cmr.cmair.server.db.model" /> 
     <property name="jpaVendorAdapter" ref="hibernateJpaVendorAdapter" /> 
     <property name="persistenceUnitName" value="persistenceUnit" /> 
     <property name="jpaProperties"> 
      <props> 
       <prop key="hibernate.hbm2ddl.auto">validate</prop> 
       <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop> 
       <prop key="hibernate.cache.provider_class">org.hibernate.cache.NoCacheProvider</prop> 
       <prop key="hibernate.show_sql">false</prop> 
       <prop key="hibernate.format_sql">true</prop> 
       <prop key="hibernate.use_sql_comments">true</prop> 
       <prop key="hibernate.temp.use_jdbc_metadata_defaults">false</prop> 
       <prop key="hibernate.current_session_context_class">jta</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" /> 
    <!-- the transactional advice --> 
    <tx:advice id="txAdvice" transaction-manager="transactionManager"> 
     <tx:attributes> 
      <tx:method name="get*" read-only="true" /> 
      <tx:method name="*" propagation="REQUIRED" /> 
     </tx:attributes> 
    </tx:advice> 
    <aop:config> 
     <aop:pointcut id="allDaoMethods" expression="execution(* de.cmr.cmair.server.db.service.*.*(..))" /> 
     <aop:advisor advice-ref="txAdvice" pointcut-ref="allDaoMethods" /> 
    </aop:config> 
</beans> 

Foo класс:

@Entity 
@Table(name = "FOO") 
public class Foo { 
    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    @Column(name = "ID", unique = true, nullable = false) 
    private long id; 
    @Column(name = "BAR") 
    private Date bar; 
    @Column(name = "NAME", length = 225) 
    private String name; 

    // ... setters/getters 
} 

Я просто понял, что я не использую управление версиями/оптимистичная блокировка, но я не уверен, влияет ли это на эту проблему.

+0

, пожалуйста, укажите конфигурацию пружины. –

+0

Почему вы сохраняете сущность, которая уже существует? Что ты пытаешься сделать? –

+0

«.save» JPADao на самом деле является «saveOrUpdate», насколько я знаю. В приложении я пытаюсь сохранить дату входа в систему. Кроме того, в документе говорится: «Использовать возвращаемый экземпляр для дальнейших операций, поскольку операция сохранения может полностью изменить экземпляр объекта». Я добавил конфигурацию пружины. – incaseoftrouble

ответ

0

Лучшим решением является оптимизация блокировки или запись должна быть заблокирована, когда пользователь пытается отредактировать запись.

+0

Вы здесь, shure, оптимистичный замок? Насколько я понимаю, оптимистическая блокировка просто помогает предотвратить параллельные изменения на одном и том же объекте. – incaseoftrouble

+0

Optimistic Lock предотвращает фиксацию записи, обновленной другим пользователем. – Arvind

Смежные вопросы