具有依赖项的Spring-Data-JPA原子插入

时间:2018-07-02 14:49:07

标签: spring jpa spring-data-jpa

我想从可扩展的微服务中将实体插入数据库。我尝试使用@Lock(LockModeType.PESSIMISTIC_WRITE)来防止条目增加一倍。问题是,我对实体有依赖性。

一个基本的例子是:

TestEntity.java

public class TestEntity {

    @GeneratedValue()
    @Id
    private Long id;

    private String string;

    @ManyToOne
    private TestEntityParent testEntityParent;

}

TestEntityParent.java

public class TestEntityParent {

    @GeneratedValue()
    @Id
    private Long id;

    private String stringTwo;

    @OneToMany(mappedBy = "testEntityParent")
    private List<TestEntity> testEntities;

} 

TestEnityRepository.java

public interface TestEnityRepository extends JpaRepository<TestEntity,Long> {

    @Lock(LockModeType.PESSIMISTIC_WRITE)
    TestEntity saveAndFlush(TestEntity testEntity);

    Optional<TestEntity> findByStringAndTestEntityParentStringTwo(String string, String stringTwo);

}

TestEntityParentRepository.java

public interface TestEntityParentRepository extends JpaRepository<TestEntityParent, Long> {

    @Lock(LockModeType.PESSIMISTIC_WRITE)
    TestEntityParent save(TestEntityParent testEntityParent);

    Optional<TestEntityParent> findByStringTwo(String stringTwo);
}

AtomicDbService.java

@Service
public class AtomicDbService {

    @Autowired
    TestEnityRepository testEnityRepository;

    @Autowired
    TestEntityParentRepository testEntityParentRepository;

    @Transactional
    public TestEntity atomicInsert(TestEntity testEntity) {
        TestEntityParent testEntityParent = testEntityParentRepository.findByStringTwo(testEntity.getTestEntityParent().getStringTwo())
                .orElse(testEntityParentRepository.save(testEntity.getTestEntityParent()));

        return testEnityRepository.findByStringAndTestEntityParentStringTwo(
                testEntity.getString(), testEntity.getTestEntityParent().getStringTwo()
        ).orElse(testEnityRepository
                .save(
                        TestEntity.builder()
                                .string(testEntity.getString())
                                .testEntityParent(testEntityParent)
                                .build()
                )
        );
    }

}

我的测试用例:

    @Test
    @Transactional
    public void testAtomicInsert(){

        TestEntityParent testEntityParent = TestEntityParent.builder().stringTwo("testTwo").build();
        TestEntity testEntity = TestEntity.builder().string("test").testEntityParent(testEntityParent).build();

        atomicDbService.atomicInsert(testEntity);
        System.out.println(testEnityRepository.findAll());
        atomicDbService.atomicInsert(testEntity);
        System.out.println(testEnityRepository.findAll());
        atomicDbService.atomicInsert(testEntity);
        System.out.println(testEnityRepository.findAll());

        System.out.println(testEnityRepository.findAll());

    }

我得到以下答案:

[TestEntity(id=2, string=test, testEntityParent=TestEntityParent(id=1, stringTwo=testTwo, testEntities=null))]
[TestEntity(id=2, string=test, testEntityParent=TestEntityParent(id=1, stringTwo=testTwo, testEntities=null)), TestEntity(id=3, string=test, testEntityParent=TestEntityParent(id=1, stringTwo=testTwo, testEntities=null))]

和一个错误:

query did not return a unique result: 2; 

没有依赖项,一切正常。

更新:

@Lock(LockModeType.PESSIMISTIC_WRITE)添加到find方法会导致

Feature not supported: "MVCC=TRUE && FOR UPDATE && JOIN"; SQL statement:

...同样适用于

 @Lock(LockModeType.PESSIMISTIC_WRITE)
 @Query("SELECT e from TestEntity e join e.testEntityParent p where e.string = :string and p.stringTwo = :stringTwo ")
 Optional<TestEntity> findWhatever(@Param("string") String string, @Param("stringTwo") String stringTwo);

...因为总是生成for update

1 个答案:

答案 0 :(得分:0)

显然,这是一个愚蠢的错误,我需要用orElse和一个lambda替换orElseGet,并且即使没有所有这些@Lock,所有的东西都可以工作,等等。

我仍然不明白交易到底出了什么问题以及原因。