#
這幾天抽空把How tomcat works看了一遍。
這本書寫得很好,把tomcat這么一個牛B的大家伙拆成一堆零件,然后告訴你怎么組裝,真是做到了掰開了揉碎了講。
簡單記一下
第一章講web服務(wù)器,如何接受和響應(yīng)http請求,還舉了個sokcet的例子。算是入門,從很底層的技術(shù)講起。
第二章講servlet容器,javax.servlet.Servle接口,接受到http請求后如何查找servlet,執(zhí)行servlet。
第三章講連接器的概念,前兩章看下來你會覺得把http請求接受響應(yīng)跟容器放在一起太亂了,這章就講如何把http操作提出來作為一個連接器。
第四章講tomcat默認(rèn)連接器,http協(xié)議操作講得很詳細(xì),不過我沒怎么看哈,用的時候直接把tomcat這段代碼拿過來就是了。
第五章講容器,在第三章的基礎(chǔ)上對容器進(jìn)行分層分類,事情復(fù)雜了就分成幾部分,“治眾如治寡,分?jǐn)?shù)是也”這個我們都知道。
tomcat講容器分成這幾個概念:
Engine:表示整個Catalina的servlet引擎
Host:表示一個擁有數(shù)個上下文的虛擬主機(jī)
Context:表示一個Web應(yīng)用,一個context包含一個或多個wrapper
Wrapper:表示一個獨(dú)立的servlet
類型復(fù)雜了,要做的事情也復(fù)雜了。
不僅僅是執(zhí)行service()方法,還要前邊執(zhí)行一堆,后邊再來一堆。引入了流水線任務(wù)Pipelining Tasks的概念,在流水線上可以執(zhí)行多個Valve(有翻譯成閥),類似于攔截器的概念。
第六章講生命周期,人多了要講究個步調(diào)統(tǒng)一,引入了Lifecycle接口的概念,方法包括啟動之前干什么、啟動之后干什么、啟動后把子容器也啟動了。
包括引入監(jiān)聽接口,都是些java常見實(shí)現(xiàn)方式,沒什么特殊。
第七章講日志系統(tǒng),沒看。
第八章講加載器,可以參考
tomcat類加載器及jar包沖突問題分析 http://m.tkk7.com/zyskm/archive/2011/12/06/365653.html 就不重復(fù)了。
第九章講session管理,沒什么特別的。
第十章講安全,沒看。
第十一章講StandardWrapper,在第五章的基礎(chǔ)上重點(diǎn)分析了wrapper的運(yùn)作機(jī)制。
其余章節(jié)目前工作中用不到,有空再看了。
作者:zyskm
http://m.tkk7.com/zyskm
上一篇說明了一種spring事務(wù)配置方式,這次補(bǔ)上另一種。
見配置文件:
<!-- 事務(wù)攔截 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="get*" propagation="REQUIRED" read-only="true" />
<tx:method name="find*" propagation="REQUIRED" read-only="true" />
<tx:method name="search*" propagation="REQUIRED" read-only="true" />
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="modify*" propagation="REQUIRED" />
<tx:method name="send*" propagation="REQUIRED" />
<tx:method name="revoke*" propagation="REQUIRED" />
<tx:method name="del*" propagation="REQUIRED" />
<tx:method name="logging*" propagation="NOT_SUPPORTED" read-only="true" />
<tx:method name="*" propagation="SUPPORTS" read-only="true" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="projectServiceOperation" expression="execution(* cn.ceopen.bss..*.service..*(..))" />
<aop:advisor pointcut-ref="projectServiceOperation" advice-ref="txAdvice" />
</aop:config>重點(diǎn)說明兩點(diǎn):
1.<aop:pointcut id="projectServiceOperation" expression="execution(* cn.ceopen.bss..*.service..*(..))" />
表示你要進(jìn)行事務(wù)控制的類名。詳細(xì)資料可以查下 aspectj語法。
配置完成一定要實(shí)際測試一下,我配置過 expression="execution(* cn.ceopen.bss..*.service.*(..))" 少了一個點(diǎn),導(dǎo)致事務(wù)不起作用。
導(dǎo)致項(xiàng)目很長一段時間事務(wù)方面沒經(jīng)過嚴(yán)格測試。
2.
Spring的AOP事務(wù)管理默認(rèn)是針對unchecked exception回滾。
也就是默認(rèn)對RuntimeException()異常極其子類進(jìn)行事務(wù)回滾。
在項(xiàng)目中定義公共的RuntimeException異常,避免每個開發(fā)人員隨意拋出異常。
不然的話沒新定義一個異常,就要修改tx:method rollback-for 太麻煩了。
總結(jié):
1.對事務(wù)配置進(jìn)行檢查,對復(fù)雜嵌套的事務(wù)邏輯必要的時候debug到spring源碼中確認(rèn)。
2.定義統(tǒng)一異常類型
3.同一個類調(diào)用自身方法,子方法的事務(wù)配置不起作用。解決方法見上一篇文章。
http://m.tkk7.com/zyskm/archive/2011/11/11/363535.html作者: zyskm
本文地址:
http://m.tkk7.com/zyskm/archive/2011/11/30/365225.html
在開發(fā)中緩存是經(jīng)常要用到的,什么東西都一遍遍的找數(shù)據(jù)庫問,數(shù)據(jù)庫表示壓力很大。
緩存方案常見的有兩種,一種是在客戶端,也就是web開發(fā)中的瀏覽器。一種是在服務(wù)器端,以memcached為代表。
瀏覽器緩存
這幾天因?yàn)轫?xiàng)目需了解了下瀏覽器緩存相關(guān)的知識。
重點(diǎn)說一下HTML5 Storage技術(shù),之前的那些技術(shù)都是浮云,存不了什么東西還賊難用,各瀏覽器不兼容。
聽名字就知道這是這兩年剛出來的技術(shù),相應(yīng)的也就對瀏覽器版本要求比較高,低版本的瀏覽器不支持。對用公司內(nèi)部用的話這個技術(shù)還是很不錯的。
最多支持5M的容量,是個xml文件,備注:
Html5 storage最大支持5M容量
文件位置,根據(jù)系統(tǒng)用戶位置會有區(qū)別。
C:\Documents and Settings\Administrator\Local Settings\Application Data\Microsoft\Internet Explorer\DOMStore\KF2G0R0O
也就是說別人都能看到你的數(shù)據(jù),所以不能存放需要保密的內(nèi)容。
每次用戶登錄的時候最好檢查一下緩存版本是否跟服務(wù)器一致。
現(xiàn)在的項(xiàng)目中有個業(yè)務(wù)受理環(huán)節(jié),設(shè)置的過程中需要大量使用一些基礎(chǔ)數(shù)據(jù)。類似分公司、部門、產(chǎn)品列表這類數(shù)據(jù),不經(jīng)常變動,反復(fù)到服務(wù)器請求的話對服務(wù)器來講是不小的壓力。
業(yè)務(wù)受理的步驟很多,得點(diǎn)好幾個下一步才能把操作完成,半截?cái)?shù)據(jù)放到數(shù)據(jù)庫里維護(hù)起來結(jié)構(gòu)就復(fù)雜了。
因此考慮采用本地緩存技術(shù),比較來比較去HTML5 Storage是不錯的選擇,反正都是公司同事可以要求都使用IE8以上瀏覽器。
下邊這段是js中用到的代碼
//用jquery方式,為加載緩存到本地按鈕綁定方法

$('#loadBtn').bind('click',function(event)
{

if(supports_html5_storage())
{
localStorage.clear();
load();
alert("數(shù)據(jù)已經(jīng)緩存到本地!");
initCorpSelect();
initDptSelect("-1");

}else
{
alert("You're out!");
}
}) ;
//清空緩存

$('#clearBtn').bind('click',function(event)
{

if(supports_html5_storage())
{
localStorage.clear();
alert("本地緩存已經(jīng)清空!");
initCorpSelect();
initDptSelect("-1");

}else
{
alert("You're out!");
}
}) ;
//加載緩存到本地瀏覽器

function load()
{

var params =
{
url:"${ctx}/crm/product/product/webstorage!ajaxLoad.do",
type: "post",
dataType: "json",
data: null,

success: function(response)
{
var corps = response.corps;
var dpts = response.dpts;
var stations = response.stations;


for(var i=0;i<corps.length;i++)
{
var corp = corps[i];
var jsCorp = new Corp(corp.corpCode,corp.corpName);
localStorage.setItem("corp."+corp.corpCode, jsCorp.toJSONString());
}


for(var i=0;i<dpts.length;i++)
{
var dpt = dpts[i];
localStorage.setItem("dpt."+dpt.dptCode, dpt.toJSONString());
}


for(var i=0;i<stations.length;i++)
{
var station = stations[i];
localStorage.setItem("station."+station.stationCode, station.toJSONString());
}
}
};
ajax(params);
}
服務(wù)器緩存
緩存解決方案很多了,只說剛剛用到的memcached。
memcached很好用了
1.安裝啟動memcached
2.寫一個memcached客戶端工具類,我用的是com.danga.MemCached.MemCachedClient 比較穩(wěn)定,至于其他性能更高的目前沒有需求沒用到。
3.加載數(shù)據(jù)到memcached
4.程序中需要基礎(chǔ)數(shù)據(jù)的時候從memcached獲取。

/** *//**
* memcached接口
* @author ce
*
*/

public interface Imemcached
{

/** *//**
* 可增加,可覆蓋
*/
public abstract boolean set(Object key, Object value);
public abstract boolean set(Object key, Object value,Date expire);

/** *//**
* 只能增加;如果增加的key存在返回“false”
*/
public abstract boolean add(Object key, Object value);
public abstract boolean add(Object key, Object value,Date expire);

public abstract boolean delete(Object key);


/** *//**
* 只能修改;如果修改不存在的key返回“false”
*/
public abstract boolean modify(Object key, Object value);


/** *//**
* 如果key不存在返回null
*/
public abstract Object getKey(Object key);


/** *//**
* 清除服務(wù)器上所有數(shù)據(jù)
*/
public abstract boolean flushAll();


/** *//**
* 判斷服務(wù)器上是否存在key
*/
public abstract boolean keyExists(Object key);


/** *//**
* 一次獲取多個數(shù)據(jù),減少網(wǎng)絡(luò)傳輸時間
*/
public abstract Map<String, Object> getKeys(String[] keys);

/** *//**
* 設(shè)置是否使用字符串方式
*/
public abstract void setPrimitiveAsString(boolean flag);
}
實(shí)際測試結(jié)果
查詢一張人員表,同時需要根據(jù)分公司編號,部門編號從分公司、部門表獲取名稱。
使用緩存技術(shù)的時候,不管客戶端還是服務(wù)器端,因?yàn)橹徊樵儐伪恚俣让黠@比多表聯(lián)合查詢要快的多。
在系統(tǒng)使用高峰的時候這種差別明顯能改善用戶的操作體驗(yàn)。
實(shí)際測試了以下幾種情況:
1.查詢:
直接通過表關(guān)聯(lián)的方式進(jìn)行查詢,速度最慢,開發(fā)最簡便
查詢結(jié)果列表中的公司名稱、部門名稱直接從數(shù)據(jù)庫查出。
2.查詢(本地緩存),緩存數(shù)據(jù)到本地,清除本地緩存:
速度最快,數(shù)據(jù)同步有一定工作量,需要高版本瀏覽器支持
查詢結(jié)果列表中的公司名稱、部門名稱從本地緩存獲取,增加_IE標(biāo)識以區(qū)別。
3.查詢(服務(wù)器緩存),緩存數(shù)據(jù)到服務(wù)器,清除服務(wù)器緩存:
速度也很快,也有數(shù)據(jù)同步問題,緩存數(shù)據(jù)到服務(wù)器比本地慢
查詢結(jié)果列表中的公司名稱、部門名稱從服務(wù)器緩存獲取,增加_mem標(biāo)識以區(qū)別。

緩存使用總結(jié)
1.采用緩存技術(shù)后系統(tǒng)性能有明顯改善,可以從性能測試工具明顯感受到(這不是廢話嘛,呵呵)。
2.緩存的數(shù)據(jù)同步問題必須解決好
3.項(xiàng)目中采用緩存技術(shù)需要統(tǒng)一規(guī)劃,各系統(tǒng)不能互相干擾。
4.性能壓力不明顯的情況下,緩存開發(fā)要顯得復(fù)雜一點(diǎn)。
作者: zyskm
本文地址:
http://m.tkk7.com/zyskm/archive/2011/11/30/364722.html
聲明性事務(wù)是spring一個很重要的功能,可以避免開發(fā)陷入繁瑣的事務(wù)控制邏輯中。
但是可能是用著太方便了很多人對spring事務(wù)原理并不清楚,有必要做一番分析。
下邊以攔截器配置方式進(jìn)行說明,tx標(biāo)簽配置方式將在接下來另一篇文章做分析。
一、首先看配置文件:
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="matchAllTxInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
<property name="transactionAttributes">
<props>
<prop key="get*">PROPAGATION_REQUIRED,readOnly,-Exception </prop>
<prop key="find*">PROPAGATION_REQUIRED,readOnly,-Exception </prop>
<prop key="search*">PROPAGATION_REQUIRED,readOnly,-Exception </prop>
<prop key="save*">PROPAGATION_REQUIRED,-Exception </prop>
<prop key="modify*">PROPAGATION_REQUIRED,-Exception </prop>
<prop key="send*">PROPAGATION_REQUIRED,-Exception </prop>
<prop key="revoke*">PROPAGATION_REQUIRED,-Exception </prop>
<prop key="del*">PROPAGATION_REQUIRED,-Exception </prop>
<prop key="logging*">PROPAGATION_NOT_SUPPORTED,readOnly,-Exception </prop>
<prop key="*">PROPAGATION_SUPPORTS,-Exception </prop>
</props>
</property>
</bean>
<bean id="autoProxyCreator"
class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="interceptorNames">
<list><idref local="matchAllTxInterceptor" /></list>
</property>
<property name="proxyTargetClass"><value>true</value></property>
<property name="beanNames">
<list><value>*Service</value></list>
</property>
</bean>配置第一步引入AOP代理autoProxyCreator,使用的是spring默認(rèn)的jdk動態(tài)代理BeanNameAutoProxyCreator。
有兩個屬性要介紹一下:
1.攔截范圍beanNames;例子中攔截范圍是*Service,表示IOC容器中以Service結(jié)尾的bean,一般配置在spring.xml,serviceContext.xml之類的spring配置文件。
要注意這里不是值src下邊的類。
bean配置信息:
<bean id="menuService" class="cn.ceopen.bss
..service.impl.MenuServiceImpl"/>有圖有真相,下邊是BeanNameAutoProxyCreator 調(diào)試信息。


2.截器interceptorNames
interceptorNames定義事務(wù)屬性和事務(wù)管理器
配置第二步就是定義事務(wù)屬性:事務(wù)傳播范圍、事務(wù)隔離級別
事務(wù)屬性沒什么好說的,使用spring進(jìn)行事務(wù)管理的都了解,不在這里詳細(xì)說了網(wǎng)上有大量資料。
配置第三步,指定事務(wù)管理器
這里用的是HibernateTransactionManager,spring提供對常見orm的事務(wù)支持。從spring源碼可以看出HibernateTransactionManager.doGetTransaction()同時支持hibernate和jdbc。
支持hibernate和jdbc混合事務(wù),不使用jta方式的話有個前提條件:使用同一個數(shù)據(jù)源,
這里所說的同一個數(shù)據(jù)源,不僅僅指物理上是同一個,在spring配置文件中也要是同一個。
我在開發(fā)中遇到過這個問題,最早定義了一個數(shù)據(jù)baseDataSource,hibernate和jdbc都使用此數(shù)據(jù)源,后來項(xiàng)目要求使用動態(tài)數(shù)據(jù)源就又配了一個數(shù)據(jù)源dynamicDataSource
僅在hibernate下做了改動,未改動jdbc對應(yīng)配置,出現(xiàn)了事務(wù)控制問題。
出錯了事務(wù)配置:
<bean id="sessionFactory"
class="com.sitechasia.webx.dao.hibernate3.NamedMoudleHbmLocalSessionFactoryBean">
<property name="dataSource" ref="dynamicDataSource" />
<!--與主題無關(guān),省略部分內(nèi)容-->
</bean>
<bean id="dynamicDataSource" class="cn.ceopen.bss.pub.base.dao.RoutingDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="baseDataSource" value-ref="baseDataSource"/>
</map>
</property>
<property name="defaultTargetDataSource" ref="baseDataSource"/>
</bean>
<bean id="baseDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<!--與主題無關(guān),省略部分內(nèi)容-->
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--dataSource應(yīng)該與sessionFactor一致-->
<property name="dataSource"><ref bean="baseDataSource"/></property>
</bean>
<bean id="abstractJdbcDao" abstract="true">
<property name="jdbc" ref="jdbcTemplate" />
</bean>dao配置文件:
<bean id="actDao" class="cn.ceopen.bss
.impl.ActDaoImpl" parent="abstractJdbcDao"/>dao中同時支持hibernate操作和jdbc操作。
二、事務(wù)屬性傳播
先看這樣一個列子:

1.基于jdk動態(tài)代理的AOP事務(wù)控制,只能針對接口。
在上邊的配置文件中設(shè)置的事務(wù)屬性對a3()都不起作用,a3()不能單獨(dú)設(shè)計(jì)事務(wù)屬性,只能繼承接口方法的事務(wù)屬性。
2.類自身事務(wù)嵌套
第一種情況:
AbcIService abcService;
BcdIService bcdService;
abcService.a1();
abcService.a2();
bcdService.b1();
這三個方法對應(yīng)的事務(wù)屬性都起作用。
第二種情況:
方法定義
public void a1() {
bcdService.b1();
}
調(diào)用:
abcService.a1();
結(jié)果:
abcService.a1();
bcdService.b1();
這兩個方法對應(yīng)的事務(wù)屬性都起作用。
第三種情況:
方法定義
public void a1() {
this.a2();
}
調(diào)用:
abcService.a1();
結(jié)果:
abcService.a1();
abcService.a2();
a2()對應(yīng)的事務(wù)屬性配置不起作用。
解決辦法:
1.把a(bǔ)2()拆到另一個類中去;
缺點(diǎn):麻煩,會把相關(guān)業(yè)務(wù)邏輯拆分了
2.調(diào)用是不用this.a2(),用abcService.a2();
public void a1() {
abcService.a2();
}
缺點(diǎn):要在類中注入自身引用。
原因分析:
為什么會出現(xiàn)這種情況呢?
我們在調(diào)用abcService.a1();時abcService是從IOC容器獲取的,并AbcServiceImpl而是它的動態(tài)代理AbcServiceProxy。
示意圖如下,spring不一定是這么實(shí)現(xiàn)的但原理一樣.

AbcServiceProxy.a()方法進(jìn)行了AOP增強(qiáng),根據(jù)配置文件中事務(wù)屬性增加了事務(wù)控制。
public void a1() {
this.a2();
}
this.a2()這里this指的是AbcIServiceImpl并沒用進(jìn)行AOP增強(qiáng),所以沒用應(yīng)用事務(wù)屬性,只能繼承a1()的事務(wù)屬性。
public void a1() {
abcService.a2();
}
abcService則實(shí)際是AbcServiceProxy.a2()所以可以應(yīng)用事務(wù)屬性。
所以在類內(nèi)部進(jìn)行方法嵌套調(diào)用,如果被嵌套的方法a2()需要區(qū)別于嵌套方法a1()的事務(wù)屬性,需要:1.在接口公開;2.通過代理調(diào)用。
作者:zyskm
http://m.tkk7.com/zyskm
上周在西單圖書城看到本不錯的書<<七步掌握業(yè)務(wù)分析>>,系統(tǒng)的講了些業(yè)務(wù)分析方面的知識,以前看到業(yè)務(wù)分析方面的資料都是站在需求捕獲分析的角度來寫,向這么完整系統(tǒng)介紹業(yè)務(wù)分析概念方法的不多。本著書非借不能讀的精神,我用了兩個周末在西單圖書大廈看完了整本書。記錄一下閱讀感受,同時結(jié)合一些自己的理解。
目錄結(jié)構(gòu)見下圖,大概是這個意思,名詞記得不是很準(zhǔn)確。

一個個說一下。
1.什么是業(yè)務(wù)分析
一般來講"什么是...?"這種問題都可以細(xì)分為這幾個問題,什么是業(yè)務(wù)分析、業(yè)務(wù)分析做什么(工作范圍)、跟系統(tǒng)分析有什么區(qū)別、什么人能做業(yè)務(wù)分析。
讓大家明白組織是怎么運(yùn)作的,為了實(shí)現(xiàn)組織的目標(biāo)提供解決方案。
實(shí)際開發(fā)過程中誰都可以做業(yè)務(wù)分析工作,目前很少看到這門業(yè)務(wù)分析師這一角色,一般是需求和系統(tǒng)分析師主要來做。根據(jù)個人能力的不同介入的程度不同。
說到范圍,業(yè)務(wù)分析主要從業(yè)務(wù)需求的角度來考慮,區(qū)別于系統(tǒng)分析更多的從系統(tǒng)能否實(shí)現(xiàn)、怎么實(shí)現(xiàn)的角度來考慮。很多時候系統(tǒng)分析師是同時兼這兩個角色的,優(yōu)勢是考慮問題有統(tǒng)一方向,缺點(diǎn)的是容易互相干擾。通用的解決思路是顏色帽子法,做哪塊工作時戴那個帽子只想這部分的事情,先不想別的部分怎么辦,先過今天再說明天。每一部分單獨(dú)考慮后再進(jìn)行綜合考慮,這樣工作才好開展,要不然一次想太多了事情就變得太復(fù)雜。
2.什么人來做
有人的地方就有江湖,既然在江湖里混就要了解這個江湖是什么人組成的,每個人的立場、對事情的認(rèn)識程度、角色、權(quán)利、影響。對這些人有了認(rèn)識,才能更好的認(rèn)識人做的事情。
這一點(diǎn)一般的需求書上只是提了一下,不想這本書就組織架構(gòu)、角色關(guān)系、溝通技巧各方面做了闡述。有個理論認(rèn)識實(shí)際操作起來更容易有方向感。
3.項(xiàng)目做什么
很多開發(fā)人員經(jīng)常是領(lǐng)導(dǎo)讓做什么就做什么,作為一個業(yè)務(wù)分析師顯然不能這么干。
業(yè)務(wù)分析師很多時候起的是一個導(dǎo)游的角色,探險(xiǎn)的時候大家全兩眼一抹黑只能掉溝里了。
誰發(fā)起的項(xiàng)目,項(xiàng)目要達(dá)成什么目標(biāo),對哪些人有益,對哪些人不利。這些都要了解,在項(xiàng)目的過程中才知道從哪方面去推動,避開可能會引起的麻煩。
4.了解業(yè)務(wù)
這個是當(dāng)然了,業(yè)務(wù)分析師嘛,具體方法先不寫了,都是寫常見的,重要的是要經(jīng)常實(shí)際總結(jié)提高。
5.了解業(yè)務(wù)環(huán)境
這也是一個江湖,更多的是從事情的角度進(jìn)行分類
6.了解技術(shù)環(huán)境
業(yè)務(wù)分析雖然不用寫代碼技術(shù)還是要懂一點(diǎn)的,好的服裝設(shè)計(jì)師都會自己做衣服(這可不是隨便說的,我見過幾個,呵呵)。
7.提升自身價(jià)值
洞悉事態(tài),能夠提出完整解決方案,這樣的人當(dāng)然是有價(jià)值的。做得越好越有價(jià)值。
書中提到了幾種提高技能方法:時間管理和一些分析技術(shù),包括溝通技巧。
先寫這么多。
作者:zyskm
http://m.tkk7.com/zyskm
基于spring的動態(tài)數(shù)據(jù)源
項(xiàng)目過程中遇到這樣一個需求,系統(tǒng)啟動后動態(tài)設(shè)置數(shù)據(jù)源,不同用戶登錄系統(tǒng)后訪問的數(shù)據(jù)庫不同。
好在所有書庫結(jié)構(gòu)一致。
用spring 的AbstractRoutingDataSource解決了這個問題。
原理如圖:

項(xiàng)目采用的是hibernate,直接在spring.xml設(shè)置sessionFactory的dataSource屬性為動態(tài)數(shù)據(jù)源即可。
因?yàn)轫?xiàng)目所有數(shù)據(jù)庫結(jié)構(gòu)都一致,為了避免每次設(shè)置數(shù)據(jù)源的時候要改一堆參數(shù),修改了spring AbstractRoutingDataSource類增加了一個getTargetDataSources方法,獲取當(dāng)前數(shù)據(jù)源詳細(xì)信息,在其基礎(chǔ)上修改數(shù)據(jù)庫名稱、用戶名、密碼即可,不用每次設(shè)置一堆參數(shù)。
Map<String,ComboPooledDataSource> targetDataSources=dynamicDataSource.getTargetDataSources();

if(targetDataSources==null)
{
targetDataSources=new HashMap<String, ComboPooledDataSource>();
targetDataSources.put("baseDataSource", baseDataSource);
}
targetDataSources.put(dataSourceName, subSystemDataSource);
dynamicDataSource.setTargetDataSources(targetDataSources);
dynamicDataSource.afterPropertiesSet();另外,設(shè)置AbstractRoutingDataSource參數(shù)后要調(diào)用afterPropertiesSet()方法,spring容器才會進(jìn)行加載操作。
在動態(tài)設(shè)置數(shù)據(jù)源方面,可以通過兩種方式實(shí)現(xiàn):
1.在action(項(xiàng)目使用struts)中進(jìn)行設(shè)置,可以確保在每個servlet線程中數(shù)據(jù)源是一致的。
2.以aop方式,對service方法進(jìn)行攔截,根據(jù)需求設(shè)置不同數(shù)據(jù)源。
在開發(fā)過程中數(shù)據(jù)庫的操作和使用要有一定規(guī)范,不然會引起混亂。
下邊是我們開發(fā)中具體例子,因?yàn)樯婕肮驹谟庙?xiàng)目詳細(xì)代碼就不列出來了,整個思路可供參考。
1.初始化腳本
各子系統(tǒng)建立自己的數(shù)據(jù)庫初始化腳本,格式可參照附件產(chǎn)品管理初始化腳本sql.rar(需要解壓到c:/sql才能在命令行執(zhí)行,見init.sql說明)
包括兩部分內(nèi)容:1.建表語句(ddl);2.基礎(chǔ)數(shù)據(jù)初始化(data)
建表語句由數(shù)據(jù)庫設(shè)計(jì)文檔(PowerDesigner)導(dǎo)出,基礎(chǔ)數(shù)據(jù)由excel文件導(dǎo)出(sql.rar提供示例,開發(fā)框架提供工具支持DbUtilTest.testExcel2Sql ())
作用:
1.數(shù)據(jù)庫結(jié)構(gòu)和基礎(chǔ)數(shù)據(jù)文檔化
2.便于快速搭建開發(fā)測試環(huán)境,新建一套環(huán)境時不用拷貝原數(shù)據(jù)庫而是執(zhí)行腳本
3.便于獨(dú)立開發(fā)測試,有一個干凈的數(shù)據(jù),避免開發(fā)測試依賴歷史數(shù)據(jù)和調(diào)試過程中互相影響
2.數(shù)據(jù)庫結(jié)構(gòu)比對
在部署多套數(shù)據(jù)庫時,懷疑表結(jié)構(gòu)不一致,可使用DbUtilTest.testCompareDataBase ()進(jìn)行檢查。
執(zhí)行后會在子系統(tǒng)根目錄生成dbcompare.html 文件,參加附件。
說明:紅色表示兩個表結(jié)構(gòu)不一致,綠色表示多出一表,黑色表示一致
3.數(shù)據(jù)庫字符集(UTF-8)
create database dbname CHARACTER SET utf8 COLLATE utf8_bin
不單獨(dú)對表和字段設(shè)置字符集,整個庫統(tǒng)一使用utf-8
4.表名字段名
建庫腳本中表名和字段名不區(qū)分大小寫
5.數(shù)據(jù)庫引擎(InnoDb)
目前主要使用的兩種引擎MyIsam,InnoDb。MyIsam查詢較快,不支持事務(wù)。InnoDb支持事務(wù)。
在建表sql指明引擎。
create table ***
(
id bigint(11) not null auto_increment,
.........
primary key (id)
)
type = innodb;
powerdesiner按如下方式設(shè)置:
字符集的概念大家都清楚,校對規(guī)則很多人不了解,一般數(shù)據(jù)庫開發(fā)中也用不到這個概念,mysql在這方便貌似很先進(jìn),大概介紹一下。
簡要說明
字符集和校對規(guī)則
字符集是一套符號和編碼。校對規(guī)則是在字符集內(nèi)用于比較字符的一套規(guī)則。
MySql在collation提供較強(qiáng)的支持,oracel在這方面沒查到相應(yīng)的資料。
不同字符集有不同的校對規(guī)則,命名約定:以其相關(guān)的字符集名開始,通常包括一個語言名,并且以_ci(大小寫不敏感)、_cs(大小寫敏感)或_bin(二元)結(jié)束
校對規(guī)則一般分為兩類:
binary collation,二元法,直接比較字符的編碼,可以認(rèn)為是區(qū)分大小寫的,因?yàn)樽址?A'和'a'的編碼顯然不同。
字符集_語言名,utf8默認(rèn)校對規(guī)則是utf8_general_ci
mysql字符集和校對規(guī)則有4個級別的默認(rèn)設(shè)置:服務(wù)器級、數(shù)據(jù)庫級、表級和連接級。
具體來說,我們系統(tǒng)使用的是utf8字符集,如果使用utf8_bin校對規(guī)則執(zhí)行sql查詢時區(qū)分大小寫,使用utf8_general_ci 不區(qū)分大小寫。不要使用utf8_unicode_ci。
如create database demo CHARACTER SET utf8; 默認(rèn)校對規(guī)則是utf8_general_ci 。
Unicode與UTF8
Unicode只是一個符號集,它只規(guī)定了符號的二進(jìn)制代碼,卻沒有規(guī)定這個二進(jìn)制代碼應(yīng)該如何存儲.
UTF8字符集是存儲Unicode數(shù)據(jù)的一種可選方法。 mysql同時支持另一種實(shí)現(xiàn)ucs2。
詳細(xì)說明
字符集(charset):是一套符號和編碼。
校對規(guī)則(collation):是在字符集內(nèi)用于比較字符的一套規(guī)則,比如定義'A'<'B'這樣的關(guān)系的規(guī)則。不同collation可以實(shí)現(xiàn)不同的比較規(guī)則,如'A'='a'在有的規(guī)則中成立,而有的不成立;進(jìn)而說,就是有的規(guī)則區(qū)分大小寫,而有的無視。
每個字符集有一個或多個校對規(guī)則,并且每個校對規(guī)則只能屬于一個字符集。
binary collation,二元法,直接比較字符的編碼,可以認(rèn)為是區(qū)分大小寫的,因?yàn)樽址?A'和'a'的編碼顯然不同。除此以外,還有更加復(fù)雜的比較規(guī)則,這些規(guī)則在簡單的二元法之上增加一些額外的規(guī)定,比較就更加復(fù)雜了。
mysql5.1在字符集和校對規(guī)則的使用比其它大多數(shù)數(shù)據(jù)庫管理系統(tǒng)超前許多,可以在任何級別進(jìn)行使用和設(shè)置,為了有效地使用這些功能,你需要了解哪些字符集和 校對規(guī)則是可用的,怎樣改變默認(rèn)值,以及它們怎樣影響字符操作符和字符串函數(shù)的行為。
校對規(guī)則一般有這些特征:
兩個不同的字符集不能有相同的校對規(guī)則。
每個字符集有一個默認(rèn)校對規(guī)則。例如,utf8默認(rèn)校對規(guī)則是utf8_general_ci。
存在校對規(guī)則命名約定:它們以其相關(guān)的字符集名開始,通常包括一個語言名,并且以_ci(大小寫不敏感)、_cs(大小寫敏感)或_bin(二元)結(jié)束
確定默認(rèn)字符集和校對
字符集和校對規(guī)則有4個級別的默認(rèn)設(shè)置:服務(wù)器級、數(shù)據(jù)庫級、表級和連接級。
數(shù)據(jù)庫字符集和校對
每一個數(shù)據(jù)庫有一個數(shù)據(jù)庫字符集和一個數(shù)據(jù)庫校對規(guī)則,它不能夠?yàn)榭铡REATE DATABASE和ALTER DATABASE語句有一個可選的子句來指定數(shù)據(jù)庫字符集和校對規(guī)則:
例如:
CREATE DATABASE db_name DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci;
MySQL這樣選擇數(shù)據(jù)庫字符集和數(shù)據(jù)庫校對規(guī)則:
· 如果指定了CHARACTER SET X和COLLATE Y,那么采用字符集X和校對規(guī)則Y。
· 如果指定了CHARACTER SET X而沒有指定COLLATE Y,那么采用CHARACTER SET X和CHARACTER SET X的默認(rèn)校對規(guī)則。
· 否則,采用服務(wù)器字符集和服務(wù)器校對規(guī)則。
在SQL語句中使用COLLATE
•使用COLLATE子句,能夠?yàn)橐粋€比較覆蓋任何默認(rèn)校對規(guī)則。COLLATE可以用于多種SQL語句中。
使用WHERE:
select * from pro_product where product_code='ABcdefg' collate utf8_general_ci
Unicode與UTF8
Unicode只是一個符號集,它只規(guī)定了符號的二進(jìn)制代碼,卻沒有規(guī)定這個二進(jìn)制代碼應(yīng)該如何存儲.Unicode碼可以采用UCS-2格式直接存儲.mysql支持ucs2字符集。
UTF-8就是在互聯(lián)網(wǎng)上使用最廣的一種unicode的實(shí)現(xiàn)方式。其他實(shí)現(xiàn)方式還包括UTF-16和UTF-32,不過在互聯(lián)網(wǎng)上基本不用。
UTF8字符集(轉(zhuǎn)換Unicode表示)是存儲Unicode數(shù)據(jù)的一種可選方法。它根據(jù) RFC 3629執(zhí)行。UTF8字符集的思想是不同Unicode字符采用變長字節(jié)序列編碼:
· 基本拉丁字母、數(shù)字和標(biāo)點(diǎn)符號使用一個字節(jié)。
· 大多數(shù)的歐洲和中東手寫字母適合兩個字節(jié)序列:擴(kuò)展的拉丁字母(包括發(fā)音符號、長音符號、重音符號、低音符號和其它音符)、西里爾字母、希臘語、亞美尼亞語、希伯來語、阿拉伯語、敘利亞語和其它語言。
· 韓語、中文和日本象形文字使用三個字節(jié)序列。