2012-10-30

在 Spring 3.1.2.RELEASE 使用多個資料庫的宣告式交易管理(Multiple Databases with Annotation-based Declarative Transaction Management)

一般的系統只會用到一個資料庫,Spring 引進宣告式交易管理後,設定變得很簡單。

Spring 設定檔。
<context:component-scan base-package="idv.neil.model,idv.neil.service,idv.neil.dao" />

<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
  <property name="configLocation" value="classpath:hibernate.cfg.xml" />
</bean>

<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
  <property name="sessionFactory" ref="sessionFactory" />
</bean>

<tx:annotation-driven />
先用 context:component-scan 告訴 Spring 所有的 Annotation 在哪些 package 裡。

然後建立 Hibernate SessionFactory,這是給 Dao 用的。

再建立 TransactionManager,這是給 Service 用的。

最後用 tx:annotation-driven 將全部串起來。


Dao 裡怎麼用?
@Repository("newDao")
@SuppressWarnings("serial")
public class NewDaoImpl implements NewDao, Serializable {

  private Logger log = LoggerFactory.getLogger(this.getClass());
  private SessionFactory sessionFactory;

  @Inject
  public NewDaoImpl(SessionFactory sessionFactory) {
    this.sessionFactory = sessionFactory;
  }

  private Session getSession() {
    return this.sessionFactory.getCurrentSession();
  }

  // ...
}
@Repository 是給 context:component-scan 用的。

@Inject 則是將 SessionFactory 塞進來,Dao 就活起來了。


Service 裡怎麼做?
@Service("newService")
@SuppressWarnings("serial")
public class NewServiceImpl implements NewService, Serializable {

  private Logger log = LoggerFactory.getLogger(this.getClass());

  private NewDao newDao;

  @Inject
  public NewServiceImpl(NewDao newDao) {
    this.newDao = newDao;
  }

  @Override
  @Transactional
  public List<Entity> list(QueryVO queryVO) {
    return this.newDao.list(queryVO);
  }

}
@Service 是給 context:component-scan 用的。

@Inject 則是將 Dao 塞進來。

最後在每個需要交易或者說是 Hibernate Session 的 method 加上 @Transactional。

這樣就是一個使用單一資料庫的系統。


哪怎麼加入第二個資料庫呢?

將以上所有的設定複製一份?不夠!還要加上 qualifier 才行。

Spring 設定檔。
<bean id="oldSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
  <property name="configLocation" value="classpath:hibernate.old.cfg.xml" />
  <qualifier value="old" />
</bean>

<bean id="oldTransactionManager" name="oldTransactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
  <property name="sessionFactory" ref="oldSessionFactory" />
  <qualifier value="old" />
</bean>

<tx:annotation-driven transaction-manager="oldTransactionManager" />
複製一份 SessionFactory,指向不同的 hibernate.cfg.xml 檔,還要加上 qualifier,供稍後 Dao 指名使用。

複製一份 TransactionManager,指向剛的 SessionFactory,一樣要加上 qualifier,也是供稍後的 Service 指名使用。

複製一份 tx:annotaion-driven,將全部串起來,但這次要指明使用哪個 transaction-manager。


再看一次 Dao 怎麼用?
@Repository("oldDao")
@SuppressWarnings("serial")
public class OldDaoImpl implements OldDao, Serializable {

  private Logger log = LoggerFactory.getLogger(this.getClass());
  private SessionFactory sessionFactory;

  @Inject
  public OldDaoImpl(@Qualifier("old") SessionFactory sessionFactory) {
    this.sessionFactory = sessionFactory;
  }

  private Session getSession() {
    return this.sessionFactory.getCurrentSession();
  }

  // ...
}
唯一的差別就是在 @Inject method 的傳入參數裡加上 @Qualifier,並指明 old。


Service 也是一樣的方式。
@Service("oldService")
@SuppressWarnings("serial")
public class OldServiceImpl implements OldService, Serializable {

  private Logger log = LoggerFactory.getLogger(this.getClass());

  private OldDao oldDao;

  @Inject
  public OldServiceImpl(OldDao oldDao) {
    this.oldDao = oldDao;
  }

  @Override
  @Transactional("old")
  public List<Entity> list(QueryVO queryVO) {
    return this.oldDao.list(queryVO);
  }

}
只是在 @Transactional 裡加上參數,也就是 old。

噹,收工!
---
---
---

沒有留言:

張貼留言