无法使用具有 2 spring 个上下文的存储库和服务
Failed to use repositories and services having 2 spring contexts
我有 1 个 spring 名为 PersistenceJPAConfig 的上下文。现在我想配置一个 spring 批处理,为此我添加了一个带有 @Configuration 注释和 @EnableBatchProcessing 的新 class。添加新配置 class 后,我在尝试使用存储库方法时遇到错误:nested exception is javax.persistence.TransactionRequiredException: no transaction is in progress
。我知道这是因为我有一个父 spring 上下文和一个子上下文,这意味着我将有 2 个实例用于每个存储库和每个服务。我试图排除存储库扫描和服务扫描:
@ComponentScan(useDefaultFilters = false,
excludeFilters = {@ComponentScan.Filter(Repository.class), @ComponentScan.Filter(Service.class), @ComponentScan.Filter(Configuration.class)})
但它不起作用。到目前为止唯一的解决方案是将所有 bean 从第二个配置移动到第一个配置,但我不希望那样。如何解决上下文之间的冲突?
主要背景:
package com.netoptics.server;
import java.util.Properties;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import com.websilicon.security.SecurityGlobals;
import com.websilicon.util.AppConfig;
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories({"com.whitelist.manager.repositories", "com.wsnms", "com.websilicon"})
public class PersistenceJPAConfig {
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource());
entityManagerFactoryBean.setPackagesToScan("com.whitelist.manager", "com.wsnms", "com.websilicon");
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
entityManagerFactoryBean.setJpaVendorAdapter(vendorAdapter);
entityManagerFactoryBean.setJpaProperties(additionalProperties());
return entityManagerFactoryBean;
}
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
@Bean
public DataSource dataSource() {
String databaseDriver = AppConfig.getInstance().getString("dataDatabaseDriver", "");
String databaseUrl = AppConfig.getInstance().getString("dataDatabaseUrl", "");
String databaseUsername = AppConfig.getInstance().getString("dataDatabaseUsername", "");
String dataDatabasePassword = AppConfig.getInstance().getPassword("dataDatabasePassword", SecurityGlobals.KEY, "");
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(databaseDriver);
dataSource.setUrl(databaseUrl);
dataSource.setUsername(databaseUsername);
dataSource.setPassword(dataDatabasePassword);
return dataSource;
}
@Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
private Properties additionalProperties() {
Properties properties = new Properties();
properties.setProperty("hibernate.hbm2ddl.auto", "none");
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.PostgreSQL82Dialect");
properties.setProperty("hibernate.show_sql", "false");
properties.setProperty("hibernate.jdbc.batch_size", "1000");
return properties;
}
}
用于配置 spring 批处理的第二个上下文:
@Configuration
@EnableBatchProcessing
@ComponentScan(useDefaultFilters = false,
excludeFilters = {@ComponentScan.Filter(Repository.class), @ComponentScan.Filter(Service.class)})
public class SaveImsiCSVBatchConfig {
@Autowired
public JobBuilderFactory jobBuilderFactory;
@Autowired
public StepBuilderFactory stepBuilderFactory;
@Autowired
public DataSource dataSource;
@Autowired
private Environment environment;
@Autowired
private WmAdminImsisResourceHelper wmAdminImsisResourceHelper;
@Bean
public JobRepository jobRepository() {
MapJobRepositoryFactoryBean factoryBean = new MapJobRepositoryFactoryBean(new ResourcelessTransactionManager());
try {
return factoryBean.getObject();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
@Bean
public JobLauncher jobLauncher(JobRepository jobRepository) {
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
jobLauncher.setJobRepository(jobRepository);
return jobLauncher;
}
@Bean
@StepScope
public JdbcCursorItemReader<WmPushedImsi> reader(@Value("#{jobParameters['sortProperty']}") String sortProperty,
@Value("#{jobParameters['sortValue']}") String sortValue, @Value("#{jobParameters['username']}") String usernameFilter,
@Value("#{jobParameters['imsinumber']}") String imsiNumberFilter) {
JdbcCursorItemReader<WmPushedImsi> reader = new JdbcCursorItemReader<>();
reader.setDataSource(dataSource);
String sql =
"select us.username, wp.imsinumber, wp.startdate, wp.expiredate, case when wp.failedpushbatchid is not null or wp.faileddeletebatchid is not null then 'Yes' ELSE 'No' end as dirty from\n"
+ "wm_pushed_imsi wp INNER JOIN wm_admin_user wu on wp.userid = wu.id INNER JOIN users us on wu.userid = us.id";
if (usernameFilter != null && imsiNumberFilter != null) {
sql += " AND us.username LIKE '%" + usernameFilter + "%' AND wp.imsinumber LIKE '%" + imsiNumberFilter + "%'";
} else if (usernameFilter != null) {
sql += " AND us.username LIKE '%" + usernameFilter + "%'";
} else if (imsiNumberFilter != null) {
sql += " AND wp.imsinumber LIKE '%" + imsiNumberFilter + "%'";
}
if (sortProperty != null) {
sql += " order by " + sortProperty + " " + sortValue;
}
reader.setSql(sql);
reader.setRowMapper(new ImsiRowMapper());
return reader;
}
@Bean
public ImsiProcessor processor() {
return new ImsiProcessor();
}
@Bean
@StepScope
public FlatFileItemWriter<WmPushedImsi> writer(@Value("#{jobParameters['currentDate']}") Date currentDate) {
wmAdminImsisResourceHelper.createDirectoryForSavingCsv();
String fileName = wmAdminImsisResourceHelper.createFileNameForCsv(currentDate) + environment.getProperty("CSVEXTENSION");
String columnsTitle = Arrays.toString(new String[] {environment.getProperty("CSV_IMSINUMBER"), environment.getProperty("CSV_USERNAME"),
environment.getProperty("CSV_STARTDATE"), environment.getProperty("CSV_EXPIREDATE"), environment.getProperty("CSV_DIRTY")});
FlatFileItemWriter<WmPushedImsi> writer = new FlatFileItemWriter<>();
writer.setResource(new FileSystemResource(fileName));
writer.setHeaderCallback(writerHeader -> writerHeader.write(columnsTitle.substring(1, columnsTitle.length() - 1)));
writer.setLineAggregator(new DelimitedLineAggregator<>() {
{
setDelimiter(",");
setFieldExtractor(new BeanWrapperFieldExtractor<>() {
{
setNames(new String[] {WmPushedImsi_.IMSI_NUMBER, "username", WmPushedImsi_.START_DATE, WmPushedImsi_.EXPIRE_DATE, "dirty"});
}
});
}
});
return writer;
}
@Bean
public Step stepToCreateCsvFile() {
return stepBuilderFactory.get(Objects.requireNonNull(environment.getProperty("CSV_STEP_CREATE_FILE"))).<WmPushedImsi, WmPushedImsi>chunk(50000)
.reader(reader("", "", "", "")).processor(processor()).writer(writer(null)).build();
}
@Bean
public Step stepToDeleteFileAndCreateArchive() {
FileArchiveAndDeletingTasklet task = new FileArchiveAndDeletingTasklet();
task.setWmAdminImsisResourceHelper(wmAdminImsisResourceHelper);
task.setDateString(environment.getProperty("CSV_DATE"));
return stepBuilderFactory.get(Objects.requireNonNull(environment.getProperty("CSV_STEP_CREATE_ARCHIVE"))).tasklet(task).build();
}
@Bean
public Job exportImsiCSVJob() {
return jobBuilderFactory.get(Objects.requireNonNull(environment.getProperty("CSV_EXPORT_JOB"))).incrementer(new RunIdIncrementer())
.start(stepToCreateCsvFile()).next(stepToDeleteFileAndCreateArchive()).build();
}
@Bean
public JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor(JobRegistry jobRegistry) {
JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor = new JobRegistryBeanPostProcessor();
jobRegistryBeanPostProcessor.setJobRegistry(jobRegistry);
return jobRegistryBeanPostProcessor;
}
public class ImsiRowMapper implements RowMapper<WmPushedImsi> {
@Override
public WmPushedImsi mapRow(ResultSet rs, int rowNum) throws SQLException {
WmPushedImsi wmPushedImsi = new WmPushedImsi();
wmPushedImsi.setImsiNumber(rs.getString(WmPushedImsi_.IMSI_NUMBER));
wmPushedImsi.setUsername(rs.getString("username"));
wmPushedImsi.setStartDate(rs.getDate(WmPushedImsi_.START_DATE));
wmPushedImsi.setExpireDate(rs.getDate(WmPushedImsi_.EXPIRE_DATE));
wmPushedImsi.setDirty(rs.getString("dirty"));
return wmPushedImsi;
}
}
}
您正在使用 MapJobRepository
和 ResourcelessTransactionManager
。使用此配置,Spring 批处理端没有事务。因此错误 no transaction is in progress
.
您需要使用您在 PersistenceJPAConfig
中定义的事务管理器配置 JobRepository
。为此,您必须定义一个 BatchConfigurer
类型的 bean 并覆盖 getTransactionManager
。这是一个例子:
@Bean
public BatchConfigurer batchConfigurer() {
return new DefaultBatchConfigurer() {
@Override
public PlatformTransactionManager getTransactionManager() {
return new MyTransactionManager();
}
};
}
有关详细信息,请查看参考文档的 Java config 部分。请注意,这需要 Spring Batch v4.1+.
我有 1 个 spring 名为 PersistenceJPAConfig 的上下文。现在我想配置一个 spring 批处理,为此我添加了一个带有 @Configuration 注释和 @EnableBatchProcessing 的新 class。添加新配置 class 后,我在尝试使用存储库方法时遇到错误:nested exception is javax.persistence.TransactionRequiredException: no transaction is in progress
。我知道这是因为我有一个父 spring 上下文和一个子上下文,这意味着我将有 2 个实例用于每个存储库和每个服务。我试图排除存储库扫描和服务扫描:
@ComponentScan(useDefaultFilters = false,
excludeFilters = {@ComponentScan.Filter(Repository.class), @ComponentScan.Filter(Service.class), @ComponentScan.Filter(Configuration.class)})
但它不起作用。到目前为止唯一的解决方案是将所有 bean 从第二个配置移动到第一个配置,但我不希望那样。如何解决上下文之间的冲突?
主要背景:
package com.netoptics.server;
import java.util.Properties;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import com.websilicon.security.SecurityGlobals;
import com.websilicon.util.AppConfig;
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories({"com.whitelist.manager.repositories", "com.wsnms", "com.websilicon"})
public class PersistenceJPAConfig {
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource());
entityManagerFactoryBean.setPackagesToScan("com.whitelist.manager", "com.wsnms", "com.websilicon");
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
entityManagerFactoryBean.setJpaVendorAdapter(vendorAdapter);
entityManagerFactoryBean.setJpaProperties(additionalProperties());
return entityManagerFactoryBean;
}
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
@Bean
public DataSource dataSource() {
String databaseDriver = AppConfig.getInstance().getString("dataDatabaseDriver", "");
String databaseUrl = AppConfig.getInstance().getString("dataDatabaseUrl", "");
String databaseUsername = AppConfig.getInstance().getString("dataDatabaseUsername", "");
String dataDatabasePassword = AppConfig.getInstance().getPassword("dataDatabasePassword", SecurityGlobals.KEY, "");
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(databaseDriver);
dataSource.setUrl(databaseUrl);
dataSource.setUsername(databaseUsername);
dataSource.setPassword(dataDatabasePassword);
return dataSource;
}
@Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
private Properties additionalProperties() {
Properties properties = new Properties();
properties.setProperty("hibernate.hbm2ddl.auto", "none");
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.PostgreSQL82Dialect");
properties.setProperty("hibernate.show_sql", "false");
properties.setProperty("hibernate.jdbc.batch_size", "1000");
return properties;
}
}
用于配置 spring 批处理的第二个上下文:
@Configuration
@EnableBatchProcessing
@ComponentScan(useDefaultFilters = false,
excludeFilters = {@ComponentScan.Filter(Repository.class), @ComponentScan.Filter(Service.class)})
public class SaveImsiCSVBatchConfig {
@Autowired
public JobBuilderFactory jobBuilderFactory;
@Autowired
public StepBuilderFactory stepBuilderFactory;
@Autowired
public DataSource dataSource;
@Autowired
private Environment environment;
@Autowired
private WmAdminImsisResourceHelper wmAdminImsisResourceHelper;
@Bean
public JobRepository jobRepository() {
MapJobRepositoryFactoryBean factoryBean = new MapJobRepositoryFactoryBean(new ResourcelessTransactionManager());
try {
return factoryBean.getObject();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
@Bean
public JobLauncher jobLauncher(JobRepository jobRepository) {
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
jobLauncher.setJobRepository(jobRepository);
return jobLauncher;
}
@Bean
@StepScope
public JdbcCursorItemReader<WmPushedImsi> reader(@Value("#{jobParameters['sortProperty']}") String sortProperty,
@Value("#{jobParameters['sortValue']}") String sortValue, @Value("#{jobParameters['username']}") String usernameFilter,
@Value("#{jobParameters['imsinumber']}") String imsiNumberFilter) {
JdbcCursorItemReader<WmPushedImsi> reader = new JdbcCursorItemReader<>();
reader.setDataSource(dataSource);
String sql =
"select us.username, wp.imsinumber, wp.startdate, wp.expiredate, case when wp.failedpushbatchid is not null or wp.faileddeletebatchid is not null then 'Yes' ELSE 'No' end as dirty from\n"
+ "wm_pushed_imsi wp INNER JOIN wm_admin_user wu on wp.userid = wu.id INNER JOIN users us on wu.userid = us.id";
if (usernameFilter != null && imsiNumberFilter != null) {
sql += " AND us.username LIKE '%" + usernameFilter + "%' AND wp.imsinumber LIKE '%" + imsiNumberFilter + "%'";
} else if (usernameFilter != null) {
sql += " AND us.username LIKE '%" + usernameFilter + "%'";
} else if (imsiNumberFilter != null) {
sql += " AND wp.imsinumber LIKE '%" + imsiNumberFilter + "%'";
}
if (sortProperty != null) {
sql += " order by " + sortProperty + " " + sortValue;
}
reader.setSql(sql);
reader.setRowMapper(new ImsiRowMapper());
return reader;
}
@Bean
public ImsiProcessor processor() {
return new ImsiProcessor();
}
@Bean
@StepScope
public FlatFileItemWriter<WmPushedImsi> writer(@Value("#{jobParameters['currentDate']}") Date currentDate) {
wmAdminImsisResourceHelper.createDirectoryForSavingCsv();
String fileName = wmAdminImsisResourceHelper.createFileNameForCsv(currentDate) + environment.getProperty("CSVEXTENSION");
String columnsTitle = Arrays.toString(new String[] {environment.getProperty("CSV_IMSINUMBER"), environment.getProperty("CSV_USERNAME"),
environment.getProperty("CSV_STARTDATE"), environment.getProperty("CSV_EXPIREDATE"), environment.getProperty("CSV_DIRTY")});
FlatFileItemWriter<WmPushedImsi> writer = new FlatFileItemWriter<>();
writer.setResource(new FileSystemResource(fileName));
writer.setHeaderCallback(writerHeader -> writerHeader.write(columnsTitle.substring(1, columnsTitle.length() - 1)));
writer.setLineAggregator(new DelimitedLineAggregator<>() {
{
setDelimiter(",");
setFieldExtractor(new BeanWrapperFieldExtractor<>() {
{
setNames(new String[] {WmPushedImsi_.IMSI_NUMBER, "username", WmPushedImsi_.START_DATE, WmPushedImsi_.EXPIRE_DATE, "dirty"});
}
});
}
});
return writer;
}
@Bean
public Step stepToCreateCsvFile() {
return stepBuilderFactory.get(Objects.requireNonNull(environment.getProperty("CSV_STEP_CREATE_FILE"))).<WmPushedImsi, WmPushedImsi>chunk(50000)
.reader(reader("", "", "", "")).processor(processor()).writer(writer(null)).build();
}
@Bean
public Step stepToDeleteFileAndCreateArchive() {
FileArchiveAndDeletingTasklet task = new FileArchiveAndDeletingTasklet();
task.setWmAdminImsisResourceHelper(wmAdminImsisResourceHelper);
task.setDateString(environment.getProperty("CSV_DATE"));
return stepBuilderFactory.get(Objects.requireNonNull(environment.getProperty("CSV_STEP_CREATE_ARCHIVE"))).tasklet(task).build();
}
@Bean
public Job exportImsiCSVJob() {
return jobBuilderFactory.get(Objects.requireNonNull(environment.getProperty("CSV_EXPORT_JOB"))).incrementer(new RunIdIncrementer())
.start(stepToCreateCsvFile()).next(stepToDeleteFileAndCreateArchive()).build();
}
@Bean
public JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor(JobRegistry jobRegistry) {
JobRegistryBeanPostProcessor jobRegistryBeanPostProcessor = new JobRegistryBeanPostProcessor();
jobRegistryBeanPostProcessor.setJobRegistry(jobRegistry);
return jobRegistryBeanPostProcessor;
}
public class ImsiRowMapper implements RowMapper<WmPushedImsi> {
@Override
public WmPushedImsi mapRow(ResultSet rs, int rowNum) throws SQLException {
WmPushedImsi wmPushedImsi = new WmPushedImsi();
wmPushedImsi.setImsiNumber(rs.getString(WmPushedImsi_.IMSI_NUMBER));
wmPushedImsi.setUsername(rs.getString("username"));
wmPushedImsi.setStartDate(rs.getDate(WmPushedImsi_.START_DATE));
wmPushedImsi.setExpireDate(rs.getDate(WmPushedImsi_.EXPIRE_DATE));
wmPushedImsi.setDirty(rs.getString("dirty"));
return wmPushedImsi;
}
}
}
您正在使用 MapJobRepository
和 ResourcelessTransactionManager
。使用此配置,Spring 批处理端没有事务。因此错误 no transaction is in progress
.
您需要使用您在 PersistenceJPAConfig
中定义的事务管理器配置 JobRepository
。为此,您必须定义一个 BatchConfigurer
类型的 bean 并覆盖 getTransactionManager
。这是一个例子:
@Bean
public BatchConfigurer batchConfigurer() {
return new DefaultBatchConfigurer() {
@Override
public PlatformTransactionManager getTransactionManager() {
return new MyTransactionManager();
}
};
}
有关详细信息,请查看参考文档的 Java config 部分。请注意,这需要 Spring Batch v4.1+.