J2EE - 如何在JBoss中解決自動增長鍵值問題

<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

 

J2EE 如何在JBoss中解決自動增長鍵值問題

【前言】

自動增長鍵值是關係數據庫的一個顯著功能特徵,如MS Sql ServerMySql可以直接將一個字段設置成自動增長(auto-increment)類型,Oracle也提供了類似的sequence number功能。然而在EJB2.0規範之前,CMP部分並沒有對自動增長鍵有相關的說明,這一缺陷一直深受J2EE開發人員的詬病,而應用服務器開發商爲此也提出了各自的解決方案,但是在沒有上升到規範的高度之前,這些解決方案都是非正式的,也就是說缺乏通用性,如聞名遐爾的Weblogic,在1.0規範的時候也僅僅是對一些主流數據庫進行了支持。JBoss3.0版本沒有支持auto-increment特徵,到3.2版本才正式支持。本篇是介紹在3.0版本下JBoss如何使用AutoNumber這個EJB插件來實現數據庫表鍵值自動增長功能,在文章的後半部分介紹在3.2版本中如何使用“unknown keys”特徵來真正實現自動鍵值增長。後臺數據庫使用的是MySql4.0.12版本。

 

EJB是一個仍在快速發展的技術,發展就意味着變化,而“快速”同樣適用於“變化”二字,尤其在EJBCMP部分,其變化尤爲突出。CMPEJB最精華的體現,由於使用了對象的方式來描述數據庫,而目前大多數據庫仍以關係類型爲主,表達與被表達之間存在着不可忽略的差異,因而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。這張表很簡單,只包含兩個字段列namevaluename列用以存儲鍵值名稱,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,只有兩個字段idname,其中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字段,idname字段在bean中都有get/set方法。

 

部署描述文件

JBoss3.2較之3.0版本又有了極其不同的差異,一個就是對EJB2.0規範的支持,其體現在部署文件描述又變了。在三個描述文件ejb-jar.xmljboss.xmljbosscmp-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 $ -->,雖然只是註釋,但是對JBossxml解析器而言這個信息非常重要,因爲JBoss仍要處理低版本的部署文件,這個id是爲了告訴JBoss用什麼規則來解析。

 

總結

通過以上兩個對auto-increment問題在JBoss不同版本中的解決方案,可以看出J2EE的一些特點,對開發人員而言這些特點相當重要。J2EE的設計思想非常先進,但同時也帶來了一定的的複雜度,需要考慮和處理的問題會有相應增加。

 

JBoss是一個開發源碼而且完全免費的應用服務器,其與MySql的搭配可以用“天造一對,地設一雙”來形容,對於需要實踐鍛鍊的開發者而言是一個天大的福音,不僅可以免費在上面進行開發,還可以參考其設計思路(如第一節對AutoNumberFactory的設計分析)JBoss在使用上面透露着二字,那就是“簡潔”,但又不失其強大的J2EE引擎功能。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章