同步块抛出DuplicateKeyException

时间:2018-09-25 19:48:47

标签: java spring multithreading threadpoolexecutor executor

下面是我的类,该类在线程弹簧执行器下进行。根据源/服务TAXP的类型,将调用TAXS,TAXT方法。 逻辑是如果不插入主税表中的'taxInfo.getGroupingId()',则不插入,否则插入主表。 插入所有二级和三级表记录。 TAXP,TAXS,TAXT是主题,它们随时接收数据。可能存在毫秒间隔,或者同时发送数据,因此块进行了同步。

从3个不同的线程执行器调用所有3个方法。

executor1.insertPrimaryTaxInfo(taxInfo);
executor2.insertSecTaxInfo(taxInfo);
executor3.insertTerTaxInfo(taxInfo);

@Service
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public class TaxServiceImpl implements TaxService {
private static final Logger LOG = LogManager.getLogger(ScanServiceImpl.class);
// TAXP
@Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = TaxServiceException.class)
    public void insertPrimaryTaxInfo(TaxInfo taxInfo) throws TaxServiceException {
        String taxId = null;
        try {

            synchronized (this) {
                taxId = taxMapper.checkExists(taxInfo.getGroupingId());             
                if (taxId == null) {
                    taxMapper.insertTaxInfo(taxInfo);   // primary tax table
                }
            }

            LOG.info("tax id --  " + taxId);
        } catch (Exception ex) {
            LOG.error("Error inserting txId for " + taxInfo.getGroupingId()
                    + ex);
            throw new TaxServiceException(ex);
        }
    }


// TAXS
@Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = TaxServiceException.class)
    public void insertSecTaxInfo(TaxInfo taxInfo) throws TaxServiceException {
        String taxId = null;
        try {

            synchronized (this) {
                taxId = taxMapper.checkExists(taxInfo.getGroupingId());             
                if (taxId == null) {
                    taxMapper.insertTaxInfo(taxInfo);   // primary tax table
                }
            }
            taxMapper.insertIntoSecTable(taxInfo); // secondary tax table
            LOG.info("tax id --  " + taxId);
        } catch (Exception ex) {
            LOG.error("Error inserting txId for " + taxInfo.getGroupingId()
                    + ex);
            throw new TaxServiceException(ex);
        }
    }


// TAXT
@Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = TaxServiceException.class)
    public void insertTerTaxInfo(TaxInfo taxInfo) throws TaxServiceException {
        String taxId = null;
        try {

            synchronized (this) {
                taxId = taxMapper.checkExists(taxInfo.getGroupingId());             
                if (taxId == null) {
                    taxMapper.insertTaxInfo(taxInfo);   // primary tax table
                }
            }
            taxMapper.insertIntoSecTable(taxInfo);  // secondary tax table
            taxMapper.insertIntoTerTable(taxInfo);  // Tertiary tax table
            LOG.info("tax id --  " + taxId);
        } catch (Exception ex) {
            LOG.error("Error inserting txId for " + taxInfo.getGroupingId()
                    + ex);
            throw new TaxServiceException(ex);
        }
    }

}

问题在于TAXP,TAXS,TAXT同时获取数据时,同时调用了上述3种方法。相差一毫秒,其中一个线程插入主表,另一个线程尝试这样做,但发现表中已存在一条记录并抛出重复的密钥exepiton。

我收到以下异常:

"com.data.exception.TaxServiceException: org.springframework.dao.DuplicateKeyException: 
### Error updating database.  Cause: java.sql.SQLIntegrityConstraintViolationException: ORA-00001: unique constraint (TAXDB2.TAX_PK) violated

同步块的原因是为了克服此异常。上面的代码有什么问题?

1 个答案:

答案 0 :(得分:0)

您似乎正在尝试在应用程序中生成基于非GUID的主键,而不是让数据库生成主键并导致冲突。

尝试同步应用程序中的数据库访问是一场失败的战斗。您应该让数据库通过其现有机制管理并发。有关更多信息,请参见ACID。另外,在您的数据库实现上查找当前的隔离级别以及它与其他实现相比有什么帮助。例如,SQL Server文档Understanding Isolation Levels