為了提高網站將來向外擴展的能力,我是用了數據分割的架構,這就要求我的網站能夠靈活的訪問多個數據庫,如果是直接操作JDBC,這根本就不成問題。但是在SpringSide2.0中,由于集成了Hibernate,所以對數據的訪問大部分時候都是通過配置實現的。
要想在SpringSide2.0中使用多個數據庫,最簡單的辦法就是配置多個sessionFactory,下面是我的探索步驟。
首先,要修改src/resources/config目錄下的jdbc.properties文件,定義多個數據庫的連接,如下:
然后,修改src/resources/spring目錄下的dataAccessContext-hibernate.xml文件,定義多個數據源,它們之間只有url屬性不同:
然后,還是修改這個文件,定義多個sessionFactory,它們之間只有數據源的屬性不同:
再然后,還是修改這個文件,定義多個不同的事務管理器,它們之間只有sessionFactory屬性不同,如下:
以上的這些修改是最基本的,就是把sessionFactory和事務管理器都定義為多份的,以后,在要使用到這些東西的時候,就不能使用Spring提供的自動按照name匹配的方便了,必須得自己指定。
利用SpringSide2.0,只需要編寫表示Model的POJO,就可以很容易得到數據訪問層的代碼,比如,我的網站有一個表示文章分類的數據表,其對應的Model如下:
那么,其DAO層的代碼如下:
相當的簡潔,該CatalogManager類提供了非常完整的find、get、save等功能。下一步,將這個類是用Spring管理起來的時候,就必須自己指定sessionFactory了,因此,在src/resources/spring/目錄下的serviceContext.xml文件中,配置的形式如下:
至于事務管理器,是在配置事務的時候用到的,因此,修改src/resources/spring目錄下的applicationContext.xml文件,如下:
OK,快速啟動項目,但是還是沒有成功,報出的錯誤提示說沒有定義name為transactionManager的bean。經過我這么一改,當然沒有定義這個bean了,但是我也沒有用它???我用的是transactionManagerIndex,經過多次排查,我仍然沒有找出問題的癥結所在。
此外,在web.xml文件中配置的org.springframework.orm.hibernate3.support.OpenSessionInViewFilter也起不到作用了,因為我不知道這個類能不能管理多個SessionFactory,似乎是不行的。
由此可見,使用默認值的地方太多了,也是會害死人的。我已經向SpringSide的老大江南白衣請教這個問題了,暫時還沒有得到回復。
為了使我的項目能夠順利繼續,我只有采取另外的變通方法,那就是把YumDaysIndex和YumDaysContent作為兩個不同的項目分別開發。但是這樣并不能夠一勞永逸,將來當數據庫的負載達到極限,需要讓主從數據庫服務器分開,主服務器只提供寫操作,從服務器只提供讀操作,我還得把寫數據庫和讀數據庫的代碼分到不同的項目中進行開發。
要想在SpringSide2.0中使用多個數據庫,最簡單的辦法就是配置多個sessionFactory,下面是我的探索步驟。
首先,要修改src/resources/config目錄下的jdbc.properties文件,定義多個數據庫的連接,如下:
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.urlYumDaysIndex=jdbc:mysql://localhost:3306/YumDaysIndex?useUnicode=true&characterEncoding=utf8
jdbc.urlYumDays001=jdbc:mysql://localhost:3306/YumDays001?useUnicode=true&characterEncoding=utf8
jdbc.urlYumDaysIndex=jdbc:mysql://localhost:3306/YumDaysIndex?useUnicode=true&characterEncoding=utf8
jdbc.urlYumDays001=jdbc:mysql://localhost:3306/YumDays001?useUnicode=true&characterEncoding=utf8
然后,修改src/resources/spring目錄下的dataAccessContext-hibernate.xml文件,定義多個數據源,它們之間只有url屬性不同:
????<!--?數據源定義,使用Apache?DBCP?連接池?-->
????<bean?id="dataSourceYumDaysIndex"?class="org.apache.commons.dbcp.BasicDataSource"?destroy-method="close">
????????<property?name="driverClassName"?value="${jdbc.driverClassName}"/>
????????<property?name="url"?value="${jdbc.urlYumDaysIndex}"/>
????????<property?name="username"?value="${jdbc.username}"/>
????????<property?name="password"?value="${jdbc.password}"/>
????</bean>
????
????<!--?數據源定義,使用Apache?DBCP?連接池?-->
????<bean?id="dataSourceYumDays001"?class="org.apache.commons.dbcp.BasicDataSource"?destroy-method="close">
????????<property?name="driverClassName"?value="${jdbc.driverClassName}"/>
????????<property?name="url"?value="${jdbc.urlYumDays001}"/>
????????<property?name="username"?value="${jdbc.username}"/>
????????<property?name="password"?value="${jdbc.password}"/>
????</bean>
????<bean?id="dataSourceYumDaysIndex"?class="org.apache.commons.dbcp.BasicDataSource"?destroy-method="close">
????????<property?name="driverClassName"?value="${jdbc.driverClassName}"/>
????????<property?name="url"?value="${jdbc.urlYumDaysIndex}"/>
????????<property?name="username"?value="${jdbc.username}"/>
????????<property?name="password"?value="${jdbc.password}"/>
????</bean>
????
????<!--?數據源定義,使用Apache?DBCP?連接池?-->
????<bean?id="dataSourceYumDays001"?class="org.apache.commons.dbcp.BasicDataSource"?destroy-method="close">
????????<property?name="driverClassName"?value="${jdbc.driverClassName}"/>
????????<property?name="url"?value="${jdbc.urlYumDays001}"/>
????????<property?name="username"?value="${jdbc.username}"/>
????????<property?name="password"?value="${jdbc.password}"/>
????</bean>
然后,還是修改這個文件,定義多個sessionFactory,它們之間只有數據源的屬性不同:
????<!--Hibernate?SessionFatory-->
????<bean?id="sessionFactoryIndex"?class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
????????<property?name="dataSource"?ref="dataSourceYumDaysIndex"/>
????????<property?name="annotatedClasses">
????????????<list>
????????????????<value>com.yumdays.model.Catalog</value>
????????????????<value>com.yumdays.model.Statistic</value>
????????????</list>
????????</property>
????????<property?name="hibernateProperties">
????????????<props>
??????????????<!--?see?/components/orm/hibernate/meta/dialect.properties?for?all?Dialect?-->
????????????????<prop?key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
????????????????<prop?key="hibernate.show_sql">true</prop>
????????????????<prop?key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>
????????????????<prop?key="hibernate.cache.use_query_cache">true</prop>
????????????</props>
????????</property>
????</bean>
????
????<!--Hibernate?SessionFatory-->
????<bean?id="sessionFactory001"?class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
????????<property?name="dataSource"?ref="dataSourceYumDays001"/>
????????<property?name="annotatedClasses">
????????????<list>
????????????</list>
????????</property>
????????<property?name="hibernateProperties">
????????????<props>
??????????????<!--?see?/components/orm/hibernate/meta/dialect.properties?for?all?Dialect?-->
????????????????<prop?key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
????????????????<prop?key="hibernate.show_sql">true</prop>
????????????????<prop?key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>
????????????????<prop?key="hibernate.cache.use_query_cache">true</prop>
????????????</props>
????????</property>
????</bean>
????<bean?id="sessionFactoryIndex"?class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
????????<property?name="dataSource"?ref="dataSourceYumDaysIndex"/>
????????<property?name="annotatedClasses">
????????????<list>
????????????????<value>com.yumdays.model.Catalog</value>
????????????????<value>com.yumdays.model.Statistic</value>
????????????</list>
????????</property>
????????<property?name="hibernateProperties">
????????????<props>
??????????????<!--?see?/components/orm/hibernate/meta/dialect.properties?for?all?Dialect?-->
????????????????<prop?key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
????????????????<prop?key="hibernate.show_sql">true</prop>
????????????????<prop?key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>
????????????????<prop?key="hibernate.cache.use_query_cache">true</prop>
????????????</props>
????????</property>
????</bean>
????
????<!--Hibernate?SessionFatory-->
????<bean?id="sessionFactory001"?class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
????????<property?name="dataSource"?ref="dataSourceYumDays001"/>
????????<property?name="annotatedClasses">
????????????<list>
????????????</list>
????????</property>
????????<property?name="hibernateProperties">
????????????<props>
??????????????<!--?see?/components/orm/hibernate/meta/dialect.properties?for?all?Dialect?-->
????????????????<prop?key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
????????????????<prop?key="hibernate.show_sql">true</prop>
????????????????<prop?key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>
????????????????<prop?key="hibernate.cache.use_query_cache">true</prop>
????????????</props>
????????</property>
????</bean>
再然后,還是修改這個文件,定義多個不同的事務管理器,它們之間只有sessionFactory屬性不同,如下:
????<!--Hibernate?TransactionManager-->
????<bean?id="transactionManagerIndex"?class="org.springframework.orm.hibernate3.HibernateTransactionManager">
????????<property?name="sessionFactory"?ref="sessionFactoryIndex"/>
????</bean>
????<bean?id="transactionManager001"?class="org.springframework.orm.hibernate3.HibernateTransactionManager">
????????<property?name="sessionFactory"?ref="sessionFactory001"/>
????</bean>
????<bean?id="transactionManagerIndex"?class="org.springframework.orm.hibernate3.HibernateTransactionManager">
????????<property?name="sessionFactory"?ref="sessionFactoryIndex"/>
????</bean>
????<bean?id="transactionManager001"?class="org.springframework.orm.hibernate3.HibernateTransactionManager">
????????<property?name="sessionFactory"?ref="sessionFactory001"/>
????</bean>
以上的這些修改是最基本的,就是把sessionFactory和事務管理器都定義為多份的,以后,在要使用到這些東西的時候,就不能使用Spring提供的自動按照name匹配的方便了,必須得自己指定。
利用SpringSide2.0,只需要編寫表示Model的POJO,就可以很容易得到數據訪問層的代碼,比如,我的網站有一個表示文章分類的數據表,其對應的Model如下:
package?com.yumdays.model;
import?java.io.Serializable;
import?javax.persistence.Entity;
import?javax.persistence.Id;
import?javax.persistence.Table;
import?org.hibernate.annotations.GenericGenerator;
@Entity
@Table(name="catalogs")
public?class?Catalog?implements?Serializable?{
????private?String?id;
????private?String?name;
????
????@Id
????@GenericGenerator(name="system-uuid",strategy="uuid")
????public?String?getId()?{
????????return?id;
????}
????public?void?setId(String?id)?{
????????this.id?=?id;
????}
????public?String?getName()?{
????????return?name;
????}
????public?void?setName(String?name)?{
????????this.name?=?name;
????}
}
import?java.io.Serializable;
import?javax.persistence.Entity;
import?javax.persistence.Id;
import?javax.persistence.Table;
import?org.hibernate.annotations.GenericGenerator;
@Entity
@Table(name="catalogs")
public?class?Catalog?implements?Serializable?{
????private?String?id;
????private?String?name;
????
????@Id
????@GenericGenerator(name="system-uuid",strategy="uuid")
????public?String?getId()?{
????????return?id;
????}
????public?void?setId(String?id)?{
????????this.id?=?id;
????}
????public?String?getName()?{
????????return?name;
????}
????public?void?setName(String?name)?{
????????this.name?=?name;
????}
}
那么,其DAO層的代碼如下:
package?com.yumdays.service;
import?org.springside.core.dao.HibernateEntityDao;
import?com.yumdays.model.Catalog;
public?class?CatalogManager?extends?HibernateEntityDao<Catalog>?{
}
import?org.springside.core.dao.HibernateEntityDao;
import?com.yumdays.model.Catalog;
public?class?CatalogManager?extends?HibernateEntityDao<Catalog>?{
}
相當的簡潔,該CatalogManager類提供了非常完整的find、get、save等功能。下一步,將這個類是用Spring管理起來的時候,就必須自己指定sessionFactory了,因此,在src/resources/spring/目錄下的serviceContext.xml文件中,配置的形式如下:
<?xml?version="1.0"?encoding="UTF-8"?>
<!DOCTYPE?beans?PUBLIC?"-//SPRING//DTD?BEAN?2.0//EN"?"http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans?default-lazy-init="true"?default-autowire="byName">
???????????<bean?id="catalogManager"?class="com.yumdays.service.CatalogManager">
???????????????????<property?name="sessionFactory"?ref="sessionFactoryIndex"/>
???????????</bean>
???????????<bean?id="statisticManager"?class="com.yumdays.service.StatisticManager">
???????????????????<property?name="sessionFactory"?ref="sessionFactoryIndex"/>
???????????</bean>
</beans>
<!DOCTYPE?beans?PUBLIC?"-//SPRING//DTD?BEAN?2.0//EN"?"http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans?default-lazy-init="true"?default-autowire="byName">
???????????<bean?id="catalogManager"?class="com.yumdays.service.CatalogManager">
???????????????????<property?name="sessionFactory"?ref="sessionFactoryIndex"/>
???????????</bean>
???????????<bean?id="statisticManager"?class="com.yumdays.service.StatisticManager">
???????????????????<property?name="sessionFactory"?ref="sessionFactoryIndex"/>
???????????</bean>
</beans>
至于事務管理器,是在配置事務的時候用到的,因此,修改src/resources/spring目錄下的applicationContext.xml文件,如下:
????<!--?以AspectJ方式?定義?AOP?-->
????<aop:config?proxy-target-class="true">
????????<!--?注意,請把第2個*號換為項目package?-->
????????<aop:advisor?pointcut="execution(*?*..service.CatalogManager.*(..))"?advice-ref="txAdviceIndex"/>
????????<aop:advisor?pointcut="execution(*?*..service.StatisticManager.*(..))"?advice-ref="txAdviceIndex"/>
????</aop:config>
????<!--?基本事務定義,使用transactionManager作事務管理,默認get*方法的事務為readonly,其余方法按默認設置.
?????????????默認的設置請參考Spring文檔事務一章.?-->
????<tx:advice?id="txAdviceIndex"?transaction-manager="transactionManagerIndex">
????????<tx:attributes>
????????????<tx:method?name="get*"?read-only="true"/>
????????????<tx:method?name="find*"?read-only="true"/>
????????????<tx:method?name="*"/>
????????</tx:attributes>
????</tx:advice>
????<aop:config?proxy-target-class="true">
????????<!--?注意,請把第2個*號換為項目package?-->
????????<aop:advisor?pointcut="execution(*?*..service.CatalogManager.*(..))"?advice-ref="txAdviceIndex"/>
????????<aop:advisor?pointcut="execution(*?*..service.StatisticManager.*(..))"?advice-ref="txAdviceIndex"/>
????</aop:config>
????<!--?基本事務定義,使用transactionManager作事務管理,默認get*方法的事務為readonly,其余方法按默認設置.
?????????????默認的設置請參考Spring文檔事務一章.?-->
????<tx:advice?id="txAdviceIndex"?transaction-manager="transactionManagerIndex">
????????<tx:attributes>
????????????<tx:method?name="get*"?read-only="true"/>
????????????<tx:method?name="find*"?read-only="true"/>
????????????<tx:method?name="*"/>
????????</tx:attributes>
????</tx:advice>
OK,快速啟動項目,但是還是沒有成功,報出的錯誤提示說沒有定義name為transactionManager的bean。經過我這么一改,當然沒有定義這個bean了,但是我也沒有用它???我用的是transactionManagerIndex,經過多次排查,我仍然沒有找出問題的癥結所在。
此外,在web.xml文件中配置的org.springframework.orm.hibernate3.support.OpenSessionInViewFilter也起不到作用了,因為我不知道這個類能不能管理多個SessionFactory,似乎是不行的。
由此可見,使用默認值的地方太多了,也是會害死人的。我已經向SpringSide的老大江南白衣請教這個問題了,暫時還沒有得到回復。
為了使我的項目能夠順利繼續,我只有采取另外的變通方法,那就是把YumDaysIndex和YumDaysContent作為兩個不同的項目分別開發。但是這樣并不能夠一勞永逸,將來當數據庫的負載達到極限,需要讓主從數據庫服務器分開,主服務器只提供寫操作,從服務器只提供讀操作,我還得把寫數據庫和讀數據庫的代碼分到不同的項目中進行開發。