如何获得一个spring JdbcTemplate来read_uncommitted?

时间:2012-07-27 05:01:09

标签: java spring jdbc jdbctemplate spring-jdbc

首先,我不能使用声明性@Transactional方法,因为应用程序有多个JDBC数据源,我不想厌倦细节,但足以说DAO方法通过了正确的数据源来执行逻辑。所有JDBC数据源都具有相同的模式,当我公开ERP系统的休息服务时,它们是分开的。

由于这个遗留系统有很多长期存在的锁定记录,我无法控制,所以我想要脏读。

使用JDBC我会执行以下操作:

private Customer getCustomer(DataSource ds, String id) {
    Customer c = null;
    PreparedStatement stmt = null;
    Connection con = null;
    try {
        con = ds.getConnection();
        con.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
        stmt = con.prepareStatement(SELECT_CUSTOMER);
        stmt.setString(1, id);
        ResultSet res = stmt.executeQuery();
        c = buildCustomer(res);
    } catch (SQLException ex) {
        // log errors
    } finally {
        // Close resources
    }
    return c;
}

好的,我知道很多锅炉。所以我试过JdbcTemplate因为我正在使用spring。

使用JdbcTemplate

private Customer getCustomer(JdbcTemplate t, String id) {
    return t.queryForObject(SELECT_CUSTOMER, new CustomerRowMapper(), id);
}

好多了,但它仍然使用默认事务隔离。我需要以某种方式改变这一点。所以我考虑使用TransactionTemplate

private Customer getCustomer(final TransactionTemplate tt,
                             final JdbcTemplate t,
                             final String id) {
    return tt.execute(new TransactionCallback<Customer>() {
        @Override
        public Customer doInTransaction(TransactionStatus ts) {
            return t.queryForObject(SELECT_CUSTOMER, new CustomerRowMapper(), id);
        }
    });
}

但是如何在此处设置事务隔离?我无法在回调的任何地方找到它或TransactionTemplate来执行此操作。

我正在阅读Spring in Action,第三版,它解释了我已经完成的事情,尽管关于事务的章节继续使用带注释的声明式事务,但如上所述我不能使用它作为我的DAO需要根据提供的参数在运行时确定使用哪个数据源,在我的例子中是国家代码。

非常感谢任何帮助。

3 个答案:

答案 0 :(得分:1)

使用TransactionTemplate可以帮助您,您需要适当地配置它。事务模板还包含事务配置。实际上TransactionTemplate扩展了DefaultTransactionDefinition

所以在你的配置中你应该有这样的东西。

<bean id="txTemplate" class=" org.springframework.transaction.support.TransactionTemplate">
  <property name="isolationLevelName" value="ISOLATION_READ_UNCOMMITTED"/>
  <property name="readOnly" value="true" />
  <property name="transactionManager" ref="transactionManager" />
</bean>

如果您随后将此bean注入您的课程,您应该能够使用之前发布/尝试过的基于TransactionTemplate的代码。

然而,可能有一个更好的解决方案可以清理您的代码。对于我参与的其中一个项目,我们有与您相似的设置(单个应用程序多个数据库)。为此我们写了一些spring代码,它基本上在需要时切换数据源。可以找到更多信息here

如果你的应用程序是远程获取或过度杀戮,你也可以尝试使用Spring AbstractRoutingDataSource,它基于查找键(在你的例子中是国家代码)选择要使用的正确数据源。

通过使用这两种解决方案中的任何一种,您都可以开始使用spring declarative transactionmanagement方法(这应该会大大清理您的代码)。

答案 1 :(得分:0)

我目前通过直接使用DataSourceTransactionManager来解决这个问题,虽然看起来我没有像我最初希望的那样节省多少锅炉。不要误解我,它更干净,虽然我仍然无法帮助,但我觉得必须有一个更简单的方法。我不需要读取事务,我只想设置隔离。

private Customer getCustomer(final DataSourceTransactionManager txMan,
                             final JdbcTemplate t,
                             final String id) {
    DefaultTransactionDefinition def = new DefaultTransactionDefinition();
    def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED);

    TransactionStatus status = txMan.getTransaction(def);
    Customer c = null;
    try {
        c = t.queryForObject(SELECT_CUSTOMER, new CustomerRowMapper(), id);
    } catch (Exception ex) {
        txMan.rollback(status);
        throw ex;
    }
    txMan.commit(status);
    return c;
}

我仍然会暂时保留这个问题,因为我真的相信必须有更好的方法。

请参阅Spring 3.1.x Documentation - Chapter 11 - Transaction Management

答案 2 :(得分:0)

定义代理数据源,类为org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy并设置事务隔离级别。通过setter或构造函数注入实际数据源。

<bean id="yourDataSource" class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy">
    <constructor-arg index="0" ref="targetDataSource"/>
    <property name="defaultTransactionIsolationName" value="TRANSACTION_READ_UNCOMMITTED"/>
</bean>