<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
J2EE - 如何在JBoss中解決自動增長鍵值問題
【前言】
自動增長鍵值是關係數據庫的一個顯著功能特徵,如MS Sql Server、MySql可以直接將一個字段設置成自動增長(auto-increment)類型,Oracle也提供了類似的sequence number功能。然而在EJB2.0規範之前,CMP部分並沒有對自動增長鍵有相關的說明,這一缺陷一直深受J2EE開發人員的詬病,而應用服務器開發商爲此也提出了各自的解決方案,但是在沒有上升到規範的高度之前,這些解決方案都是非正式的,也就是說缺乏通用性,如聞名遐爾的Weblogic,在1.0規範的時候也僅僅是對一些主流數據庫進行了支持。JBoss的3.0版本沒有支持auto-increment特徵,到3.2版本才正式支持。本篇是介紹在3.0版本下JBoss如何使用AutoNumber這個EJB插件來實現數據庫表鍵值自動增長功能,在文章的後半部分介紹在3.2版本中如何使用“unknown keys”特徵來真正實現自動鍵值增長。後臺數據庫使用的是MySql4.0.12版本。
EJB是一個仍在快速發展的技術,發展就意味着變化,而“快速”同樣適用於“變化”二字,尤其在EJB的CMP部分,其變化尤爲突出。CMP是EJB最精華的體現,由於使用了對象的方式來描述數據庫,而目前大多數據庫仍以關係類型爲主,表達與被表達之間存在着不可忽略的差異,因而EJB規範在制定中需要考慮相關因素,不得不做出均衡的取捨。由於前面所提到的發展性,規範的最終成熟也不是一蹴而就,一個規範的推出可能迫於時機因素(技術或者是商業) 而推遲,開發人員在實際開發當中應該能夠明確意識到這點,在遇到規範所引發的侷限的地方,充分發揮主觀能動性,在考慮問題解決問題方面能夠突破到一個新境界。
JBoss3.0提供的AutoNumber處理方式
JBoss3.0給用戶提供了一個AutoNumber插件來“僞”實現CMP的鍵值自動增長,之所以冠以一個“僞”字,是因爲這個功能並不是通過數據庫自身的功能來實現,而是利用了一個工廠類型的CMP Entity Bean來爲各類實體Bean(對應數據庫中的表)生成相應的鍵值,用戶不必考慮如何來維護鍵值的唯一性和連續性,這一切都由AutoNumber來代理,用戶要做的就是在使用這個“插件”之前先部署這個Bean Jar,並弄清楚其調用原理以知悉如何來使用這個CMP Bean。
在着手部署這個插件之前,先來看一段JBoss源代碼文件AutoNumberFactory的代碼註釋:
/**
* Gets the next key for the given collection.
* Note 1: you must deploy EJB AutoNumber
* Note 2: the keys are persistent in your database, independent of
* the actual table
* Note 3: you can only add instances to the collection which have a
* key generated by this method, otherwise the keys are not guaranteed
* to be unique
* Note 4: key values are >= 0
*/
大意如下:該類是爲了給一個鍵集合獲取下一個鍵值,爲了使用這個工廠方法,你首先必須部署AutoNumber這個EJB,鍵集信息被存儲在數據庫中,與實際表無關,爲了確保鍵值的唯一性,你只能通過這個工廠方法來生成鍵值,鍵值的範圍是大於或等於0的整數。
部署AutoNumber CMP Entity Bean
AutoNumber插件的位置是在%JBOSS_HOME%/server/default/lib/ autonumber-plugin.jar,令人覺得驚奇的是JBoss沒有爲這個插件如何部署給出例子或者是指導性的說明文件(或者是本人尚未發現)。在研究了一下相關的源代碼後,以下是一個部署的實例,以及在數據庫中如何創建一個表來對應這個實體Bean。
首先在MySql中創建一張表,來對應AutoNumber bean。這張表很簡單,只包含兩個字段列name和value,name列用以存儲鍵值名稱,value用以存儲當前可用的鍵值。DDL語句如下:
CREATE TABLE `myauto` (
`name` varchar(20) NOT NULL default '',
`value` int(11) NOT NULL default '0',
PRIMARY KEY (`name`),
UNIQUE KEY `name` (`name`)
) TYPE=InnoDB
這裏將name設置爲主鍵,並且爲unique,約束了鍵名不可重複。
Ejb-jar.xml描述文件:
<ejb-jar>
<enterprise-beans>
<entity>
<display-name>MyAutoNumber</display-name>
<ejb-name>AutoNumber</ejb-name>
<home>org.jboss.varia.autonumber.AutoNumberHome</home>
<remote>org.jboss.varia.autonumber.AutoNumber</remote>
<ejb-class>org.jboss.varia.autonumber.AutoNumberEJB2</ejb-class>
<persistence-type>Container</persistence-type>
<prim-key-class>java.lang.String</prim-key-class>
<reentrant>False</reentrant>
<cmp-version>2.x</cmp-version>
<abstract-schema-name>autonumberschema</abstract-schema-name>
<cmp-field>
<field-name>name</field-name>
</cmp-field>
<cmp-field>
<field-name>value</field-name>
</cmp-field>
<primkey-field>name</primkey-field>
</entity>
</enterprise-beans>
<assembly-descriptor>
<container-transaction>
<method>
<ejb-name>AutoNumber</ejb-name>
<method-name>*</method-name>
</method>
<trans-attribute>Required</trans-attribute>
</container-transaction>
</assembly-descriptor>
</ejb-jar>
Jbosscmp-jdbc.xml描述文件:
<jbosscmp-jdbc>
<enterprise-beans>
<entity>
<ejb-name>AutoNumber</ejb-name>
<datasource>java:/MySqlDS</datasource>
<datasource-mapping>mySQL</datasource-mapping>
<create-table>false</create-table>
<remove-table>false</remove-table>
<read-only>false</read-only>
<table-name>myauto</table-name>
<cmp-field>
<field-name>name</field-name>
<column-name>NAME</column-name>
</cmp-field>
<cmp-field>
<field-name>value</field-name>
<column-name>VALUE</column-name>
</cmp-field>
</entity>
</enterprise-beans>
</jbosscmp-jdbc>
<datasource>標籤值改成你相應的數據源名稱,<table-name>及<field-name>標籤值也改成你相應建立的數據庫表及字段列名稱。
Jboss.xml描述文件:
<jboss>
<enterprise-beans>
<entity>
<ejb-name>AutoNumber</ejb-name>
<jndi-name>JBossUtilAutoNumber</jndi-name>
</entity>
</enterprise-beans>
</jboss>
在正式部署前,將部署文件置入一個meta-inf目錄中,然後連同autonumber-plugin.jar文件一同jar進一個新的jar文件當中,最後是將該新jar文件copy至%JBOSS_HOME%/server/default/deploy目錄並啓動JBoss以觀部署後效。<jndi-name>標籤指明的jndi名必須是JBossUtilAutoNumber,因爲AutoNumberFactory就是通過這個名字來調用AutoNumberBean的本地引用的。
AutoNumber的調用
AutoNumber這個cmp bean是通過AutoNumberFactory這個工廠類的靜態類方法來調用的,調用方法很簡單,假如你有一張表叫table1,裏頭有一整數類型的列充當了主鍵,那麼可以將這個鍵值集合命名成“table1_pk”,通過AutoNumberFactory. getNextInteger(“table1_pk”)就可以獲得表table1的當前一個Integer類型的鍵值。如果是第一次調用,那麼獲得的值爲0,通過這個工廠類來調用AutoNumber不用做任何初始化工作,在調用的CMP類文件中要寫這樣一個import語句:import org.jboss.varia.autonumber.AutoNumberFactory;
此外AutoNumber還提供了鍵值初始化和reset的方法,詳細參考AutoNumberFactory的源代碼文件。AutoNumberFactory主要用在cmp bean的ejbCreate方法中,如:
Integer myPk=AutoNumberFactory. getNextInteger(“table1_pk”);
setId(myPk);
AutoNumber的原理
AutoNumber插件的核心是一個CMP bean,並且使用了工廠方式來生成自動增長鍵,不過這個工廠模式是建立在數據庫上,可以同時爲多個表生成鍵值。每次調用getNextInteger的時候,AutoNumberFactory通過JNDI名獲得AutoNumber bean的本地接口,調用bean提供的方法,該方法要做的就是返回當前數據庫中的鍵值,而後將鍵值加一。
小結
AutoNumber相當於開闢了另一條路來方便用戶獲取一個整數類型的鍵值,並確保通過該路徑獲取的鍵值能夠保持唯一性。但是這種繞開數據庫的方式有着很大的問題,因爲它不是數據庫自身來維護的,比如你在另一個數據訪問程序(如mysql提供的客戶端)來爲一個表增加一條記錄,如果你忘記了模擬AutoNumber的形式獲得鍵值,也就是忘了在myauto表中將相應鍵值加一,那麼你的ejb下一次獲得的鍵值將會產生衝突。另外一個缺點就是鍵值只支持Integer類型。對於第一個缺點,是AutoNumber無法解決的,但是第二個缺點卻是可以解決的,那就是創建我們自己的Factory方法,支持各種需要的主鍵數據類型,AutoNumber給了我們一個很好的設計範例。
在JBoss3.2中實現自動增長鍵值功能
先決條件
對於MySql的用戶來說,你需要一個更好的引擎來支持更好的功能,爲了讓3.2版本能夠在mysql中完成這個功能實現的一個例子,你首先要做的就是下載一個先進的JDBC引擎,
MySQL Connector/J 3,該引擎支持了JDBC3.0,至於原因可參看【部署描述文件】部分。這裏稍微引開一個話題,如果你是一個好的java程序員,那麼你必須時刻對版本保持高度的敏感性,看看Jbuilder版本更新之快之大,JBoss4.0也在日程當中 ,更高的版本意味着更好的功能,但是,不要遺漏了版本之間的差異,不僅僅是主體的差異,還有聯繫體的差異(這裏是JBoss是主體,mysql jdbc driver是聯繫體),不然你會頭碰南牆欲哭無淚,“爲伊消得人憔悴”,云云。閒話少說,將Connector/J 3的驅動jar文件拷貝至%JBOSS_HOME%/ server/default/lib/之下,記得將原有驅動刪除。
一個CMP Bean場景
爲了方便描述how to,使用一個簡單的cmp bean作爲實例來加以描述。假設有一張表auto_inc_test,只有兩個字段id和name,其中id是自動增長類型,sql腳本如下:
create table auto_inc_test (
id TINYINT(4) NOT NULL auto_increment,
name varchar(10),
primary key (id)
) type=InnoDB
至於如何設計和實現這個cmp bean,這裏就不詳加描述,對於Jbuilder而言僅僅是幾個drag and drop的回合,我們將重心放在部署文件的配置上面。我們的bean 名稱叫做TestAutoIncBean,有一個ejbCreate方法,只接受一個字符類型數據爲參數,即name字段,id和name字段在bean中都有get/set方法。
部署描述文件
JBoss3.2較之3.0版本又有了極其不同的差異,一個就是對EJB2.0規範的支持,其體現在部署文件描述又變了。在三個描述文件ejb-jar.xml、jboss.xml和jbosscmp-jdbc.xml中,前兩個與一般部署無異,最後一個部署文件的描述如下:
Jbosscmp-jdbc.xml描述文件
<!-- $Id: standardjbosscmp-jdbc.xml,v 1.39.2.12 2003/03/02 13:43:33 cooperfbi Exp $ -->
<jbosscmp-jdbc>
<defaults>
<datasource>java:/MySqlDS</datasource>
<datasource-mapping>mySQL</datasource-mapping>
<create-table>false</create-table>
<remove-table>false</remove-table>
<read-only>false</read-only>
<time-out>300</time-out>
<pk-constraint>true</pk-constraint>
<fk-constraint>false</fk-constraint>
<row-locking>false</row-locking>
<preferred-relation-mapping>foreign-key</preferred-relation-mapping>
<read-ahead>
<strategy>on-load</strategy>
<page-size>1000</page-size>
<eager-load-group>*</eager-load-group>
</read-ahead>
<list-cache-max>1000</list-cache-max>
<unknown-pk>
<key-generator-factory>UUIDKeyGeneratorFactory</key-generator-factory>
<unknown-pk-class>java.lang.Integer</unknown-pk-class>
<jdbc-type>INTEGER</jdbc-type>
<sql-type>INTEGER</sql-type>
</unknown-pk>
<entity-command name="mysql-get-generated-keys"/>
</defaults>
<enterprise-beans>
<entity>
<ejb-name>TestAutoInc</ejb-name>
<table-name>auto_inc_test</table-name>
<cmp-field>
<field-name>id</field-name>
<column-name>id</column-name>
</cmp-field>
<cmp-field>
<field-name>name</field-name>
<column-name>name</column-name>
</cmp-field>
</entity>
</enterprise-beans>
<dependent-value-classes>
</dependent-value-classes>
</jbosscmp-jdbc>
這裏把與數據庫映射的描述部分放在<enterprise-beans>標籤外部,意指這些數據庫設置對所有entity bean適用,也可以放在<enterprise-beans>內部對單獨的entity bean進行指定。<unknown-pk>是JBoss3.2新增的描述標籤,用戶可以在其中指定<key-generator-factory>,該標籤指明獲得鍵值工廠生成器的JNDI名稱,這裏是UUIDKeyGeneratorFactory,由JBoss服務器自身內部提供的,用戶不必關注其中細節,另外用戶要指定主鍵類的類型,這裏是Integer type。另外值得一提的是<entity-command>標籤,它指明瞭在創建entity bean的時候要調用哪個輔助插件,這裏的值是"mysql-get-generated-keys",當創建entity bean的時候調用org.jboss.ejb.plugins.cmp.jdbc.mysql.JDBCMySQLCreateCommand插件,該插件則調用了Connector /J3提供的接口來處理與主鍵相關的事務。注意該描述文件的頭一句<!-- $Id: standardjbosscmp-jdbc.xml,v 1.39.2.12 2003/03/02 13:43:33 cooperfbi Exp $ -->,雖然只是註釋,但是對JBoss的xml解析器而言這個信息非常重要,因爲JBoss仍要處理低版本的部署文件,這個id是爲了告訴JBoss用什麼規則來解析。
總結
通過以上兩個對auto-increment問題在JBoss不同版本中的解決方案,可以看出J2EE的一些特點,對開發人員而言這些特點相當重要。J2EE的設計思想非常先進,但同時也帶來了一定的的複雜度,需要考慮和處理的問題會有相應增加。
JBoss是一個開發源碼而且完全免費的應用服務器,其與MySql的搭配可以用“天造一對,地設一雙”來形容,對於需要實踐鍛鍊的開發者而言是一個天大的福音,不僅可以免費在上面進行開發,還可以參考其設計思路(如第一節對AutoNumberFactory的設計分析)。JBoss在使用上面透露着二字,那就是“簡潔”,但又不失其強大的J2EE引擎功能。