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

时间:2015-03-20 23:26:30

标签: java spring jdbc spring-transactions

我正在编写与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);
    }
}

Dao实施:

@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 有不同的连接。为什么呢?

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

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

3 个答案:

答案 0 :(得分:0)

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

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

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

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

答案 1 :(得分:0)

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

请参阅弹簧docs,了解如何使用两者。 @EnableTransactionManagementJavaDocs中有详细说明,但应与XML配置相匹配。

更新

问题是您正在将原始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。没有时间给你适当的样品,但如果你仍然卡住,可能会在以后的日子里这样做;)

答案 2 :(得分:0)

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