java - 无法使用具有 2 个 spring 上下文的存储库和服务

标签 java spring spring-batch spring-config

我有 1 个名为 PersistenceJPAConfig 的 spring 上下文。现在我想配置一个 Spring Batch,为此我添加了一个带有 @Configuration 注释和 @EnableBatchProcessing 的新类。添加新的配置类后,我在尝试使用存储库方法时遇到错误:嵌套异常是 javax.persistence.TransactionRequiredException:没有事务正在进行。我知道这是因为我有一个父 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 Batch 的第二个上下文:

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

    }


}

最佳答案

您正在将MapJobRepositoryResourcelessTransactionManager结合使用。使用此配置,Spring Batch 端没有事务。因此会出现错误没有交易正在进行

您需要使用在 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+。

关于java - 无法使用具有 2 个 spring 上下文的存储库和服务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54874168/

相关文章:

java - 有没有办法在 Java 中实现代数类型?

java - WCF自动生成的WSDL

java - 检查 Apache POI 库中的单元格是否为空

java - 使用 PropertyOverrrideConfigurer 覆盖 bean 引用

instance - Spring 批处理 : Restartable attribute ignored

java - Spring Batch 中的复制作业

elasticsearch - 如何在Elastic APM中启用对Spring批处理的监视?

java - 我无法编辑文本文件中的记录。语言 : JAVA IDE: NETBEANS

java - JmsTemplate 无法发送响应,因为 javax.jms.IllegalStateException : Session is closed

java - JPA Select 查询不返回一个字母单词的结果