【问题标题】:Configure transaction in Spring 4.1.5 without XML在没有 XML 的 Spring 4.1.5 中配置事务
【发布时间】:2015-05-24 12:01:02
【问题描述】:

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

不幸的是,我是 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 中,参数 this 有正确的连接,但是有类似的东西:

_conn.commit();

_conn有不同的联系。为什么?

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

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

【问题讨论】:

  • 您的问题是什么?有什么问题?
  • 问题是我无法正确配置它...即使下面的指南不起作用...
  • 问题应该是:如何提交或回滚先前回调中插入表的数据。
  • 您希望数据库中有哪些数据?!!
  • 这些来自生成数据的方法。我从数据库调用一个将数据插入表的过程。而已。之后,我想决定是提交还是回滚。

标签: java spring jdbc spring-transactions


【解决方案1】:

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

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

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

记得把@EnableTransactionManagement添加到你的spring应用/主类

【讨论】:

  • 感谢您的回答。但这对我不起作用:(我在@qtips回答中说了更多,请看一下:)
【解决方案2】:

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

请参阅 Springs 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。没有时间给你适当的样品,但如果你仍然卡住,可能会在以后的日子里这样做;)

【讨论】:

  • 感谢您的回复!不幸的是它没有用......我在 ApplicationConfig 类中添加了注释 EnableTransactionManagement 并在我的 dao 方法中添加了注释 Transactional ,但随后出现错误:“没有定义类型 [org.springframework.transaction.PlatformTransactionManager] 的合格 bean”所以我也在服务方法中添加了事务,然后我得到了同样的错误。我用谷歌搜索了错误,发现link 然后我没有错误,但它不起作用。我在控制台“提交”中获得信息,但在数据库表中看不到新记录。
  • 您是否不小心用 xml 上下文覆盖了事务设置? stackoverflow.com/questions/5491748/…。你也可以尝试打开调试日志看看会发生什么。你如何测试你的代码?我还阅读了一些关于单元测试默认回滚的内容(但是你应该得到一些关于它的消息)。使用调试日志和测试方式编辑您的问题,以便其他人可以提供帮助;)
  • 不,我不会覆盖它,我什至不会在我的应用程序中使用 xml。一切都基于 Java 类。我也不使用单元测试。我已经更新了我的问题,但我不知道如何设置调试日志。
【解决方案3】:
@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;
    }
}

【讨论】:

猜你喜欢
  • 2014-01-24
  • 1970-01-01
  • 2010-10-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-02
  • 2011-10-08
  • 1970-01-01
相关资源
最近更新 更多