[英]Spring boot "no transaction is in progress" with 2 datasources
我有两个数据库,我正在尝试将一些记录保存到服务方法中。
这给了我错误: org.springframework.dao.InvalidDataAccessApiUsageException: no transaction is in progress; nested exception is javax.persistence.TransactionRequiredException: no transaction is in progress
org.springframework.dao.InvalidDataAccessApiUsageException: no transaction is in progress; nested exception is javax.persistence.TransactionRequiredException: no transaction is in progress
。
这是实体:
@Entity
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "TABLE_NAME")
public class SomeEntity {
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_generator")
@SequenceGenerator(name = "seq_generator", sequenceName = "SEQ_ID", allocationSize = 1)
@Id
@Column(name = "ID", nullable = false)
private Long id;
@Column(name = "SOME_STR", nullable = false)
private String someStr;
@Column(name = "SOME_INT", nullable = false)
private Integer someInt;
public SomeEntity(String someStr, Integer someInt) {
this.someStr = someStr;
this.someInt = someInt;
}
}
@Entity
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "TABLE_NAME")
public class SomeEntityHist {
@Id
@Column(name = "ID", nullable = false)
private Long id;
@Column(name = "SOME_STR", nullable = false)
private String someStr;
@Column(name = "SOME_INT", nullable = false)
private Integer someInt;
}
这是多个数据库连接的配置文件之一:
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef = "realEntityManager",
basePackages = {"com.some.project.files.repository.real"}
)
@RequiredArgsConstructor
@Log4j2
@AutoConfigureOrder(1)
public class RealDatasourceConfig {
private final Environment env;
@Primary
@Bean
public DataSource realDataSource() throws SQLException {
HikariDataSource hikariDataSource = new HikariDataSource();
hikariDataSource.setDriverClassName(Objects.requireNonNullElse(env.getProperty("real.driver-class-name"), "oracle.jdbc.OracleDriver"));
hikariDataSource.setJdbcUrl(env.getProperty("real.db-url"));
hikariDataSource.setUsername(env.getProperty("real.username"));
hikariDataSource.setPassword(env.getProperty("real.password"));
hikariDataSource.setMinimumIdle(Integer.parseInt(Objects.requireNonNullElse(env.getProperty("real.minPoolSize"), "1")));
hikariDataSource.setMaximumPoolSize(Integer.parseInt(Objects.requireNonNullElse(env.getProperty("real.maxPoolSize"), "10")));
Properties props = new Properties();
props.setProperty("maxStatements", env.getProperty("real.maxStatements", "300"));
hikariDataSource.setDataSourceProperties(props);
hikariDataSource.setPoolName(env.getProperty("real.pool-name"));
hikariDataSource.setConnectionTestQuery(env.getProperty("real.connection-test-query"));
return hikariDataSource;
}
@Primary
@Bean
public LocalContainerEntityManagerFactoryBean realEntityManager(EntityManagerFactoryBuilder builder) throws SQLException {
return builder
.dataSource(realDataSource())
.packages("com.some.project.files.entity.real")
.persistenceUnit("real")
.build();
}
@Primary
@Bean(name = "transactionManager")
public JpaTransactionManager realTransactionManager(EntityManagerFactory realEntityManager) {
return new JpaTransactionManager(realEntityManager);
}
}
这是另一个:
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef = "histEntityManager",
basePackages = {"com.some.project.files.repository.hist"}
)
@RequiredArgsConstructor
@Log4j2
@AutoConfigureOrder(3)
public class HistDatasourceConfig {
private final Environment env;
@Bean
public DataSource histDataSource() throws SQLException {
HikariDataSource hikariDataSource = new HikariDataSource();
hikariDataSource.setDriverClassName(Objects.requireNonNullElse(env.getProperty("hist.driver-class-name"), "oracle.jdbc.OracleDriver"));
hikariDataSource.setJdbcUrl(env.getProperty("hist.jdbc-url"));
hikariDataSource.setUsername(env.getProperty("hist.username"));
hikariDataSource.setPassword(env.getProperty("hist.password"));
hikariDataSource.setMinimumIdle(Integer.parseInt(Objects.requireNonNullElse(env.getProperty("hist.minPoolSize"), "1")));
hikariDataSource.setMaximumPoolSize(Integer.parseInt(Objects.requireNonNullElse(env.getProperty("hist.maxPoolSize"), "10")));
Properties props = new Properties();
props.setProperty("maxStatements", env.getProperty("hist.maxStatements", "300"));
hikariDataSource.setDataSourceProperties(props);
hikariDataSource.setPoolName(env.getProperty("hist.pool-name"));
hikariDataSource.setConnectionTestQuery(env.getProperty("hist.connection-test-query"));
return hikariDataSource;
}
@Bean("histEntityManager")
public LocalContainerEntityManagerFactoryBean histEntityManager(EntityManagerFactoryBuilder builder) throws SQLException {
return builder
.dataSource(histDataSource())
.packages("com.some.project.files.entity.hist")
.persistenceUnit("hist")
.build();
}
@Bean
public JpaTransactionManager histTransactionManager(EntityManagerFactory histEntityManager) {
return new JpaTransactionManager(histEntityManager);
}
}
问题在于组织实体。 如果我只保存另一个它会保存。
但是,如果我尝试像这样保存 hist 实体:
@Override
@Transactional
public void someMethod() {
SomeEntity entity = new SomeEntity("abc", 123);
SomeRepository.save(entity);
SomeEntityHist entityHist = new SomeEntityHist(1L, "abc", 123);
SomeRepositoryHist.save(entityHist);
}
它保存第一个,但不保存历史记录,当我查看日志时,它只是调用 select 查询而不插入。
如果我尝试使用saveAndFlush
方法保存 hist 实体,则会出现错误。
我能做些什么的原因是什么。 是关于配置文件的吗?
您使用@Transactional
注释该方法,在后台 Spring 使用@Primary
事务管理器打开一个事务,在您的情况下是名为"transactionManager"
的 bean,对应于"realDataSource"
。 在这个阶段,只为第一个数据库打开了一个事务,而不是为您的历史数据库打开了一个事务,这就是您收到此错误的原因。
如果你想为你的第二个数据源打开一个事务,你必须 select 对应的事务管理器@Transactional(transactionManager = "histTransactionManager")
由于您不能使用不同的@Transactional
注释两次相同的方法,因此您可以研究像 Atomikos 这样的分布式事务的解决方案。 Spring 曾经提供自己的解决方案ChainedTransactionManager ,现在已弃用。
如果可能的话,另一个解决方案是摆脱第二个数据源。
您的方法调用来自哪里? 您可以分享在您提供的示例之前执行的所有代码吗? 例如,调度程序没有 SessionContext。 你有什么理由不使用自动配置的数据源吗? 这通常更容易。 我猜你不见了
@EnableJpaRepositories(
entityManagerFactoryRef = "realEntityManager",
basePackages = {"com.some.project.files.repository.real"}
transactionManagerRef = "__yourTransactionManagerReference__" <--- you are probably missing this
)
我也不确定,但我认为你需要一个“PlatformTransactionManager”而不是一个简单的事务管理器。 您可能会从https://www.baeldung.com/spring-boot-configure-multiple-datasources获得有用的信息。 你用的是什么 Spring-Boot 版本?
@Airy 和 @GJohannes 发布的两个答案都指出了我缺少的部分,但我还需要添加一件事: @Qualifier("histEntityManager")
这是对我有用的最终配置文件:
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef = "histEntityManager",
transactionManagerRef = "histTransactionManager",
basePackages = {"com.some.project.files.repository.hist"}
)
@RequiredArgsConstructor
@Log4j2
@AutoConfigureOrder(3)
public class HistDatasourceConfig {
private final Environment env;
@Bean
public DataSource histDataSource() throws SQLException {
HikariDataSource hikariDataSource = new HikariDataSource();
hikariDataSource.setDriverClassName(Objects.requireNonNullElse(env.getProperty("hist.driver-class-name"), "oracle.jdbc.OracleDriver"));
hikariDataSource.setJdbcUrl(env.getProperty("hist.jdbc-url"));
hikariDataSource.setUsername(env.getProperty("hist.username"));
hikariDataSource.setPassword(env.getProperty("hist.password"));
hikariDataSource.setMinimumIdle(Integer.parseInt(Objects.requireNonNullElse(env.getProperty("hist.minPoolSize"), "1")));
hikariDataSource.setMaximumPoolSize(Integer.parseInt(Objects.requireNonNullElse(env.getProperty("hist.maxPoolSize"), "10")));
Properties props = new Properties();
props.setProperty("maxStatements", env.getProperty("hist.maxStatements", "300"));
hikariDataSource.setDataSourceProperties(props);
hikariDataSource.setPoolName(env.getProperty("hist.pool-name"));
hikariDataSource.setConnectionTestQuery(env.getProperty("hist.connection-test-query"));
return hikariDataSource;
}
@Bean("histEntityManager")
public LocalContainerEntityManagerFactoryBean histEntityManager(EntityManagerFactoryBuilder builder) throws SQLException {
return builder
.dataSource(histDataSource())
.packages("com.some.project.files.entity.hist")
.persistenceUnit("hist")
.build();
}
@Bean
public JpaTransactionManager histTransactionManager(@Qualifier("histEntityManager") EntityManagerFactory histEntityManager) {
return new JpaTransactionManager(histEntityManager);
}
}
当然,我正在添加@Transactional(transactionManager = "histTransactionManager")
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.