【问题标题】:Persist same dependency in different transactions issue在不同的事务问题中保持相同的依赖关系
【发布时间】:2017-12-20 12:33:08
【问题描述】:

我有一部分程序以并行方式与另一个系统实现同步。每个同步过程都会打开新事务。同步对象将映射到某些数据并持久化。 将要映射的对象的数据可以在之前持久化,并且可以在同步时持久化。

我的持久层提供级联操作,所以我不关心所有依赖项的持久化。

问题

在映射对象之前没有持久化,在同步的时候会被保存在不同的线程中同时出现休眠异常:

org.springframework.dao.DataIntegrityViolationException: A different object with the same identifier value was already associated with the session : [my.project.entity.CrmCaseLink#27];
nested exception is javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [my.project.entity.CrmCaseLink#27]

我尝试解决问题没有帮助

1.

我尝试使用缓存,在那里我可以为不同的线程获得相同的CrmCaseLink,但这并没有解决我的问题。

2.

我又试了一次

@CacheConfig(cacheNames = Constants.CRM_CASE_LINK_CACHE, cacheManager = Constants.GUAVA_CACHE_MANAGER)
@Service
public class CrmCaseLinkService {

    private static final Logger LOGGER = LogManager.getLogger(CrmCaseLinkService.class);
    @Autowired
    private CrmCaseLinkRepository crmCaseLinkRepository;

    @Cacheable(key = "#id")
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public synchronized CrmCaseLink findOne(Long id) {
        LOGGER.debug("trace loading crm case link by specified id: {}", id);
        return Optional.ofNullable(crmCaseLinkRepository.findOne(id))
                .orElseGet(() -> crmCaseLinkRepository.save(new CrmCaseLink(id)));
    }
}

问题

为什么这里的缓存是错误的?我希望hiberate 能够在不同的事务中管理相同的非持久化实体。我确信在单独的 spring bean 中具有新事务的缓存解决方案是无可争议的解决方案,但它也对我没有帮助。

其他细节

java8、spring、spring data jpa、postgres、hibernate5、码头容器

【问题讨论】:

    标签: java spring multithreading hibernate jpa


    【解决方案1】:

    问题原因说明

    我认为问题出现是因为在提供的解决方案中使用了分离的实体对象。如您所见,我为同步单例方法创建了新事务

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public synchronized CrmCaseLink findOne(Long id) {
        LOGGER.debug("trace loading crm case link by specified id: {}", id);
        return Optional.ofNullable(crmCaseLinkRepository.findOne(id))
                .orElseGet(() -> crmCaseLinkRepository.save(new CrmCaseLink(id)));
    }
    

    如果数据库存储中不存在新对象,则保留新对象。但是public synchronized CrmCaseLink findOne 是从另一个事务中使用来获取CrmCaseLink 并将其映射到其他对象上的。要成功映射和持久化其他实体,我必须使用附加的CrmCaseLink

    解决方案代码示例

    我已经用下一个方式更改了代码:

    1. 数据库查找和持久化是不同的方法
    2. Persist 方法必须在新事务中同步和处理
    3. 移除缓存,因为映射需要附加实体,不同的事务使用这个服务来加载CrmCaseLink

    代码示例:

    @Service
    public class CrmCaseLinkManager implements CrmLinkManager {
        @Autowired
        private CrmCaseLinkRepository crmCaseLinkRepository;
    
        @Override
        public CrmCaseLink findOne(Long id) {
            return crmCaseLinkRepository.findOne(id);
        }
    
        @Override
        @Transactional(propagation = Propagation.REQUIRES_NEW)
        public synchronized void save(Long id) {
            if (crmCaseLinkRepository.exists(id)) {
                return;
            }
            crmCaseLinkRepository.save(new CrmCaseLink(id));
        }
    }
    

    使用它的代码部分演示了如何从实现的服务中获取必要的附加实体:

    T getOrCreateCrmLink(Long id, CrmLinkManager<T> crmLinkManager) {
        T crmLink = crmLinkManager.findOne(id);
        if (crmLink == null) {
            crmLinkManager.save(id);
            crmLink = crmLinkManager.findOne(id);
        }
        return crmLink;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-11-29
      • 2019-06-27
      • 2014-01-02
      • 2019-02-25
      • 2016-10-06
      • 2013-08-27
      • 2019-11-19
      相关资源
      最近更新 更多