8

首先,我不能使用声明性@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因此,自从我使用弹簧以来,我就已经尝试过了。

使用 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 需要确定运行时根据提供的参数使用哪个数据源,在我的例子中是国家代码。

任何帮助将不胜感激。

4

4 回答 4

6

我目前已经通过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 文档 - 第 11 章 - 事务管理

于 2012-07-27T05:41:37.827 回答
4

使用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 代码,它们基本上在需要时切换数据源。更多信息可以在这里找到。

如果这对您的应用程序来说过于牵强或过度杀伤力,您也可以尝试使用 Spring's AbstractRoutingDataSource,它基于查找键(在您的情况下为国家代码)选择要使用的正确数据源。

通过使用这两种解决方案中的任何一种,您都可以开始使用 springs声明式事务管理方法(这应该会大大清理您的代码)。

于 2013-08-21T07:48:09.637 回答
2

定义一个代理数据源,类为 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"/>
</豆>
于 2017-03-10T13:32:51.173 回答
0

如果不使用 Spring 提供的“事务”抽象级别,我不确定您是否可以做到这一点。

构建您的 transactionTemplate 的更“无 xml”可能是这样的。

private TransactionTemplate getTransactionTemplate(String executionTenantCode, boolean readOnlyTransaction) {
    TransactionTemplate tt = new TransactionTemplate(transactionManager);
    tt.setReadOnly(readOnlyTransaction);
    tt.setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED);
    tt.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
    return tt;
}

在任何情况下,我都会“利用”@Transactional指定适当事务管理器的注释,并与单独的数据源绑定。我已经为多租户应用程序做到了这一点。

用法:

@Transactional(transactionManager = CATALOG_TRANSACTION_MANAGER, 
    isolation = Isolation.READ_UNCOMMITTED, 
    readOnly = true)
public void myMethod() {
  //....
}

bean(s) 声明:

public class CatalogDataSourceConfiguration {
    
    @Bean(name = "catalogDataSource")
    @ConfigurationProperties("catalog.datasource")
    public DataSource catalogDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = ENTITY_MANAGER_FACTORY)
    public EntityManagerFactory entityManagerFactory(
            @Qualifier("catalogEntityManagerFactoryBean") LocalContainerEntityManagerFactoryBean emFactoryBean) {
        return emFactoryBean.getObject();
    }

    @Bean(name= CATALOG_TRANSACTION_MANAGER)
    public PlatformTransactionManager catalogTM(@Qualifier(ENTITY_MANAGER_FACTORY) EntityManagerFactory emf) {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(emf);
        return transactionManager;
    }

    @Bean
    public NamedParameterJdbcTemplate catalogJdbcTemplate() {
        return new NamedParameterJdbcTemplate(catalogDataSource());
    }

}
于 2021-12-07T17:59:01.860 回答