Hibernate 在事务提交后不保存实体

Hibernate does not save entity after transaction commit

我有事务服务,我用它来创建一个人并将其保存在数据库中。 问题是提交事务后实体没有保存到数据库中。

起初我以为是 JpaRepository 的错,每次调用方法时它都会打开一个新事务,但据我了解,这些只是打开的单个物理事务中的逻辑事务为我服务。

我使用 Spring Boot 和 Hibernate 连接我的 firebird 数据库。 为此,我配置了两个独立的数据源和事务管理器。 在每个@Transactional 注释中,我指定要使用的事务管理器。 获取数据完美无缺。问题只涉及保存。

我实际上在这里执行了更多操作,但为了示例,我将限制自己只保存实体。 下面的简单示例。

@Transactional("secondaryTransactionManager")
@RequiredArgsConstructor
public class PersonServiceImpl implements PersonService {

  private final PersonJpaRepository personJpaRepository;

    @Override
  public String savePerson() {
    var personEntity = new PersonEntity();
    personEntity.setName("Slawek");
    personEntity.setSurname("Filip");
    personEntity.setPersonalNumber("12341234123");
    var save = personJpaRepository.save(personEntity);
    return save.getName();
  }
}

虽然个人实体没有保存在数据库中。有日志

2021-10-16 17:41:13.345 TRACE 884 --- [nio-8080-exec-3] o.s.t.i.TransactionInterceptor           : Getting transaction for [com.backend.declarations.service.impl.PersonServiceImpl.savePerson]
2021-10-16 17:41:13.772 TRACE 884 --- [nio-8080-exec-3] o.s.t.i.TransactionInterceptor           : No need to create transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.toString]: This method is not transactional.
2021-10-16 17:41:13.967 TRACE 884 --- [nio-8080-exec-3] o.s.t.i.TransactionInterceptor           : No need to create transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.toString]: This method is not transactional.
2021-10-16 17:41:14.716 TRACE 884 --- [nio-8080-exec-3] o.s.t.i.TransactionInterceptor           : Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
2021-10-16 17:41:14.728 DEBUG 884 --- [nio-8080-exec-3] o.s.orm.jpa.EntityManagerFactoryUtils    : Opening JPA EntityManager
2021-10-16 17:41:14.728 TRACE 884 --- [nio-8080-exec-3] .i.SessionFactoryImpl$SessionBuilderImpl : Opening Hibernate Session.  tenant=null
2021-10-16 17:41:14.729 TRACE 884 --- [nio-8080-exec-3] org.hibernate.internal.SessionImpl       : Opened Session [16b4f365-b4f9-4267-bed6-a5057aad25a9] at timestamp: 1634398874728
2021-10-16 17:41:14.735 TRACE 884 --- [nio-8080-exec-3] o.hibernate.event.internal.EntityState   : Transient instance of: com.backend.declarations.repository.entity.PersonEntity
2021-10-16 17:41:14.736 TRACE 884 --- [nio-8080-exec-3] o.h.e.i.DefaultPersistEventListener      : Saving transient instance
2021-10-16 17:41:14.738 DEBUG 884 --- [nio-8080-exec-3] org.hibernate.SQL                        : select gen_id( GEN_OSOBY_ID, 1 ) from RDB$DATABASE
2021-10-16 17:41:14.750 TRACE 884 --- [nio-8080-exec-3] o.h.r.j.i.ResourceRegistryStandardImpl   : Registering statement [org.firebirdsql.jdbc.FBPreparedStatement@1b6]
2021-10-16 17:41:14.752 TRACE 884 --- [nio-8080-exec-3] o.h.r.j.i.ResourceRegistryStandardImpl   : Registering result set [org.firebirdsql.jdbc.FBResultSet@7e70efdc]
2021-10-16 17:41:14.760 DEBUG 884 --- [nio-8080-exec-3] o.h.id.enhanced.SequenceStructure        : Sequence value obtained: 3422
2021-10-16 17:41:14.760 TRACE 884 --- [nio-8080-exec-3] o.h.r.j.i.ResourceRegistryStandardImpl   : Releasing result set [org.firebirdsql.jdbc.FBResultSet@7e70efdc]
2021-10-16 17:41:14.760 TRACE 884 --- [nio-8080-exec-3] o.h.r.j.i.ResourceRegistryStandardImpl   : Closing result set [org.firebirdsql.jdbc.FBResultSet@7e70efdc]
2021-10-16 17:41:14.764 TRACE 884 --- [nio-8080-exec-3] o.h.r.j.i.ResourceRegistryStandardImpl   : Releasing statement [org.firebirdsql.jdbc.FBPreparedStatement@1b6]
2021-10-16 17:41:14.764 DEBUG 884 --- [nio-8080-exec-3] o.h.r.j.i.ResourceRegistryStandardImpl   : HHH000387: ResultSet's statement was not registered
2021-10-16 17:41:14.764 TRACE 884 --- [nio-8080-exec-3] o.h.r.j.i.ResourceRegistryStandardImpl   : Closing prepared statement [org.firebirdsql.jdbc.FBPreparedStatement@1b6]
2021-10-16 17:41:14.764 TRACE 884 --- [nio-8080-exec-3] o.h.e.jdbc.internal.JdbcCoordinatorImpl  : Starting after statement execution processing [ON_CLOSE]
2021-10-16 17:41:14.765 DEBUG 884 --- [nio-8080-exec-3] o.h.e.i.AbstractSaveEventListener        : Generated identifier: 3422, using strategy: org.hibernate.id.enhanced.SequenceStyleGenerator
2021-10-16 17:41:14.765 TRACE 884 --- [nio-8080-exec-3] o.h.e.i.AbstractSaveEventListener        : Saving [com.backend.declarations.repository.entity.PersonEntity#3422]
2021-10-16 17:41:14.769 TRACE 884 --- [nio-8080-exec-3] org.hibernate.engine.spi.ActionQueue     : Adding an EntityInsertAction for [com.backend.declarations.repository.entity.PersonEntity] object
2021-10-16 17:41:14.772 TRACE 884 --- [nio-8080-exec-3] org.hibernate.engine.spi.ActionQueue     : Adding insert with no non-nullable, transient entities: [EntityInsertAction[com.backend.declarations.repository.entity.PersonEntity#3422]]
2021-10-16 17:41:14.772 TRACE 884 --- [nio-8080-exec-3] org.hibernate.engine.spi.ActionQueue     : Adding resolved non-early insert action.
2021-10-16 17:41:14.778 TRACE 884 --- [nio-8080-exec-3] o.s.t.i.TransactionInterceptor           : Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
2021-10-16 17:41:14.871 TRACE 884 --- [nio-8080-exec-3] o.s.t.i.TransactionInterceptor           : No need to create transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.toString]: This method is not transactional.
2021-10-16 17:41:14.951 TRACE 884 --- [nio-8080-exec-3] o.s.t.i.TransactionInterceptor           : No need to create transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.toString]: This method is not transactional.
2021-10-16 17:41:18.392 TRACE 884 --- [nio-8080-exec-3] o.s.t.i.TransactionInterceptor           : Completing transaction for [com.backend.declarations.service.impl.PersonServiceImpl.savePerson]
2021-10-16 17:41:18.393 TRACE 884 --- [nio-8080-exec-3] org.hibernate.internal.SessionImpl       : Closing session [16b4f365-b4f9-4267-bed6-a5057aad25a9]
2021-10-16 17:41:18.393 TRACE 884 --- [nio-8080-exec-3] o.h.e.jdbc.internal.JdbcCoordinatorImpl  : Closing JDBC container [org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl@695993a3]
2021-10-16 17:41:18.393 TRACE 884 --- [nio-8080-exec-3] o.h.r.j.i.ResourceRegistryStandardImpl   : Releasing JDBC resources
2021-10-16 17:41:18.393 TRACE 884 --- [nio-8080-exec-3] o.h.r.j.i.LogicalConnectionManagedImpl   : Closing logical connection
2021-10-16 17:41:18.394 TRACE 884 --- [nio-8080-exec-3] o.h.r.j.i.ResourceRegistryStandardImpl   : Releasing JDBC resources
2021-10-16 17:41:18.395 TRACE 884 --- [nio-8080-exec-3] o.h.r.j.i.LogicalConnectionManagedImpl   : Logical connection closed

主要 Ts 配置:

@Configuration
@EnableJpaRepositories(basePackages = {
        "com.backend.auth.repository",
        "com.backend.domainoptions.repository",
        "com.backend.actionlog.repository",
        "com.backend.surveys.repository"
        },
        entityManagerFactoryRef = "primaryEntityManager",
        transactionManagerRef = "primaryTransactionManager")
@EnableTransactionManagement
class PrimaryTsConfiguration {

    @Autowired
    Environment env;

    @Bean
    @Primary
    public LocalContainerEntityManagerFactoryBean primaryEntityManager() {
        LocalContainerEntityManagerFactoryBean em
                = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(primaryDataSource());
        em.setPackagesToScan(
                "com.backend.auth.repository",
                "com.backend.domainoptions.repository",
                "com.backend.actionlog.repository",
                "com.backend.surveys.repository"
        );

        HibernateJpaVendorAdapter vendorAdapter
                = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);
        Map<String, Object> properties = new HashMap<>();
        properties.put("hibernate.hbm2ddl.auto",
                env.getProperty("spring.jpa.hibernate.hbm2ddl.auto"));
        properties.put("hibernate.dialect",
                env.getProperty("spring.jpa.hibernate.dialect"));
        em.setJpaPropertyMap(properties);

        return em;
    }

    @Bean(name = "defaultDs")
    @Primary
    public DataSource primaryDataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(env.getProperty("spring.datasource.driverClassName"));
        dataSource.setUrl(env.getProperty("spring.datasource.url"));
        dataSource.setUsername(env.getProperty("spring.datasource.username"));
        dataSource.setPassword(env.getProperty("spring.datasource.password"));
        return dataSource;
    }

    @Bean(name="primaryTransactionManager")
    @Autowired
    @Primary
    DataSourceTransactionManager primaryTransactionManager(@Qualifier("defaultDs") DataSource datasource) {
        return new DataSourceTransactionManager(datasource);
    }

}

二级TS配置:

@Configuration
@EnableJpaRepositories(basePackages = {
        "com.backend.schools.repository",
        "com.backend.declarations.repository",
        "com.backend.verifications.repository"
        },
        entityManagerFactoryRef = "secondaryEntityManager",
        transactionManagerRef = "secondaryTransactionManager")
@EnableConfigurationProperties(RepositoryProperties.class)
@EnableTransactionManagement
public class SecondaryTmConfiguration {
    @Autowired
    Environment env;

    @Configuration
    @PropertySource(factory = YamlPropertySourceFactory.class, value = "datasourceconfig.yml")
    static class PropertiesConfiguration {

    }

    @Bean
    public LocalContainerEntityManagerFactoryBean secondaryEntityManager(RepositoryProperties repositoryProperties) {
        LocalContainerEntityManagerFactoryBean em
                = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(secondaryDataSource(repositoryProperties));
        em.setPackagesToScan(
            "com.backend.schools.repository.entity",
            "com.backend.declarations.repository.entity",
            "com.backend.verifications.repository.entity"
        );

        HibernateJpaVendorAdapter vendorAdapter
                = new HibernateJpaVendorAdapter();
        em.setJpaVendorAdapter(vendorAdapter);
        Map<String, Object> properties = new HashMap<>();
        properties.put("hibernate.hbm2ddl.auto",
                env.getProperty("spring.jpa.hibernate.hbm2ddl.auto"));
        properties.put("hibernate.dialect",
                env.getProperty("spring.jpa.hibernate.dialect"));
        em.setJpaPropertyMap(properties);

        return em;
    }

    @Bean(name = "secondDs")
    public DataSource secondaryDataSource(RepositoryProperties repositoryProperties) {
        System.out.println(repositoryProperties);
        DataSourceRouting dataSourceRouting = new DataSourceRouting();
        dataSourceRouting.initDatasource(createDataSources(repositoryProperties));
        return dataSourceRouting;
    }

    @Bean(name="secondaryTransactionManager")
    @Autowired
    DataSourceTransactionManager secondaryTransactionManager(@Qualifier("secondDs") DataSource datasource) {
        return new DataSourceTransactionManager(datasource);
    }

    private List<Pair<String, DataSource>> createDataSources(RepositoryProperties repositoryProperties) {
        return repositoryProperties.getDbNumbers().stream()
            .map(dbNumber -> createDataSource(dbNumber, repositoryProperties))
            .collect(Collectors.toList());
    }

    private Pair<String, DataSource> createDataSource(String dbNumber, RepositoryProperties repositoryProperties) {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setUrl(createDataSourceUrl(dbNumber));
        dataSource.setUsername(repositoryProperties.getUsername());
        dataSource.setPassword(repositoryProperties.getPassword());
        dataSource.setDriverClassName(repositoryProperties.getDriverClassName());
        return Pair.of(dbNumber, dataSource);
    }

    private String createDataSourceUrl(String dbNumber) {
        return env.getProperty("spring.custom.datasource.url.prefix") + dbNumber + env.getProperty("spring.custom.datasource.url.suffix");
    }
}

为什么它不起作用? 我错过了什么?

好的,问题是 JPA 实体不适用于 DataSourceTransactionManager。 我改用 JpaTransactionManager 并且效果很好