悲观的锁使用弹簧数据jpa无法正常工作

时间:2017-05-18 07:40:05

标签: java multithreading jpa spring-data-jpa

我希望在数据库的帮助下获取序列号。 这就是我想要的:

1.从数据库中读取实体并将其锁定

2.增加序列号并更新实体。

我认为第一个线程可以锁定记录,而另一个线程在第一个线程提交其事务之前不会工作,但是,我得到了与我想要的相反。

下面是我的代码:

存储库:

public interface ActivityNoGeneratorRepository extends BaseRepository<ActivityNoGenerator, Long> {
    @Lock(LockModeType.PESSIMISTIC_WRITE)
    @Query(value = "select  generator from ActivityNoGenerator generator where id=:id")
    ActivityNoGenerator getGeneratorByIdForUpdate(@Param("id") Long id);
}

服务:

@Service
public class ActivityNoGeneratorServiceImpl implements IActivityNoGeneratorService {
    @Autowired
    private ActivityNoGeneratorRepository activityNoGeneratorRepository;

    @Override
    @Transactional
    public String getActivityNo() {
        ActivityNoGenerator activityNoGenerator = activityNoGeneratorRepository.getGeneratorByIdForUpdate(1L);
        System.out.println(1);
        Integer currentValue = activityNoGenerator.getCurrentValue() + 1;
        if (!StringUtils.equals(DateFormatUtils.format(new Date(), "yyyyMM"), DateFormatUtils.format(activityNoGenerator.getLastAccessTime(), "yyyyMM"))) {
            currentValue = 1;
        }
        String serialNum = String.format("%0" + activityNoGenerator.getWidth() + "d", currentValue);
        String activityNo = activityNoGenerator.getPrefix() + activityNoGenerator.getPlatformCode() + DateFormatUtils.format(new Date(), "yyyyMM") + serialNum;

        activityNoGenerator.setCurrentValue(currentValue);
        activityNoGenerator.setLastAccessTime(new Date());
        activityNoGeneratorRepository.save(activityNoGenerator);

        return activityNo;
    }
}

测试:

public class IActivityNoGeneratorServiceTest {

    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath*:spring/applicationContext.xml");
        final IActivityNoGeneratorService activityNoGeneratorService = applicationContext.getBean(IActivityNoGeneratorService.class);

        for (int i = 0; i < 2; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(activityNoGeneratorService.getActivityNo());
                }
            }).start();
        }
    }
}

结果:

Hibernate: 
    select
        activityno0_.id as id1_3_,
        activityno0_.current_value as current_2_3_,
        activityno0_.last_access_time as last_acc3_3_,
        activityno0_.platform_code as platform4_3_,
        activityno0_.platform_name as platform5_3_,
        activityno0_.prefix as prefix6_3_,
        activityno0_.step as step7_3_,
        activityno0_.width as width8_3_ 
    from
        activity_no_generator activityno0_ 
    where
        activityno0_.id=? for update

Hibernate: 
    select
        activityno0_.id as id1_3_,
        activityno0_.current_value as current_2_3_,
        activityno0_.last_access_time as last_acc3_3_,
        activityno0_.platform_code as platform4_3_,
        activityno0_.platform_name as platform5_3_,
        activityno0_.prefix as prefix6_3_,
        activityno0_.step as step7_3_,
        activityno0_.width as width8_3_ 
    from
        activity_no_generator activityno0_ 
    where
        activityno0_.id=? for update

1
1
Hibernate: 
    update
        activity_no_generator 
    set
        current_value=?,
        last_access_time=?,
        platform_code=?,
        platform_name=?,
        prefix=?,
        step=?,
        width=? 
    where
        id=?
Hibernate: 
    update
        activity_no_generator 
    set
        current_value=?,
        last_access_time=?,
        platform_code=?,
        platform_name=?,
        prefix=?,
        step=?,
        width=? 
    where
        id=?
Exception in thread "Thread-6" org.springframework.orm.jpa.JpaSystemException: commit failed; nested exception is org.hibernate.TransactionException: commit failed
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:333)
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:244)
    at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:521)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:761)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:730)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:483)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:290)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
    at com.sun.proxy.$Proxy44.getActivityNo(Unknown Source)
    at com.lemall.srd.pop.activity.oa.service.IActivityNoGeneratorServiceTest$1.run(IActivityNoGeneratorServiceTest.java:18)
    at java.lang.Thread.run(Thread.java:745)
Caused by: org.hibernate.TransactionException: commit failed
    at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:187)
    at org.hibernate.jpa.internal.TransactionImpl.commit(TransactionImpl.java:77)
    at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:517)
    ... 10 more
Caused by: org.hibernate.TransactionException: unable to commit against JDBC connection
    at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.doCommit(JdbcTransaction.java:116)
    at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:180)
    ... 12 more
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    at com.mysql.jdbc.Util.handleNewInstance(Util.java:404)
    at com.mysql.jdbc.Util.getInstance(Util.java:387)
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:950)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3966)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3902)
    at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2526)
    at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2673)
    at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2545)
    at com.mysql.jdbc.ConnectionImpl.commit(ConnectionImpl.java:1614)
    at com.zaxxer.hikari.pool.ProxyConnection.commit(ProxyConnection.java:355)
    at com.zaxxer.hikari.pool.HikariProxyConnection.commit(HikariProxyConnection.java)
    at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.doCommit(JdbcTransaction.java:112)
    ... 13 more
HD1020012017050631

Process finished with exit code 0

我调试我的代码并找到第二个执行的线程而不等待第一个线程提交。

任何线索我犯了什么错误?非常感谢!

1 个答案:

答案 0 :(得分:1)

我看到您在存储库方法中缺少@Transactional注释,这可能是您的服务方法和repo方法在两个不同的事务中运行的原因。还可以在事务注释中使用Propagation.Required选项。