簡體   English   中英

我怎樣才能得到一個 spring JdbcTemplate 到 read_uncommitted?

[英]How can I get a spring JdbcTemplate to read_uncommitted?

首先,我不能使用聲明性@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;
}

好吧,很多樣板,我知道。 所以自從我使用 spring 以來,我已經嘗試了JdbcTemplate

使用 Jdbc 模板

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 需要在運行時根據提供的參數使用哪個數據源,在我的例子中是國家/地區代碼。

任何幫助將不勝感激。

我目前已經通過直接使用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 章 - 事務管理

在這里使用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 的AbstractRoutingDataSource ,它基於查找鍵(在您的情況下為國家/地區代碼)選擇要使用的正確數據源。

通過使用這兩種解決方案中的任何一種,您都可以開始使用 springs 聲明式事務管理方法(這應該可以大大清理您的代碼)。

定義一個代理數據源,類為 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>

我不確定您是否可以在不使用 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());
    }

}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM