在没有XML的情况下在Spring 4.1.5中配置事务

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在没有XML的情况下在Spring 4.1.5中配置事务相关的知识,希望对你有一定的参考价值。

我正在编写与Oracle数据库连接的应用程序。我从DB调用函数,将新记录插入表。在回调之后,我可以决定我想做什么:提交或回滚。

不幸的是我是Spring的新手,所以配置有问题。而且我更希望在Java类中进行此配置,而不是在XML中。在这里,我需要你的帮助。

更新的代码:

ApplicationConfig代码:

@Configuration
@EnableTransactionManagement
@ComponentScan("hr")
@PropertySource({"classpath:jdbc.properties", "classpath:functions.properties", "classpath:procedures.properties"})
public class ApplicationConfig {

    @Autowired
    private Environment env;

    @Bean(name="dataSource")
    public DataSource dataSource() {
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName(env.getProperty("jdbc.driver"));
        dataSource.setUrl(env.getProperty("jdbc.url"));
        dataSource.setUsername(env.getProperty("jdbc.username"));
        dataSource.setPassword(env.getProperty("jdbc.password"));
        dataSource.setDefaultAutoCommit(false);
        return dataSource;
    }

    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        jdbcTemplate.setResultsMapCaseInsensitive(true);
        return jdbcTemplate;
    }

    @Bean(name="txName")
    public PlatformTransactionManager txManager() {
        DataSourceTransactionManager txManager = new DataSourceTransactionManager();
        txManager.setDataSource(dataSource());
        return txManager;
    }
}

我有Dao和Service,两者都实现了正确的接口。

服务实施:

@Service
public class HumanResourcesServiceImpl implements HumanResourcesService {

    @Autowired
    private HumanResourcesDao hrDao;

    @Override
    public String generateData(int rowsNumber) {
        return hrDao.generateData(rowsNumber);
    }

    @Override
    @Transactional("txName")
    public void shouldCommit(boolean doCommit, Connection connection) throws SQLException {
        hrDao.shouldCommit(doCommit, connection);
    }
}

道实施:

@Repository
public class HumanResourcesDaoImpl implements HumanResourcesDao {

    private JdbcTemplate jdbcTemplate;
    private SimpleJdbcCall generateData;

    @Autowired
    public HumanResourcesDaoImpl(JdbcTemplate jdbcTemplate, Environment env) {
        this.jdbcTemplate = jdbcTemplate;
        generateData = new SimpleJdbcCall(jdbcTemplate)
            .withProcedureName(env.getProperty("procedure.generateData"));
    }

    @Override
    public String generateData(int rowsNumber) {
        HashMap<String, Object> params = new HashMap<>();
        params.put("i_rowsNumber", rowsNumber);
        Map<String, Object> m = generateData.execute(params);
        return (String) m.get("o_execution_time");
    }

    @Override
    @Transactional("txName")
    public void shouldCommit(boolean doCommit, Connection connection) throws SQLException {
        if(doCommit) {
            connection.commit();
        } else {
            connection.rollback();
        }
    }
}

主类代码:

public class Main extends Application implements Initializable {
    @Override
    public void initialize(URL url, ResourceBundle resourceBundle) {
        ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);

        hrService = context.getBean(HumanResourcesService.class);

        BasicDataSource ds = (BasicDataSource)context.getBean("dataSource");
        Connection connection = ds.getConnection();

        //do something and call
        //hrService.generateData
        //do something and call
        //hrService.shouldCommit(true, connection);
        //which commit or rollback generated data from previoues callback
    }
}

更新:

我认为问题在于连接,因为这句话:

this.jdbcTemplate.getDataSource().getConnection();

创建新连接,因此无需提交或回滚。但我仍然无法理解为什么这不能正常工作。没有错误,没有新的记录......什么是奇怪的,就是当我调试connection.commit();我发现在DelegatingConnection.java中,参数有正确的连接,但有类似的东西:

_conn.commit();

和_conn有不同的连接。为什么?

我应该以某种方式同步这两种方法的连接或什么?或者这只是一个连接?说实话,我不确定它是如何工作的。存储过程的一个连接和所有回调都在这个连接中,或者每个回调都可能创建新的连接?

真正的问题是如何从先前的回调中提交或回滚数据,这些数据会插入到表中?

答案

一个简单的方法是使用@Transactional注释方法:

@Transactional
public void myBeanMethod() {
    ...
    if (!doCommit)
        throw new IllegalStateException(); // any unchecked will do
}

和spring将回滚所有数据库更改。

请记住将@EnableTransactionManagement添加到您的spring应用程序/主类中

另一答案

您可以使用@Transactional@EnableTransactionManagement来设置事务,而无需使用XML配置。简而言之,使用@Transactional注释要进行事务的方法/类。要设置事务管理,请在@EnableTransactionManagement中使用@Configuration

有关如何使用两者的信息,请参阅Springs docs@EnableTransactionManagementJavaDocs中有详细说明,但应与XML配置相匹配。

UPDATE

问题是您正在将原始JDBC调用(java.sql.Connection)与Spring JDBC混合。当你执行你的SimpleJdbcCall时,Spring会创建一个新的Connection。这与你后来尝试提交的Connection不同。因此,执行提交时没有任何反应。我试图以某种方式获得SimpleJdbcCall使用的连接,但找不到任何简单的方法。

为了测试这个我尝试了以下(我没有使用params):

@Override
public String generateData(int rowsNumber) {
    //HashMap<String, Object> params = new HashMap<>();
    //params.put("i_rowsNumber", rowsNumber);
    //Map<String, Object> m = generateData.execute(params);

     Connection targetConnection = DataSourceUtils.getTargetConnection(generateData.getJdbcTemplate().getDataSource().getConnection());
     System.out.println(targetConnection.prepareCall((generateData.getCallString())).execute());
  targetConnection.commit();

    return (String) m.get("o_execution_time");
}

如果我不保存targetConnection,而是在提交时通过调用DataSourceUtils.getTargetConnection()再次尝试获取连接,则没有任何反应。因此,您必须在执行语句的同一连接上提交。这似乎并不容易,也不是正确的方法。

解决方案是放弃java.sql.Connection.commit()电话。相反,您完全使用Spring Transactions。如果在执行数据库调用的方法上使用@Transaction,Spring将在方法完成时自动提交。如果方法体遇到任何异常(甚至在实际数据库调用之外),它将自动回滚。换句话说,这应该足以进行正常的事务管理。

但是,如果您正在进行批处理,并希望通过提交和回滚更好地控制您的事务,您仍然可以使用Spring。要以编程方式控制Spring的事务,可以使用具有提交和回滚方法的TransactionTemplate。没有时间给你适当的样品,但如果你仍然卡住,可以在以后的日子里这样做;)

另一答案
@Configuration
@EnableTransactionManagement
@ComponentScan(basePackages="org.saat")
@PropertySource(value="classpath:resources/db.properties",ignoreResourceNotFound=true)
public class AppConfig {

    @Autowired
    private Environment env;

    @Bean(name="dataSource")
    public DataSource getDataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(env.getProperty("db.driver"));
        dataSource.setUrl(env.getProperty("db.url"));
        dataSource.setUsername(env.getProperty("db.username"));
        dataSource.setPassword(env.getProperty("db.password"));
        return dataSource;
    }


    @Bean(name="entityManagerFactoryBean")
    public LocalContainerEntityManagerFactoryBean getSessionFactory() {
        LocalContainerEntityManagerFactoryBean  factoryBean = new LocalContainerEntityManagerFactoryBean ();
        factoryBean.setDataSource(getDataSource());
        factoryBean.setPackagesToScan("org.saat");
        factoryBean.setJpaVendorAdapter(getJpaVendorAdapter());
        Properties props=new Properties();
        props.put("hibernate.dialect", env.getProperty("hibernate.dialect"));
        props.put("hibernate.hbm2ddl.auto",env.getProperty("hibernate.hbm2ddl.auto"));
        props.put("hibernate.show_sql",env.getProperty("hibernate.show_sql"));
        factoryBean.setJpaProperties(props);
        return factoryBean;
    }


    @Bean(name="transactionManager")
    public JpaTransactionManager getTransactionManager() {
        JpaTransactionManager jpatransactionManager = new JpaTransactionManager();
        jpatransactionManager.setEntityManagerFactory(getSessionFactory().getObject());
        return jpatransactionManager;
    }

    @Bean
    public JpaVendorAdapter getJpaVendorAdapter() {
        HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter();
        return hibernateJpaVendorAdapter;
    }
}

以上是关于在没有XML的情况下在Spring 4.1.5中配置事务的主要内容,如果未能解决你的问题,请参考以下文章

在"Spring"中配编码格式的方法.

如何在没有spring boot actuator的情况下在spring cloud中使用zuul?

如何在没有 spring-boot 的情况下在 spring-webflux 中加载配置?

SpringMVC中配aop拦截不生效,咋回事

SpringMVC中配aop拦截不生效,咋回事

如何在不维护 jsessionid 的情况下在 Spring Boot 中保护 RESTful API