我正在开发一个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.通过实现锁定/版本机制来长远地做到这一点。
您可以在笔记记录上创建锁定/版本机制。有多种实现方法,一个示例是在注释记录上添加版本列。在更新之前,您应该检查记录的版本是否等于检索到的版本,并确定是要重试还是要使更新失败。