Environment:

  • Java 21
  • Spring 7.0.0-M8
  • Hibernate 7.1.3
  • Example Project: https://github.com/hantsy/spring7-sandbox/blob/master/hibernate

Description

I migrated the Spring 6/Hibernate 6.6 to new Spring 7 stack and taste the new EntityManager injection via constructor, check the complete file here.

@Repository
@Transactional
public class JpaPostRepository implements PostRepository {

    private final EntityManager entityManager;

    public JpaPostRepository(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    @Transactional(readOnly = true)
    @Override
    public List<Post> findAll() {
      //...
    }

    @Transactional(readOnly = true)
    @Override
    public Stream<Post> stream() {
      //...
    }

    @Transactional(readOnly = true)
    @Override
    public List<Post> findByKeyword(String q, Status status, int offset, int limit) {
      // ...
    }

    @Transactional(readOnly = true)
    @Override
    public Optional<Post> findById(UUID id) {
       //...
    }

    @Override
    public Post save(Post Post) {
       //...
    }

    @Override
    public int updateStatus(UUID id, Status status) {
      //...
    }

    @Override
    public int deleteById(UUID id) {
       //...
    }

    @Override
    public int deleteAll() {
       //...
    }
}

There are two versions (H2 and TestContainers for Postgres) of testing examples in the folder https://github.com/hantsy/spring7-sandbox/tree/master/hibernate/src/test/java/com/example/demo

The H2 based tests is like this.

@SpringJUnitConfig(classes = {PostRepositoryTest.TestConfig.class})
public class PostRepositoryTest {
    private final static Logger log = LoggerFactory.getLogger(PostRepositoryTest.class);

    @Autowired
    PostRepository posts;

    @BeforeEach
    public void setup() {
        var deleted = this.posts.deleteAll();
        log.debug("deleted posts: {}", deleted);
    }

    @Test
    public void testSaveAll() {
        var data = List.of(
                Post.of("test", "content", Status.PENDING_MODERATION),
                Post.of("test1", "content1", Status.DRAFT)
        );
        data.forEach(this.posts::save);

        var results = posts.findAll();
        assertThat(results.size()).isEqualTo(2);

        var resultsByKeyword = posts.findByKeyword("", Status.PENDING_MODERATION, 0, 10);
        assertThat(resultsByKeyword.size()).isEqualTo(1);
    }

    @Test
    public void testInsertAndQuery() {
        var data = Post.of("test1", "content1", Status.DRAFT);
        var saved = this.posts.save(data);
        this.posts.findById(saved.getId()).ifPresent(
                p -> assertThat(p.getStatus()).isEqualTo(Status.DRAFT)
        );

    }

    @Configuration
    @ComponentScan(basePackageClasses = PostRepository.class)
    @Import({JpaConfig.class})
    static class TestConfig {

        @Bean
        @Primary
        public DataSource embeddedDataSource() {
            return new EmbeddedDatabaseBuilder()
                    .setType(EmbeddedDatabaseType.H2)
                    .build();
        }
    }

}

When running the tests, I got the exception:

jakarta.persistence.OptimisticLockException: Row was already updated or 
deleted by another transaction for entity 
[com.example.demo.model.Post with id 'fcb86432-fecc-4d5a-a2c8-252f2c940007']