我正在开发一个Spring-MVC应用程序,其中有一个更新对象内容的方法。此方法需要大量处理,并且可由多个用户调用以更新同一对象。我如何确保该方法按每个用户的触发顺序进行更新,并且没有用户在更新时拥有过时的数据。

服务层方法:

@Transactional
@Service
private GroupNotesServiceImpl implements GroupNotesService{
    @Override
    public String editNoteWithMap(Map<String, Object> noteMap) {
        Person person = this.personService.getCurrentlyAuthenticatedUser();
        if ((noteMap != null) && (!noteMap.isEmpty())) {
            int noteId = (Integer) noteMap.get("id");
// How to ensure below object is not stale.
        GroupNotes databaseNoteObject = this.groupNotesDAO.getGroupNoteById(noteId);
{  // Process the Map }
// Update the object as below :
        this.groupNotesDAO.editGroupNote(databaseNoteObject, databaseNoteObject.getOwnedSectionId());

}


DAO层方法:

@Repository
@Transactional
public class GroupNotesDAOImpl implements GroupNotesDAO {

    private final SessionFactory sessionFactory;

    @Override
    public GroupNotes editGroupNote(GroupNotes mnotes, int msectionid) {
        Session session = this.sessionFactory.getCurrentSession();
 session.flush();
// I had the lock before, but it started messing around for other updates of associated objects. That was caused by getById also being under lock.
            //  session.buildLockRequest(new LockOptions(LockMode.PESSIMISTIC_WRITE)).lock(mnotes);
            GroupSection groupSection = (GroupSection) session.get(GroupSection.class, msectionid);
            groupSection.getSectionsnotes().add(mnotes);
            mnotes.setOwnednotes(groupSection);
            GroupNotes savedObject = (GroupNotes) session.merge(mnotes);
            session.merge(groupSection);
            session.flush();
            return savedObject;

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

  @Override
    public GroupNotes getGroupNoteById(int id) {
        Session session = this.sessionFactory.getCurrentSession();
        session.flush();
        return (GroupNotes) session.get(GroupNotes.class, id);
    }
}


root-context.xml:



<context:component-scan base-package="com.tooltank.spring">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

<context:property-placeholder location="classpath:application.properties"/>

<beans:bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
            destroy-method="close">
    <beans:property name="driverClassName" value="org.postgresql.Driver"/>
    <beans:property name="url"
                    value="jdbc:postgresql://localhost:5432/DBNAME"/>
    <beans:property name="username" value="USERNAME"/>
    <beans:property name="password" value="PASSWORD"/>
    <beans:property name="removeAbandoned" value="true"/>
    <beans:property name="removeAbandonedTimeout" value="20"/>
    <beans:property name="defaultAutoCommit" value="false"/>
</beans:bean>

<!-- Hibernate 4 SessionFactory Bean definition -->
<beans:bean id="hibernate4AnnotatedSessionFactory"
            class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    <beans:property name="dataSource" ref="dataSource"/>
    <beans:property name="packagesToScan" value="com.tooltank.spring.model"/>
    <beans:property name="hibernateProperties">
        <beans:props>
            <beans:prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQL9Dialect</beans:prop>
            <beans:prop key="hibernate.show_sql">false</beans:prop>
            <!--   <beans:prop key="hibernate.jdbc.batch_size">1000</beans:prop>-->
            <beans:prop key="hibernate.hbm2ddl.auto">update</beans:prop>
            <beans:prop key="cache.use_second_level_cache">true</beans:prop>
            <beans:prop key="cache.use_query_cache">true</beans:prop>
            <beans:prop key="hibernate.order_updates">true</beans:prop>
            <beans:prop key="show_sql">false</beans:prop>
        </beans:props>
    </beans:property>

</beans:bean>

<beans:bean id="LoginServiceImpl" class="com.tooltank.spring.service.LoginServiceImpl"/>


<task:annotation-driven executor="myExecutor"/>

<task:executor id="myExecutor" pool-size="7-42" queue-capacity="11"/>


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

<beans:bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
    <beans:property name="sessionFactory" ref="hibernate4AnnotatedSessionFactory"/>
</beans:bean>

<cache:annotation-driven/>

<beans:bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
    <beans:property name="caches">
        <beans:set>
            <beans:bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean"
                        p:name="person"/>
        </beans:set>
    </beans:property>
</beans:bean>

<!-- AppConfiguration for Spring-Data-Redis -->
<beans:bean id="jedisConnFactory"
            class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:usePool="true"/>

<beans:bean id="redisSaveTemplate" class="org.springframework.data.redis.core.RedisTemplate"
            p:connectionFactory-ref="jedisConnFactory"/>

<beans:bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"
            p:connectionFactory-ref="jedisConnFactory"/>


谢谢。

最佳答案

有几种方法可以做到这一点。

1.快速方法,只需同步即可!

通过使editNoteWithMap()同步化方法,可以确保只有一个线程访问editNoteWithMap()。但是,如果可以在其他地方(其他方法/其他系统)更新笔记,或者担心会导致性能问题,则同步化可能不是您想要的魔术帮助。

2.通过实现锁定/版本机制来长远地做到这一点。

您可以在笔记记录上创建锁定/版本机制。有多种实现方法,一个示例是在注释记录上添加版本列。在更新之前,您应该检查记录的版本是否等于检索到的版本,并确定是要重试还是要使更新失败。

10-07 19:16
查看更多