简体   繁体   English

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

[英]Is there a way to save an @Entity with pre-defined @EmbeddedId value using Spring Data's CrudRepository#save() method?

I'm creating a new service with the goal of consuming Kafka events in an idempotent manner and storing the data into a new PostgreSQL database. 我正在创建一个新服务,目的是以幂等方式使用Kafka事件并将数据存储到新的PostgreSQL数据库中。

The event will provide data which will be used in the composite key: 该事件将提供将在复合键中使用的数据:

@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 ...

And the Entity will be referencing it via @EmbeddedId : 实体将通过@EmbeddedId引用它:

@Entity
@Table
public class MyEntity implements Serializable {

  @EmbeddedId private MyCompositeKey myCompositeKey;

... Columns/Constructors/getters ...

When an event is consumed, I want to let spring-data-jpa be smart enough to know whether we are replacing data from an existing MyEntity, or creating a new row. 当一个事件被消耗时,我想让spring-data-jpa足够聪明,知道我们是在替换现有MyEntity中的数据,还是创建一个新行。

The logic was deemed safe enough to use the CrudRepository#save method before researching the expectation of the logic within that method: 在研究该方法中逻辑的期望之前,逻辑被认为足够安全,可以使用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);
        }
    }

I've gotten to the point where the transactions appear to be completed, but no records are persisted to the table. 我已经达到了交易似乎已完成的程度,但没有记录持久存在于表中。

I've confirmed via debugging that the call to #save is branching into the return this.em.merge(entity) logic referenced above. 我已通过调试确认对#save的调用是分支到return this.em.merge(entity)return this.em.merge(entity)逻辑。

I've only found one possibly helpful blog post[1] for a similar scenario, and am lost on where to go next after it didn't seem to resolve the issue. 我只发现了一个可能有用的博客文章[1],用于类似的场景,并且在它似乎无法解决问题之后,我会失去下一步该去的地方。

The only other option I can foresee is to manually go through a potential three-query execution: 我可以预见的唯一其他选择是手动执行潜在的三查询执行:

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

Components 组件

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

References 参考

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

Alright, I found the issue. 好吧,我发现了这个问题。 All of this design was working fine, it was the configuration which was missing. 所有这些设计都运行良好,缺少配置。

For some context - Spring Boot seems to configure default javax.sql.DataSource , default javax.persistence.EntityManagerFactory , and default org.springframework.transaction.PlatformTransactionManager beans. 对于某些上下文 - Spring Boot似乎配置了默认的javax.sql.DataSource ,默认的javax.persistence.EntityManagerFactory和默认的org.springframework.transaction.PlatformTransactionManager bean。

My context was configured with a javax.sql.DataSource bean in order to specify a configuration prefix distinction using org.springframework.boot.context.properties.ConfigurationProperties . 我的上下文配置了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();
  }
}

My context did not add in replacements for the dependent javax.persistence.EntityManagerFactory and org.springframework.transaction.PlatformTransactionManager beans. 我的背景并没有替代因添加javax.persistence.EntityManagerFactoryorg.springframework.transaction.PlatformTransactionManager豆。

The fix was to add in all of the configuration. 修复是添加所有配置。 From the docs : 来自文档

You must create LocalContainerEntityManagerFactoryBean and not EntityManagerFactory directly, since the former also participates in exception translation mechanisms in addition to creating EntityManagerFactory. 您必须直接创建LocalContainerEntityManagerFactoryBean而不是EntityManagerFactory,因为除了创建EntityManagerFactory之外,前者还参与异常转换机制。

The resulting configuration: 结果配置:

@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;
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM