有没有办法使用Spring Data的CrudRepository#save()方法保存带有预定义@EmbeddedId值的@Entity?

问题描述 投票:0回答:1

我正在创建一个新服务,目的是以幂等方式使用Kafka事件并将数据存储到新的PostgreSQL数据库中。

该事件将提供将在复合键中使用的数据:

@Embeddable
public class MyCompositeKey implements Serializable {

    @Column(name="field1", nullable = false)
    private UUID field1;

    @Column(name="field2", nullable = false)
    private UUID field2;

    @Column(name="field3", nullable = false)
    private UUID field3;

... boilerplate Constructors/getters ...

实体将通过@EmbeddedId引用它:

@Entity
@Table
public class MyEntity implements Serializable {

  @EmbeddedId private MyCompositeKey myCompositeKey;

... Columns/Constructors/getters ...

当一个事件被消耗时,我想让spring-data-jpa足够聪明,知道我们是从现有的MyEntity替换数据,还是创建一个新行。

在研究该方法中逻辑的期望之前,逻辑被认为足够安全以使用CrudRepository#save方法:

    @Transactional
    public <S extends T> S save(S entity) {
        if (this.entityInformation.isNew(entity)) {
            this.em.persist(entity);
            return entity;
        } else {
            return this.em.merge(entity);
        }
    }

我已经达到了交易似乎已完成的程度,但没有记录持久存在于表中。

我已通过调试确认调用#save正在分支到上面引用的return this.em.merge(entity)逻辑。

我只发现了一个可能有用的博客文章[1]用于类似的场景,并且在它似乎无法解决问题之后,我将失去下一步的位置。

我可以预见的唯一其他选择是手动执行潜在的三查询执行:

  1. findById
  2. 如果存在,delete
  3. save

组件

  • spring-boot-starter 2.0.6
  • spring-boot-starter-data-jpa 2.0.6
  • hibernate 5.2.x

参考

[1] https://jivimberg.io/blog/2018/11/05/using-uuid-on-spring-data-jpa-entities/

java hibernate jpa spring-data-jpa composite-primary-key
1个回答
0
投票

好吧,我发现了这个问题。所有这些设计都运行良好,缺少配置。

对于某些上下文 - Spring Boot似乎配置默认的javax.sql.DataSource,默认的javax.persistence.EntityManagerFactory和默认的org.springframework.transaction.PlatformTransactionManager bean。

我的上下文配置了javax.sql.DataSource bean,以便使用org.springframework.boot.context.properties.ConfigurationProperties指定配置前缀的区别。

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = {"com.myservice"})
public class RepositoryConfiguration {

  @Bean
  @ConfigurationProperties(prefix = "myservice.datasource")
  public DataSource dataSource() {
    return DataSourceBuilder.create().build();
  }
}

我的上下文没有为依赖的javax.persistence.EntityManagerFactoryorg.springframework.transaction.PlatformTransactionManager bean添加替代品。

修复是添加所有配置。来自docs

您必须直接创建LocalContainerEntityManagerFactoryBean而不是EntityManagerFactory,因为除了创建EntityManagerFactory之外,前者还参与异常转换机制。

结果配置:

@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = {"com.myservice"})
public class RepositoryConfiguration {

  @Bean
  @ConfigurationProperties(prefix = "myservice.datasource")
  public DataSource dataSource() {
    return DataSourceBuilder.create().build();
  }

  @Bean
  public LocalContainerEntityManagerFactoryBean entityManagerFactory() {

    HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();

    LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
    factory.setJpaVendorAdapter(vendorAdapter);
    factory.setPackagesToScan("com.myservice");
    factory.setDataSource(dataSource());
    return factory;
  }

  @Bean
  public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
    JpaTransactionManager txManager = new JpaTransactionManager();
    txManager.setEntityManagerFactory(entityManagerFactory);
    return txManager;
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.