如何让 spring JdbcTemplate 读取_未提交?
Posted
技术标签:
【中文标题】如何让 spring JdbcTemplate 读取_未提交?【英文标题】:How can I get a spring JdbcTemplate to read_uncommitted? 【发布时间】:2012-07-25 18:32:24 【问题描述】:首先,我不能使用声明性@Transactional
方法,因为应用程序有多个 JDBC 数据源,我不想对细节感到厌烦,但只要说 DAO 方法传递了正确的数据就足够了-source 来执行逻辑。所有 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 需要在运行时根据提供的参数(在我的例子中是国家代码)确定使用哪个数据源。
任何帮助将不胜感激。
【问题讨论】:
【参考方案1】:如果不使用 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 声明:
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());
【讨论】:
【参考方案2】:在这里使用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
,它基于查找键(在您的情况下为国家代码)选择要使用的正确数据源。
通过使用这两种解决方案中的任何一种,您都可以开始使用 springs declarative transactionmanagement 方法(这应该会大大清理您的代码)。
【讨论】:
这是否意味着我需要为每个数据库和每个隔离配置多个 txTemplate?因此,如果我想要 6 个数据库的可写隔离和只读脏隔离,我需要 12 个 txTemplates? 要么,要么在需要时自己构建它们,这将要求您传入事务管理器并相应地设置配置。我想最好的解决方案是使用 AbstractRoutingDataSource 这样你就可以利用 springs 声明式事务管理(并在运行中清理你的代码)。【参考方案3】:定义一个代理数据源,类为org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy,并设置事务隔离级别。通过 setter 或构造函数注入实际的数据源。
【讨论】:
【参考方案4】:我目前已经通过直接使用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
【讨论】:
这是一个古老的问题和答案,所以我只是想是否有解决您描述的问题的新方法。我认为,新版本事务注释中的新事务管理器属性解决了问题Spring 3.1 Transactional 那么提交和回滚是否会用于这个只读查询?它没有进行插入或更新,只是想澄清一下,因为我不知道 为什么读取未提交查询需要回滚?以上是关于如何让 spring JdbcTemplate 读取_未提交?的主要内容,如果未能解决你的问题,请参考以下文章