EasyJF
序 言
EasyDBO是一個非常適合中小型軟件數據庫開發的數據持久層框架,系統參考hibernate、JDO等,結合中小項目軟件的開發實際,實現簡單的對象-關系數據庫映射。 本文主要簡介EasyDBO的使用方法,作為初學者的快速上手指南。由于EasyDBO是一個不斷更新的開源項目,本教程中一些新特性、功能的介紹及應用將不定期的在EasyDBO官方網站上提供。
該教程主要由EasyJF開源團隊-EasyDBO項目組編寫,免費在各大Java專業網站及其它專業媒體上發表。
當前參該文檔編寫的EasyJF成員有(排名不分先后):
大峽、stef_wu、天意及其它EasyJF團隊成員
歡迎更多的朋友加入到我們的寫作當中!
EasyDBO項目開發人員
大峽、piginzoo、william、stef_wu、天意、散仙、qgz0910、clyyu等
目錄:
序言 一、EasyDBO簡介1.1 對象-關系映射簡介
1.2如何實現對象-關系映射
1.3 為什么要設計EasyDBO
1.4 EasyDBO簡介
二、EasyDBO快速上手及示例
2.1獲取EasyDBO SDK及源代碼
2.2 EasyDBO文件組成
2.3 編譯安裝EasyDBO
2.4 使用EasyDBO
2.5、開始簡單的映射
2.6、使用EasyDBO總體流程簡單分析
三、EasyDBO使用詳解
3.1 ORM核心引擎EasyJDB的創建
3.1.1 映射處理核心引擎EasyJDB類簡介
3.1.2 創建EasyJDB對象實例
3.2 數據源設置
3.3數據庫查詢方言
3.4 實(域)對象PO及泛對象FO
3.5 使用接口實現簡單映射
3.6 使用配置文件配置映射關系
3.7 使用Java5注解來設置映射
3.8 主鍵配置
3.9 緩存設置
3.10 一對一映射
3.11 一對多映射
3.12 多對多映射
3.13 延遲加載
3.14 事務處理
3.15 使用存儲過程
四、EasyJDB中常用方法介紹
4.1 對象添、刪、改
4.2 從持久層讀取單個域對象DOM(PO)
4.3從持久層讀取多個域對象DOM(PO)
4.4 從持久層讀取多個泛對象FO(Fan Object)
4.5從持久層返回唯一對象
4.6 執行自定義sql語句
4.7 存儲過程
五、EasyDBO以其它框架的集成運用
5.1 EasyDBO與EasyJWeb集成
5.2 EasyDBO與Spring集成
5.3 在Struts中使用EasyDBO
?
6.2 簡易java框架開源論壇系統(最新版本0.5,更新時間10月1日)
6.3 簡易java框架開源訂銷管理系統(最新更新:2006-4-3)
6.4 EasyJF開源網上會議系統iula-0.1.0(最新更新:2006-7-13)
?
七、結束語
八、聯系我們
?
一、EasyDBO簡介
1.1 對象-關系映射簡介
對象-關系映射(Object/Relation Mapping,簡稱ORM),是隨著面向對象的軟件開發方法發展而產生的。面向對象的開發方法是當今企業級應用開發環境中的主流開發方法,關系數據庫是企業級應用環境中永久存放數據的主流數據存儲系統。對象和關系數據是業務實體的兩種表現形式,業務實體在內存中表現為對象,在數據庫中表現為關系數據。內存中的對象之間存在關聯和繼承關系,而在數據庫中,關系數據無法直接表達多對多關聯和繼承關系。因此,對象-關系映射(ORM)系統一般以中間件的形式存在,主要實現程序對象到關系數據庫數據的映射。
在開發關系數據庫的系統時,可以通過SQL語句讀取及操作關系數據庫數據。在Java領域,可以直接通過JDBC編程來訪問數據庫。JDBC可以說是JAVA訪問關系數據庫的最原始、最直接的方法。這種方式的優點是運行效率高,缺點是在Java程序代碼中嵌入大量SQL語句,冗余是不可避免的,開發人員常常發現自己在一次又一次地編寫相同的普通代碼,如獲得連接、準備語句、循環結果集以及其他一些 JDBC 特定元素,使得項目難以維護。特別是當涉及到非常多的關系數據表、需要在多個不同類型的關系數據庫系統中使用時,通過在程序中使用JDBC開發實施起來更加困難。
在開發基于數據應用為主的軟件系統時,引入對象-關系映射中間件是提高開發效率、提升軟件產品的可維護、擴展性的現實需要。實踐表明,在基于數據處理為主的企業級應用程序開發中,通過引入對象-關系映射中間件,可以節省與對象持久化相關的差不多35%的編程工作量,同時提升軟件產品可維護及易擴展性,提升軟件產品質量。
1.2如何實現對象-關系映射
在開發企業級應用時,有必要通過引入對象-關系映射系統中間件,實現數據庫的快速開發。企業可以通過JDBC編程來開發單獨的持久化層,把數據庫訪問操作封裝起來,提供簡潔的API,供業務層統一調用,實現自己的ORM系統中間件。
當然,一個成熟的對象-關系映射中間件產品,不只是簡單的把內存中的對象持久化到數據庫、把數據庫中的關系數據加載到內存中,還要保證系統頻繁地訪問數據庫的性能,降低訪問數據庫的頻率,需要引入多線程、緩存、事務管理等很多細節,涉及到的技術比較復雜,因此,我們更多是使用市場上優秀的ORM系統中間件產品。
當前主流的 ORM 中間件產品主要有:
Hibernate
EasyDBO
iBatis
JDO
EJB Entities 3
EJB Entity Beans 2.x
TopLink
1.3 為什么要設計EasyDBO
要使用java訪問數據庫,我們都知道使用JDBC,然而jdbc相對來說畢竟是一個比較底層的數據庫訪問API,在實際應用中需要寫非常多的SQL語句,管理數據源、處理數據訪問異常等諸多工作,造成開發的極為不便。而且,由于不同數據庫之間的sql語句存在一定的差異,也使得我們寫的程序難免捆綁到某一種數據庫上,無法實現程序靈活的在不同的數據庫之間進行移植。
近年來,對象關系映射系統得到廣泛的應用。隨著以iBatis、hibernate為代表的輕量級ORM系統普及,我們基本上可以告別JDBC,告訴煩瑣的SQL語句,同時也解決了前面提出的問題。然而,在軟件的領域,永遠沒有完美的系統解決方案,隨著我們在大量的項目中運用這些ORM系統,我們又發現了很多新的問題與苦惱。如:煩瑣的配置文件、機器語言一樣sql語句、數據訪問的效率、性能問題以及難于發現的錯誤等等。
在一些中小型的應用軟件中,一方面由于存在諸多因素,使得需求往往存在不確定性,另一方面軟件正向更加專業化方向發展,軟件的需求越來越復雜。如果每次變動都需要經過修改域模型、改配置文件、改業務邏輯等多個機械的步驟及環節,顯得難以適應。因此,要求我們有一個能快速適應變化的超輕量級ORM系統,適應我們快捷開發的需求。
1.4 EasyDBO簡介
EasyDBO是基于java技術,應用于Java程序中實現快速數據庫開發的對象-關系映射(ORM)系統框架。從本質上說,EasyDBO是一個對JDBC的簡單封裝,通過借鑒當前的主流ORM系統,引入了更加簡單實用的方式來實現對象及關系數據庫的映射,從而使得我們在開發中可以把精力更多的集成中在域建模及軟件業務邏輯上面。
EasyDBO是一個超輕量級的ORM系統,其定位于解決中小型系統項目中的對象關系映射。提供更加簡便、靈活的映射方式,把實際應用中的最佳實踐融入到ORM的設計中,從而滿足快捷開發的要求,即快速、簡捷的完成應用軟件開發。
EasyDBO對外提供一用于處理對象-關系映射等的核心引擎,我們通過使用該引擎即可實現關系數據庫的相關操作。若結合EasyJF所提供的其它的開源框架如EasyJWeb等使用,則可以在實際開發中大大提高開發效率。
二、EasyDBO快速上手及示例
2.1獲取EasyDBO SDK及源代碼
EasyDBO作為國內的一個Java開源項目,可以通過其開發團隊的官方網站www.easyjf.com中下載,下載地址:http://www.easyjf.com/easydbo/download.htm。當然,也可以直接從SVN上直接Check out該項目最新的源代碼,EasyDBO的SVN地址:http://svn.easyjf.com/repository/easyjf/easydbo/
2.2 EasyDBO文件組成
下載的文件主要包括
lib為編譯EasyDBO所需要的支持庫;
src目錄為EasyDBO框架的全部源代碼、測試代碼及示例代碼;
bin目錄為EasyDBO的發布命令及腳本等。
2.3 編譯安裝EasyDBO
一般情況下,我們直接下載整個EasyDBO項目的源代碼,然后在自己的機器上根據JDK重新編譯一次。通過執行bin里面的build.bat jar,或者雙擊build.bat,然后選擇jar,即可執行EasyDBO的編譯工作。
如下圖所示:
編譯完成后,若輸入的是jar命令。則會生成一個release目錄,其中有一個名為easyjf-dbo-1-0-0.jar的文件,其中后面的數字表示版本號。
(注:由于EasyDBO中支持使用Java5注解來配置對象關系映射,因此,需要在Java5(jdk1.5)以上的環境進行編譯,才能使用全部的功能。)
2.4 使用EasyDBO
要在項目中使用EasyDBO,把easyjf-dbo.jar文件與log4j日志的jar,添加到你項目的類路徑或classpath中即可。
當然,由于涉及到數據庫開發,還必須保證您所用的數據庫驅動包、連接沲驅動包也需要存放于類路徑上。
下圖是我們使用My SQL數據,使用apache-dbcp連接池來處理數據庫的項目中,使用EasyDBO所需要的最少的jar文件。
2.5、開始簡單的映射
下面是我們使用EasyDBO的一個簡單例子。我們以一個留言板表Message為例,首先定一個表示留言板信息的持久層對象PO,內容是一個簡單JavaBean,由于我們使用比較簡單的接口映射方式來實現映射關系,所以這個Java Bean還實IObject接口。Message.java的內容大致如下:
package example;
import java.util.Date;
import com.easyjf.dbo.IObject;
public class Message implements IObject {
private int cid;
private String title;
private String content;
private String inputUser;
private Date inputTime;
private Boolean publish;
private Integer status;
public String getTableName() {
return "message";
}
public String getKeyField() {
return "cid";
}
public String getKeyGenerator() {
return "com.easyjf.dbo.NullIdGenerator";
}
public int getCid() {
return cid;
}
public void setCid(int cid) {
this.cid = cid;
}
//...省略getter及setter方法。
}
下面,我們寫一個簡單的演示代碼,看看在程序中如何使用EasyDBO自動實現對象及關系表之間的映射。示例代碼如下:
package example;
import org.apache.commons.dbcp.BasicDataSource;
public class MessageTest {
public static void main(String[] args) {
//首先準備一個數據源
BasicDataSource datasource = new BasicDataSource();
datasource.setDriverClassName("org.gjt.mm.mysql.Driver");
datasource.setUrl("jdbc:mysql://127.0.0.1:3306/easyjf");
datasource.setUsername("root");
datasource.setPassword("mysql");
//使用數據源創建一個EasyDBO映射處理引擎EasyJDB對象
com.easyjf.dbo.EasyJDB easyjdb=new com.easyjf.dbo.EasyJDB(datasource);
Message m=new Message();
m.setTitle("留言標題");
m.setContent("留言內容");
m.setInputTime(new java.util.Date());
m.setInputUser("easyjf");
m.setPublish(Boolean.TRUE);
m.setStatus(new Integer(0));
//使用EasyDBO映射處理引擎執行相關的數據持久化操作
boolean ret=easyjdb.add(m);
if(ret)
System.out.println("成功寫入數據!");
//從數據庫中讀取對象
java.util.List list=easyjdb.query(Message.class,"1=1");
Message m2=(Message)list.get(0);
System.out.println(m2.getTitle());
System.out.println(m2.getContent());
}
}
輸出結果:
成功寫入數據!
留言標題
留言內容
2.6、使用EasyDBO 總體流程簡單分析
1、選擇三種映射方式中的一種,書寫域對象(DOM或PO),即普通的Java Bean。若選擇的映射方式是接口方式,則要求Java Bean實現IObject接口;若使用Java5注解來定義映射,則要求在JavaBean中使用相關的標簽來指定映射方式;若采用配置文件方式來定義映射,則要求在easyjf-dbo.xml文件中定義對象-關系表的映射。
2、在應用程序中創建一個EasyJDB實例,設置數據源、加載配置文件(如果需要的話),然后設置EasyJDB相關參數,如緩存、是否在控制臺回顯Sql語句、默認事務處理方式等。
3、使用EasyJDB來執行數據添、刪、改等操作。
4、在需要的時候,也可以通過EasyJDB來執行傳統的SQL語句、自定義復雜的SQL查詢以及存儲過程調用等。
三、EasyDBO使用詳解
3.1 ORM核心引擎EasyJDB的創建
3.1.1 映射處理核心引擎EasyJDB類簡介
在EasyDBO中,直接負責映射處理的類是EasyJDB,我們在應用程序中只需要取得一個映射引擎的實例,即可使用該對象實現對象-關系數據庫的映射操作。
EasyJDB中的方法大致可以歸為三種類型:
1、第一種是把對象持久化到數據庫中的相關方法,也是直接執行一定數據庫操作的方法,如add、update、del、saveOrUpdate、execute等。比如使用easyjdb.add(m),實現把域對象m中的數據保存到持久層存儲設備數據庫中。
2、第二種是把從持久層存儲設備數據庫中查詢域對象的方法。如query、uniqueResult、read等。比如easyjdb.read(Mesage.class,"2"),可以從數據庫中讀取主鍵值為2的Message對象。
3、最后一種是關于映射處理引擎參數設置的方法,包括setShowSql、setEnableCache、setAutoCommit、setMaxSize等方法。比如setEnableCache方法可以開取或關閉EasyDBO中的緩存。
3.1.2 創建EasyJDB對象實例
為了能夠使用EasyJDB來處理對象到關系數據庫的映射,我們需要在應用程序中取得一個EasyJDB的實例。在EasyDBO中,主要有兩種方法來構造EasyJDB實例。
第一種方法:通過EasyJDB構造子來創建EasyJDB實例
在構造EasyJDB實例的時候,需要提供到少一個可能的數據源作為參數。EasyJDB提供了如下幾個:
EasyJDB(javax.sql.DataSource dataSource)
根據一個數據源來創建一個處理默認方言為MySQL數據類型方言的EasyJDB實例;
EasyJDB(javax.sql.DataSource dataSource, java.lang.String cacheName)
根據一個數據源來創建一個EasyJDB實例,并明確指定是否使用開取緩存功能;
EasyJDB(javax.sql.DataSource dataSource, ISqlQuery sqlQuery)
根據數據源及方言來創建一個EasyJDB實例。
EasyJDB(javax.sql.DataSource dataSource, ISqlQuery sqlQuery, java.lang.String cacheName)
根據數據源、數據庫方言、緩存名稱等參數,創建EasyJDB實例。
EasyJDB(javax.sql.DataSource dataSource, ISqlQuery sqlQuery, java.lang.String cacheName, boolean showSql)
根據數據源、數據庫方言、緩存名稱、是否在后臺回顯EasyDBO生成的sql語句等參數,創建EasyJDB實例。
EasyJDB()
默認構造函數,構造出的實例后需要通過手動設置正確的數據源及數據庫方言,EasyJDB引擎才能正常工作。
下面是幾種正確的構造EasyJDB實例的示例:
BasicDataSource datasource = new BasicDataSource();
//...
EasyJDB easyjdb=new com.easyjf.dbo.EasyJDB(datasource,new com.easyjf.dbo.sql.MSSqlServerQuery());
Message m=(Message)easyjdb.read(Message.class,cid);
EasyJDB easyjdb=new com.easyjf.dbo.EasyJDB(datasource);
easyjdb.setSqlQuery(new MSSqlServerQuery())
Message m=(Message)easyjdb.read(Message.class,cid);
EasyJDB easyjdb=new com.easyjf.dbo.EasyJDB(datasource,new MSSqlServerQuery(),"EasyDBO",true);
Message m=(Message)easyjdb.read(Message.class,cid);
第二種方法,使用EasyJDB的工廠方法創建EasyJDB實例
EasyJDB中有一個名為getInstance的靜態方法,該方法可以直接從EasyJDB的默認配置文件中加載數據源及相關配置屬性,創建一個EasyJDB對象。在只有一個數據庫的情況下,通過在配置文件easyjf-dbo.xml中配置相關的EasyDBO參數,然后使用getInstance方法得到EasyJDB實例。
使用方法:
EasyJDB easyjdb=EasyJDB.getInstance();
Message m=(Message)easyjdb.read(Message.class,cid);
3.1.3 指定映射配置文件
在程序構造完EasyJDB對象以后,可以通過setConfFiles來指定EasyDBO的映射配置文件,這樣EasyDBO會首先加載這些配置文件中的映射關系。如下面代碼所示:
public void testConfigFile()
{
BasicDataSource datasource = new BasicDataSource();
datasource.setDriverClassName("org.gjt.mm.mysql.Driver");
datasource.setUrl("jdbc:mysql://127.0.0.1:3306/easyjf");
datasource.setUsername("root");
datasource.setPassword("mysql");
EasyJDB easyjdb=new EasyJDB(datasource);
java.util.List configFile=new java.util.ArrayList();
configFile.add("/easyjf-dbo.xml");
configFile.add("/easyjf-dbo2.xml");
easyjdb.setConfigFiles(configFile);
System.out.println(easyjdb.getMapping().getMap().size());
}
通過使用easyjdb.setConfigFiles方法,把/easyjf-dbo.xml及/easyjf-dbo2.xml兩個配置文件的內容都加載到EasyJDB引擎中。
3.1.4 指定緩存
也可以構造完EasyJDB引擎后,在程序中手動構造緩存。直接設置EasyJDB的innerCache屬性值,然后再把enableCache設置成true即可。innerCache是一個DBOCache類型,DBOCache中持有一個ICache作為真正的Cache。因此,程序中指定緩存的步驟如下:
public void testCustomCache()
{
EasyJDB easyjdb=new EasyJDB(datasource);
com.easyjf.cache.ICache cache=new com.easyjf.cache.EasyCache();
com.easyjf.dbo.DboCache dcache=new com.easyjf.dbo.DboCache();
dcache.setCache(cache);
easyjdb.setInnerCache(dcache);
easyjdb.setEnableCache(true);
easyjdb.setShowSql(true);
easyjdb.setConfigFiles(configFile);
for(int i=0;i<5;i++)
{
easyjdb.query(Message.class,"1=1");
} ??
}
3.2 數據源設置
在實際應用中,一個EasyJDB實例與一個數據源進行綁定,這里指的EasyJDB的數據源也即javax.sql.DataSource的一個實例。要能正確使用EasyDBO訪問數據庫,必須有一個有效的數據源。這些數據源可以是一個普通的Apache DBCP數據庫連接池,也可以是一個存在其它容器中的JNDI數據源,還可以是用戶自己實現的數據庫連接池。
例1、使用Apache DBCP連接池數據源
import org.apache.commons.dbcp.BasicDataSource;
BasicDataSource mssql = new BasicDataSource();
mssql.setDriverClassName("net.sourceforge.jtds.jdbc.Driver");
mssql.setUrl("jdbc:jtds:sqlserver://127.0.0.1:1433;DatabaseName=easyjf;SelectMethod=cursor");
mssql.setUsername("easyjf");
mssql.setPassword("easyjf");EasyJDB easyjdb=new EasyJDB();
easyjdb.setDataSource(mssql);
easyjdb.setSqlQuery(new MSSqlServerQuery());
例2、使用JNDI數據源
Context ctx=null;
ctx=new InitialContext();
DataSource ds=(DataSource)ctx.lookup("java:comp/env/jdbc/easyjf");
EasyJDB easyjdb=new EasyJDB(ds,new MSSqlServerQuery());
3.3數據庫查詢方言
由于每一種數據庫的SQL語句及語法存在一定的差異。因此,為了能同時兼容不同的數據庫,EasyDBO中專門設計了一個方言類來支持不同的數據庫。在構造EasyJDB實例的時候,需要針對不同的數據庫,設置與其相對應的數據查詢方言,EasyJDB才能完全正常地工作。默認情況下,EasyJDB使用My SQL方言MySqlQuery作為EasyDBO的方言。
方言是通過com.easyjf.dbo.sql.ISqlQuery接口定義的,若要支持不同的數據庫,則用戶可以根據該接口實現自己的方言即可。
當前,我們已經提供了MSSqlServerQuery、MySqlQuery、OracleQuery等幾種方言,分別用于支持MS SQL(ACCESS)、MY SQL、Oracle等幾種不同類型的數據庫。
設置EasyJDB的引擎方言的方法如下:
1、直接在構造函數指定使用的方言
EasyJDB easyjdb=new EasyJDB(datasource,new MSSqlServerQuery());
2、使用EasyJDB類的setSqlQuery設置查詢方言
EasyJDB easyjdb=new EasyJDB(dataSource);
easyjdb.setSqlQuery(new MSSqlServerQuery());
3、在easyjf-dbo.xml文件中配置方言,然后使用getInstance方法取得EasyJDB實例。
easyjf-dbo.xml中配置方言
<property name="easydbo.dialect">com.easyjf.dbo.sql.MSSqlServerQuery</property>
EasyJDB easyjdb=EasyJDB.getInstance();
3.4 實(域)對象PO及泛對象FO
在EasyDBO中,關系表中的數據可以跟映射成兩種類型的對象,一種是實對象,也即經常所說的域對象(DOM)或程序對象PO。比如,一個User表與一個User類映射,一個Message表與Message類映射,這里的User及Message對象我們都稱為域對象或程序對象PO。
泛對象,也即類型為EasyDBO中的DBObject類型的對象。凡是不能直接映射到某一個域對象的,都可以在查詢的時候,直接映射到DBObject。DBOject類是EasyDBO的一個核心類,用于代表關系數據表中的數據,其使用一個Map來存放查詢到的數據。
在實際應用中,能直接映射成域對象的,我們都盡量使用域對象。若數據結構比較復雜、涉及多個相在關聯的比較隨意的查詢時,我們就可以使用泛對象來輕松簡單表示映射數據。
比如:select a.cid as cid,b.title as title,c.userName as userName from IDS?? a,Message b,User c inner join.....
3.5 使用接口實現簡單映射
為了使User類能支持對象-關系映射,我們需要根據User類屬性在相應的對象-關系映射中間件產品中作一些配置。用過Hibernate的都知道,在hibernate中需要配置hibernate.cfg.xml文件及與User類相應的User.hbm.xml文件來定義映射關系。
在EasyDBO中,可以通過簡單的實現EasyDBO中的com.easyjf.dbo.IObject接口,達到最簡單的對象-關系映射操作。接口映射建立在下面的假設,一個對象對應一個表,并且對象的屬性于表字段名也一一對應。
IObject接口的內容如下:
package com.easyjf.dbo;
public interface IObject {
String getTableName();
String getKeyField();
String getKeyGenerator();
}
其中getTableName()方法可以得到對象對應的關系表名稱;
getKeyField()方法返回關系中標識對象ID的主鍵字段名;
getKeyGenerator()方法返回對象標識ID的生成處理器。
下面是實現了IObject接口的User類,并提供了可以把對象User通過EasyDBO持久化到關系數據表中的方法。擴展后的User.java全部源代碼如下:
package com.easyjweb.business;
import java.util.Date;
import com.easyjf.dbo.IObject;
public class User implements IObject ,Serializable {
private String cid;
private String userName;
private String password;
private String email;
private String tel;
private Date birthday;
private String intro;
public String getTableName() {
return "UserTable";
}
public String getKeyField() {
return "cid";
}
public String getKeyGenerator() {
return "com.easyjf.dbo.RandomIdGenerator";
}
public String getCid() {
return cid;
}
public void setCid(String cid) {
this.cid = cid;
}
//..
//其它getter及setter方法
}
從上面的代碼可以看出,我們可以看出,我們User類在關系表中對應的表名為User,主鍵字段是cid,主鍵生成算法是com.easyjf.dbo.RandomIdGenerator類,生成的是一個16位的隨機字符串。
使用接口映射方式可以實現最簡單的、最常用的單表映射,在一些以數據表為主的在系統或很多中小型的數據庫應用中,由于表之間的關系不復雜,可以使用這種方式來實現映射,不需要寫配置文件,非常實用及方便。
由于只在接口中定義了表的名稱、主鍵字段及主鍵生成器三項信息。因此無法進行諸如有選擇性的緩遲加載,也無法定義表之間的關系等。因此,對于關系比較復雜的表,映射成對象的時候需要用定義1對1、1對N、N對M等多種關系,還有可能需要配置一些字段使用緩遲加載等,而且接口定義要求屬性的名稱與數據表字段的名稱一一對應,對于表字段名與對象屬性名不相同的時候,這種方法就無法正確工作了。
3.6 使用配置文件配置映射關系
配置文件映射是指通過在配置文件中定義對象與關系表的映射關系。在配置文件中,除了定義對象對應的表、表之間的關系、對象屬性與表字段的對應關系以外,還可以定義表一些屬性的緩遲加載,定義1對1、1對N等關系。這是EasyDBO為了滿足比較復雜的對象-關系映射而提供的一種映射方式。
EasyDBO的映射配置文件默認名稱為easyjf-dbo.xml,在前面的Message表的映射中,若改成配置文件映射,則域對象變成一個標識為Serializable的普通JavaBean,不再需要實現IObject接口。其內容如下:
public class Message implements Serializable {
private String cid;
private String title;
private String content;
private String inputUser;
private Date inputTime;
private Boolean publish;
private Integer status;
public String getCid() {
return cid;
}
public void setCid(String cid) {
this.cid = cid;
}
//...
//其它的getter及setter方法
}
在映射配置文件easyjf-dbo.xml中,通過下面的內容來定義對象關系映射。
<class name="com.easyjf.dbo.example.Message" table="Message" schema="dbo" catalog="easyjf" lazy = "false">
<id name="cid" type="string">
<column name="cid" length="16" not-null="true" />
<generator class="com.easyjf.dbo.IdGenerator" />
</id>
<property name="title" type="string">
<column name="title" length="50" not-null="true" />
</property>
<property name="content" type="string">
<column name="content"/>
</property>
<property name="status" type="integer">
<column name="status" length=""/>
</property>
<property name="inputUser" type="string">
<column name="inputUser" length="23" />
</property>
<property name="inputTime" type="date">
<column name="inputTime" length="8" />
</property>
<property name="publish" type="bool">
<column name="publish" length="1" />
</property>
</class>
在配置文件中,使用<class>標簽來定義一個類,在<class>標簽中,name屬性表示域對象PO完整的類名,屬性table表示對應的主表;<id>標簽定義對象的主鍵,<id>標簽下的<generator>標簽定義主鍵生成器;<property>標簽定義對象的屬性,其下的<column>定義屬性對應的表字段等。
在當字段名稱與對象屬性名稱無法一一對應,以及在需要配置比較復雜的關系時,使用該方式可以實現比較復雜、實用的對象-關系映射。
3.7 使用Java5注解來設置映射
由于配置文件的書寫及管理比較復雜,EasyDBO還提供了使用Java5注解的形式來定義對象關系映射。對于Java5以上的版本,我們建議使用該方式來定義映射。其可以實現跟使用配置文件完全相同的功能,滿足復雜關系映射的配置。對于Java5以上的版本,我們建議使用該方式來定義映射。
下面是使用注解來配置映射的例子。上列中的Message類書寫如下所示:
import java.io.Serializable;
import java.util.Date;import com.easyjf.dbo.annotation.*;
@Table(tableName="message")
public class MessageAnnotation implements Serializable{
@TableField(name="cid")
private String cid;
@TableField(name="title")
private String title1;
@TableField(name="content")
private String content1;
@TableField(name="inputUser")
private String inputUser;
@TableField(name="inputTime")
private Date inputTime;
@TableField(name="publish")
private Boolean publish1;
@TableField(name="status")
private Integer status1;
public String getCid() {
return cid;
}
public void setCid(String cid) {
this.cid = cid;
}
//...
//其它的getter及setter方法
}
在上面的域對象PO中,@Table標簽用來定義表所對應的關系表。@TableField標簽用來定義對象屬性所對應的表字段。另外還可以通過@OneToOne標簽、@ManyToOne等標簽來定義1對1、1對多的關系,我們將在后面詳細講述。
3.8 主鍵配置
在EasyDBO中,我們假設每一個表都有一個主鍵,這也是一種實踐證明比較科學的關系表設計方式。有了主鍵,就可以通過主鍵值直接從持久層即數據庫中讀取對象。因此,需要在映射的時候定義對象的主鍵,同時定義對象主建值的生成器。
主鍵的定義通過IObject的String getKeyField()方法,或者是配置文件中的<id>標簽,或者是注解中的@Table標簽的keyField屬性來指定。主鍵值生成器是指在第一次把對象保存到數據庫中時,EasyDBO使用的主鍵生成算法處理器,通過IObject的String getKeyGenerator()方法,或者配置文件中的<generator>標簽,或者是@Table標簽keyGenerator屬性來指定主鍵值生成器。生成器是一個實現了IIdGenerator接口的對象。
IIdGenerator接口的內容如下:
public interface IIdGenerator {
Object generator(Class classType);
Object generator(Class classType, String column);
}
在EasyDBO中,內置了幾種比較常用的主鍵值生成器。一種是用于支持自動增量的NullIdGenerator,另外兩種是用于生成16位隨機字符串的主建值生成器IdGenerator及RandomIdGenerator。用戶可以根據實際的應用需要定義自己的主鍵生成器,我們建議在一套系統中使用統一的主鍵生成器。
3.9 緩存設置
在默認的情況下,EasyDBO不使用緩存。有兩種方法可以開取緩存功能!
方法一:
直接給EasyJDB的innerCache設置一個合法的DBOCache,并把enableCache設置成true,則在使用的過程中就會開取緩存功能。這種方法在基于IOC的配置中比較適用,也可以在自己的程序中手動創建DBOCache對象來設置緩存。代碼如下:
com.easyjf.cache.ICache cache=new com.easyjf.cache.EasyCache();
com.easyjf.dbo.DboCache dcache=new com.easyjf.dbo.DboCache();
dcache.setCache(cache);
easyjdb.setInnerCache(dcache);
easyjdb.setEnableCache(true);
方法二:
若要開取緩存功能,可以在創建EasyJDB實例的時候,設置EasyJDB的cacehName為非null或""即可。若給EasyJDB對象設置了cacheName,則會自動嘗試通過easyjf-cache.xml文件中加載指定配置的cache。也可以在配置文件easyjf-dbo.xml文件中配置中設置是否使用緩存,以及緩存的名稱。
僅僅讓EasyJDB有一個緩存名稱,或把EasyJDB的enableCache屬性設置為true只是表示EasyJDB運行過程中,將嘗試使用緩存,能否真正讓緩存正確工作,還要取決于Cache詳細參數的配置。
Cache的詳細參數配置在另外一個名為easyjf-cache.xml的文件中,要在EasyDBO中使用緩存功能,必須確保該文件正確設置。easyjf-cache.xml文件的內容比較簡單,大致內容如下:
<?xml version="1.0" encoding="utf-8"?>
<easyjf-cache>
<!-- storePolicy主要有LRU、LFU、FIFO三種,備注LRU緩存還有問題-->
<cache name="EasyDBO" storePolicy="LFU" maxElements="50"
expiredInterval="1000" type="com.easyjf.cache.EasyCache" />
</easyjf-cache>
在配置文件中,<cache>標簽用來定義詳細的緩存內容,name表示緩存的名稱,storePolicy屬性定義緩存使用的策略,maxElements定義緩存中的最大元素個數,expiredInterval表示緩存超時的時間,type表示緩存類型。在EasyDBO中,默認是EasyCache。
在easyjf-dbo.xml中配置緩存:
<property name="easydbo.cache_name">EasyDBO</property>
<property name="easydbo.enable_cache">true</property>
在創建EasyJDB的過程中設置緩存:
EasyJDB easyjdb=new EasyJDB(dataSoruce,"EasyDBO");
3.10 一對一映射
我們知道,在基于OO的程序設計中,對象與對象之間存在著繼承、聚合等關聯關系;而在關系表中,表與表之間也存在著一定的關聯關系。因此,在ORM系統中,我們也希望反映這種對象與對象之間的關系。在EasyDBO中,一對一關系映射是通過<one-to-one>標簽或者@OneToOne標簽實現。
下面看一個配置文件:
<class name="com.easyjf.dbo.example.OrderDetail" table="OrderDetail" schema="dbo" catalog="easyjf" lazy = "false">
<id name="cid" type="string">
<column name="cid" length="16" not-null="true" />
<generator class="com.easyjf.dbo.RandomIdGenerator" />
</id>
<one-to-one name="order" type="com.easyjf.dbo.example.Order" column="orderId" key="cid" lazy="true">
</one-to-one>
<one-to-one name="product" type="com.easyjf.dbo.example.Product" column="productId" key="cid">
</one-to-one>
...
</class>
對于<one-to-one>標簽:
name屬性:表示與類本身對應的類在聲明時使用的屬性名,比如order就是在OrderDetail對象中定義的private Order order;
type屬性:表示對應類的類型(Class)
column屬性:表示在類本身的數據表中標示對應類的數據表字段,比如column="orderId"中,order項就是在OrderDetail表中使用orderId字段來對應order表的cid,一般這個是一個外鍵對應。
Lazy屬性:表示這個對象要延遲加載,關于延遲加載請看相關小節介紹。
在上面的配置中,定單詳細信息中包含了兩個一對一關系。即從OrderDetail的角度來看,一個定單詳細信息有一個Order及一個Product以之相對應。因此,我們可以在映射配制中使用<one-to-one>標簽,來配置OrderDetail的order屬性。
而這個時候對于OrderDetail對象來說,在他的bean的定義里面,就會是這樣:
public class OrderDetail {
private String cid;
private Order order;
private Product product;
private Integer num;
private java.math.BigDecimal price;
//…
}
可以直接使用
OrderDetail detail=(OrderDetail)db.get(OrderDetail.class, id);
String orderTitle=detail.getOrder().getTitle();
來訪問order對象了。
這里只是單向一對一的情況,如果要想order和orderDetail對應,就是使用
Order order=(Order)db.get(Order.class, id);
OrderDetail detail=order.getOrderDetail();
來訪問detail對象,實現雙向一對一,那么我們還要在Order一端配置對OrderDetail的映射:
<one-to-one name="orderDetail" type="com.easyjf.dbo.example.OrderDetail" column="orderDetailId" key="cid" lazy="true">
當然,根據我們的經驗,一個order不會只和一個orderDetail對應,這就要使用下面小節介紹的一對多的情況。
另外,若不喜歡使用配置文件,則可以使用Java注解來配置這種關系,如下所示:
@Table(tableName="OrderDetail")
public class OrderDetail {
@TableField(name="cid")
private String cid;
@OneToOne(column="orderId",tableName="",type=Order.class)
private Order order;
@OneToOne(column="productId",tableName="",type=Product.class)
private Product product;
@TableField(name="num")
private Integer num;
@TableField(name="price")
private java.math.BigDecimal price;
public String getCid() {
return cid;
}
public void setCid(String cid) {
this.cid = cid;
}
//...
}
3.11 一對多映射
一對多是指一個類的一個實例與另外一個類多個實例存在著對應關系。比如上面所說的對于一個訂單Order,有多個訂單詳細列表OrderDetail。可以通過配置文件<many-to-one>標簽或使用Java注解@ManyToOne來配置這種關系,如下面是Order對象與OrderDetail的一對多映射的配置文件:
<class name="com.easyjf.dbo.example.Order" table="OrderInfo" schema="dbo" catalog="easyjf" lazy = "false">
<id name="cid" type="string">
<column name="cid" length="16" not-null="true" />
<generator class="com.easyjf.dbo.RandomIdGenerator" />
</id>
<property name="title" type="string">
<column name="title" length="50" not-null="true" />
</property>
<many-to-one name="children" fieldType="java.util.List" type="com.easyjf.dbo.example.OrderDetail" column="orderId" key="cid">
</many-to-one>
</class>
對于<many-to-one>標簽:
Name屬性:一對多中,一一方的類中定義的來保存多一方類的變量名;
fieldType屬性:一對多中,一一方類用來保存多一方類的集合類型。比如這里的ArrayList或者List,
Settype屬性:一對多中,對應的多一方的類的類型(Class)column屬性:一對多中,多一方用來標示一一方的數據表的字段名,比如這里就是在orderDetail表中有一個orderId來對應一個Order對象。
key屬性:一對多中,多一方的主鍵名,這里對應的是orderDetail表中的cid字段。
在這樣的配置下,Order類就會是這樣:
public class Order {
private String cid;
private String title;
private java.math.BigDecimal money;
private List children;
同樣,我們就可以使用:
Order order=(Order)db.get(Order.class, id);
List allItem=order.getChildren();
for(...){
//遍歷所有的OrderDetail操作;
}
//...
若不喜歡寫配置文件的朋友,也可以使用下面Java注解標識的方式:
@Table(tableName="orderInfo")
public class Order {
@TableField(name="cid")
private String cid;
@TableField(name="title")
private String title;
@TableField(name="money")
private java.math.BigDecimal money;
@ManyToOne(column="orderId",tableName="",type=OrderDetail.class)
private List children=new java.util.ArrayList();
public String getCid() {
return cid;
}
public void setCid(String cid) {
this.cid = cid;
}
//......
}
測試代碼:
EasyJDB db=EasyJDB.getInstance();
db.setAutoCommit(false);
Order order=new Order();
order.setTitle("測試");
order.setCid("1111");
java.util.List details=new java.util.ArrayList();
for(int i=0;i<10;i++){
OrderDetail detail=new OrderDetail();
detail.setOrder(order);
detail.setNum(15);
detail.setPrice(new java.math.BigDecimal(16));
details.add(detail);
}
order.setChildren(details);
db.add(order);
db.commit();
在一對多的關系映射中,默認情況下EasyDBO使用了延遲加載功能,若要關掉延遲加載,可以把屬性的lazy設置成false即可。
3.12 多對多映射
在現實對象關系中,還存在多對多關系,也即一個類實例與另一個類的多個實例相互彼此對應。在實際應用中,這種多對多關系很多時候都可以轉換成一對多關系。因此,若能轉換成一對多的時候,我們盡量轉換成一對多來處理。實在不行,可以使用EasyDBO的多對多映射功能,來表示這種關系。在EasyDBO的映射配置中,使用配置文件中的<many-to-many>標簽或Java注解@ManyToMany標簽來配置這種多對多的關系。在多對多關系中,經常要引入一個中間表。下面看一個多對多的實例:
產品的Product表結構
供應商的Provider表結構
存放多對多關系的ProvidrProduct表結構
在上面的所示的關系中,一個Provider可以供應多種產品,同一個產品Product也可以由多個供應商供應。因此,產品Product與供應商Provider之間是多對多的關系。在EasyDBO中,我們通過下面配置文件來表示這種關系!
<class name="com.easyjf.dbo.example.Product" table="product" schema="dbo" catalog="easyjf" lazy = "false">
<id name="cid" type="string">
<column name="cid" length="16" not-null="true" />
<generator class="com.easyjf.dbo.IdGenerator" />
</id>
<many-to-many name="providers" fieldType="java.util.List" type="com.easyjf.dbo.example.Provider" tableName="providerproduct" column="productId" key="cid" tagColumn="providerName" tagKey="userName">
</many-to-many>
...
</class>
<class name="com.easyjf.dbo.example.Provider" table="Provider" schema="dbo" catalog="easyjf" lazy = "false">
<many-to-many name="prdoucts" fieldType="java.util.List" type="com.easyjf.dbo.example.Product" tableName="providerproduct" column="providerName" key="userName" tagColumn="productId" tagKey="cid">
</many-to-many>
...
</class>
在上面的配置文件中:
name-表示存在多對多關系的屬性名;
filedType-表示屬性的類型,即集合或數組等微容器類型;
type-表示關聯對象的實際類型,即微容器中的元素類型;
tableName-表示中間表名;
columan-表示關聯表中跟本對象對應的字段名稱;
key-表示關聯表中代表本對象的屬性名稱;
tagColumn-表示關聯目標對象的字段名稱;
tagKey-表示關系目標對象的屬性名稱。
在以上屬性中,除了filedType可以缺省以外,其它的屬性都是必須設置的。
跟一對一、一對多關系一樣,我們也可以使用Java5注解來表示配置這種第,只要使用@ManyToMany標簽即可,如下所示:
Product.java
import com.easyjf.dbo.annotation.ManyToMany;
import com.easyjf.dbo.annotation.Table;
import com.easyjf.dbo.annotation.TableField;
@Table(tableName="Product")
public class Product {
@TableField(name="cid")
private String cid;
@TableField(name="title")
private String title;
@TableField(name="intro")
private String intro;
@TableField(name="price")
private java.math.BigDecimal price;
@ManyToMany(fieldType=java.util.List.class,type=Provider.class,column="productId",tableName="providerproduct",key="cid",tagColumn="providerName",tagKey="userName")
private java.util.List providers;
@TableField(name="status")
private Integer status;
public String getCid(){
return cid;
}
//...
}
Provider.java
package com.easyjf.dbo.example;
import com.easyjf.dbo.annotation.ManyToMany;
import com.easyjf.dbo.annotation.Table;
import com.easyjf.dbo.annotation.TableField;
@Table(tableName="Provider")
public class Provider {
@TableField(name="userName")
private String userName;
@TableField(name="tel")
private String tel;
@TableField(name="birthday")
private java.util.Date birthday;
@TableField(name="status")
private Integer status;
@ManyToMany(fieldType=java.util.List.class,type=Product.class,column="providerName",tableName="providerproduct",key="userName",tagColumn="productId",tagKey="cid")
private java.util.List products;
public java.util.List getProducts() {
return products;
}
public void setProducts(java.util.List products){
this.products = products;
}
//...
}
測試代碼:
EasyJDB db=EasyJDB.getInstance();
db.setAutoCommit(false);
Provider p=new Provider();
p.setUserName("EasyJF");
java.util.List products=new java.util.ArrayList();
for(int i=0;i<5;i++)
{
Product pro=new Product();
pro.setTitle("產品"+i);
pro.setIntro("好產品啊"+i);
products.add(pro);
}
p.setProducts(products);
db.add(p);
db.commit();
3.13 延遲加載
在基于ORM系統的軟件開發中,我們并不需要每時每刻都加載持久對象的所有屬性,有一些屬性的所占的內存比較大,有一時屬性加載時間也比較長,而我們又并非每次都要用到對象的全部屬性。因此,為了讓用戶能有選擇性的加載持久化對象屬性,EasyDBO提供了一個延遲加載功能,通過這個功能,把一些占用內存比較大或加載時間長的不常用的對象屬性設置成延遲加載,這樣就可以實現持久層對象加載的優化處理,提高了持久化對象的加載速度及效率。
默認情況下,除了一對多,多對多的屬性以外,一個持久層對象其它所有的屬性都是即時加載的。若要把一個對象屬性設置成緩遲加載,需要在持久層對象的映射配置文件或者是注解中進行定義。
在配置文件中定義緩遲加載,直接把屬性映射中的lazy屬性設置成true即可,如下面的示例:
<class name="com.easyjf.dbo.example.OrderDetail" table="OrderDetail" schema="dbo" catalog="easyjf">
<id name="cid" type="string">
<column name="cid" length="16" not-null="true" />
<generator class="com.easyjf.dbo.RandomIdGenerator" />
</id>
<property name="num" type="integer" >
<column name="num" length=""/>
</property>
<property name="price" type="java.math.BigDecimal">
<column name="price" length=""/>
</property>
<one-to-one name="order" type="com.easyjf.dbo.example.Order" column="orderId" key="cid" lazy="true">
</one-to-one>
<one-to-one name="product" type="com.easyjf.dbo.example.Product" column="productId" key="cid">
</one-to-one>
</class>
在上面的示例中,屬性order就是使用了緩遲加載。即當使用EasyJDB的get(Class cls, Object id)來從數據庫中加載一個OrderDetail對象的時候,不會立即加載相應的oder屬性的值,只有當要使用這個持久對象的order屬性的時候,才會從數據庫中加載。
另外,若不喜歡使用煩瑣的配置文件,也可以通過在注解中定義緩遲加載。如下所示:
@Table(tableName="OrderDetail")
public class OrderDetail {
@TableField(name="cid")
private String cid;
@OneToOne(column="orderId",tableName="",type=Order.class,lazy=true)
private Order order;
@OneToOne(column="productId",tableName="",type=Product.class)
private Product product;
@TableField(name="num")
private Integer num;
@TableField(name="price")
private java.math.BigDecimal price;
//....
在上面的OrderDetail類定義中,我們使用@TableField標簽來配制對象映射關系,其中屬性order使用了緩遲加載。
3.14 事務處理
默認情況下,EasyDBO是不支持事務的(自動提交),要使用事務,需要通過把EasyJDB的自動提交標志設置成為false,然后再用commit()來提交數據,最后使用close方法來釋放數據源,這個原理跟JDBC的默認操作是一致的。
示例如下:
EasyJDB db=EasyJDB.getInstance();
db.setAutoCommit(false);
Message m=new Message();
Message m2=new Message();
m.setTitle("標題");
m2.setTitle("標題2");
m.setInputTime(new java.util.Date());
db.add(m);
db.add(m2);
db.commit();
db.close();//釋放數據源
從代碼中可以看到,add(m),add(m2)是在一個事務中,其中任何一條add方法出錯,則都會造成數據加整個提交失敗。
3.15 使用存儲過程
在EasyDBO中,可以直接通過存儲過程名稱及參數調用存儲過程。EasyDBO支持兩種類型的存儲過程,一種是直接對數據庫進行相關操作的存儲過程,如數據更新、數據處理、數據刪除等,這種存儲過程不返回任何結果;另外一種是通過存儲過程返回查詢結果列表的,返回的結果以泛對象的形式存放于List中。
在EasyJDB核心映射處理類中,我們有以下幾個方法用于支持存儲過程,用戶直接調用這些方法即可。這幾個方面分別如下:
java.util.List callQuery(java.lang.String procedureName)
調用指定的存儲過程,并返回一個類型為DBObject的結果列表。
java.util.List callQuery(java.lang.String procedureName, java.lang.Object[] parameter)
根據所給的參數,調用指定的存儲過程,并返回一個類型為DBObject的結果列表。
void callUpdate(java.lang.String procedureName)
執行指定名稱的存儲過程。
void callUpdate(java.lang.String procedureName, java.lang.Object[] parameter)
根據所給的參數,執行指定名稱的存儲過程。
下面是一個簡單列子:
EasyJDB easyjdb=new EasyJDB(dataSource);
List list=easyjdb.callQuery("getPM",new Object[]{"四川省"});
for(int i=0;i<list.size;i++)
{
DBObject obj=(DBObject)list.get(i);
System.out.println(obj.get("title"));
System.out.println(obj.get("num"));
}
其中getPM是一個存儲過程名,這個存儲過程需要給其提供一個地區作為參數。
四、EasyJDB中常用方法介紹
4.1 對象添、刪、改
boolean add(java.lang.Object obj)
把對象obj保存到持久層數據庫中;
boolean update(java.lang.Object obj)
把對象obj的內容更新的持久層數據庫中;
boolean saveOrUpdate(java.lang.Object obj)
把對象obj添加或更新到持久層數據庫中;
boolean del(java.lang.Object obj)
把對象從持久層對象中刪除。
在上面的幾個方法中,參數obj可以是一個域對象DOM(或PO),若是一個域名對象,則EasyDBO會把其相關屬性插入或修改到對應表的相應列中,若對象還包括一對一、一對多或對多多等屬性,則在添加的時候還會在相應的表中插入或修改數據記錄;同理,若是執行刪除操作,若對象包含多對多或者一對多屬性,則還會刪除其下屬的對象。
參數obj也可以是一個泛對象(FO),即類型為DBObject的對象。此時會根據泛對象中定義的表以及列名稱,生成一個對應操作的sql語句,然后執行該sql語句進行數據操作。
上面四個方法,是我們在使用EasyDBO進行數據操作過程中最常用到的方法。saveOrUpdate方法是一個比較靈活的方法,其首先要查找一次系統中是否存在所要操作的對象,若存在則執行update操作,若不存在則執行add操作,在實際的應用需要靈活使用。
除了上面四個方法可以達到數據庫內容的改變以外,還可以通過存儲過程或者是直接使用EasyJDB的execute方法執行數據操作sql語句達到數據庫內容的更改。
4.2 從持久層讀取單個域對象DOM(PO)
java.lang.Object get(java.lang.Class cls, java.lang.Object id)
根據對象類型cls及主鍵值從特久設備中讀讀取對象,若找不到對象則返回null.。這里要求id必須為一個對象類型。
示例:
Message message=(Message)db.get(Message.class,"1111");
java.lang.Object read(java.lang.Class cls, java.lang.String scope)
根據查詢條件從數據表中讀出一個對象,若符合條件的記錄有多個,則該方法只返回第一個對象,其它的將被忽略。
示例:
Message message=(Message)db.get(Message.class,"inputUser='大峽'");
這種查詢方式主要采用拼湊sql語句的方式,由于可能存在sql注入攻擊,因此我們不建議使用。
java.lang.Object read(java.lang.Class cls, java.lang.String scope, java.util.Collection params)
根據查詢條件及具體的參數從數據表中讀出一個對象,若符合條件的記錄有多個,則該方法只返回第一個對象,其它的將被忽略。此處的參數存放于集合類型中,取個數及值與查詢條件scope中的問號"?"號對應。
示例:
java.util.Collection paras=new java.util.ArrayList();
paras.add("大峽");
Message message=(Message)db.get(Message.class,"inputUser=?",paras);
在實際應用中,這種方法可以避免sql注入漏洞攻擊,另外還可以靈活支持jdbc的各種數據類型,因此,我們推薦使用這種方法使用查詢。
4.3從持久層讀取多個域對象DOM(PO)
java.util.List query(java.lang.Class cls, java.lang.String scope)
執行條件查詢,返回類型為cls的對象列表!
示例:
java.util.List list=db.query(Message.class,"inputUser='大峽'");
這個查詢將從持久層數據庫中查出所有輸入人inputUser值為“大峽”的Message對象。同理,在實際應用中,這種方法可以避免sql注入漏洞攻擊,另外還可以靈活支持jdbc的各種數據類型,因此,我們推薦使用這種方法使用查詢。
java.util.List query(java.lang.Class cls, java.lang.String scope, java.util.Collection params)
根據查詢條件及參數執行查詢,返回類型為cls的對象列表。
示例:
java.util.Collection paras=new java.util.ArrayList();
paras.add("大峽");
java.util.List list=db.query(Message.class,"inputUser=?",paras);
這個查詢將從持久層數據庫中查出所有輸入人inputUser值為“大峽”的所有Message對象。
java.util.List query(java.lang.Class cls, java.lang.String scope, java.util.Collection params, int begin, int max)
根據用戶自定義的sql語句執行查詢操作,返回從begin開始,max條記錄,類型為cls的對象列表。在我們只需要加載查詢結果中的幾個對象時,使用該方法將提高處理速度,提升系統性能。
示例:
java.util.Collection paras=new java.util.ArrayList();
paras.add("大峽");
java.util.List list=db.query(Message.class,"inputUser=?",paras,100,10);
查詢輸入人inputUser值為“大峽”對象,從第100條符合的數據開始,最多取10個對象。
java.util.List query(java.lang.Class cls, java.lang.String scope, java.util.Collection params, int begin, int max, boolean cache)
根據用戶自定義的sql語句執行查詢操作,返回從begin開始,max條記錄,類型為cls的對象列表,并把查詢結果存入緩存中。
這個查詢的功能跟上面一樣,只是這個查詢將會把查詢結果存入緩存中,若下次再遇到跟這個一樣的查詢,則直接使用緩存中的查詢結果。
示例:
java.util.Collection paras=new java.util.ArrayList();
paras.add("大峽");
java.util.List list=db.query(Message.class,"inputUser=?",paras,100,10,true);
4.4 從持久層讀取多個泛對象FO(Fan Object)
java.util.List query(java.lang.String sql)
執行用戶自定義sql語句,直接返回泛數據表DBObject對象列表。
java.util.List query(java.lang.String sql, java.util.Collection params)
根據用戶自定義的sql語句及查詢參數執行查詢,返回泛數據表DBObject對象列表
java.util.List query(java.lang.String sql, java.util.Collection params, int begin, int max)
根據用戶自定義的sql語句執行查詢操作,返回從begin開始,max條記錄的泛數據表DBObject對象列表
java.util.List query(java.lang.String sql, java.util.Collection params, int begin, int max, boolean cache)
根據用戶自定義的sql語句執行查詢操作,返回從begin開始,max條記錄的泛數據表DBObject對象列表。
在讀取多個泛對象的查詢中,需要我們提供完整的SQL語句。如select * from Message where inputUser='大峽'。與讀取多個域對象不同的是,由于沒有指定返回結果集的對象類型,因此,查詢出來的結果集合中是類型為DBObject泛對象。其它的參數與前面查詢域對象的參數意義及用法相同。
示例:
java.util.Collection paras=new java.util.ArrayList();
paras.add("大峽");
java.util.List list=db.query("select * from Message where inputUser=?",paras,0,10,true);
for(int i=0;i<list.size();i++)
{
DBObject obj=(DBObject)list.get(i);
System.out.println(obj.get("title"));
}
4.5從持久層返回唯一對象
java.lang.Object uniqueResult(DBTable table, DBField field, java.io.Serializable value)
根據主鍵加載一個表中的某一個列;
這個方法適合用于在知道表配置關系DBTable,DBField的情況下。
java.lang.Object uniqueResult(java.lang.String sql)
執行一條sql語句,返回唯一的對象,不緩存查詢結果。
java.lang.Object uniqueResult(java.lang.String sql, boolean cache)
執行一條sql語句,返回唯一的對象,根據cache的值決定是否緩存結果。
java.lang.Object uniqueResult(java.lang.String sql, java.util.Collection params)
根據自定義sql語句及查詢參數執行查詢,返回唯一的對象,不緩存查詢結果。
java.lang.Object uniqueResult(java.lang.String sql, java.util.Collection params, boolean cache)
根據自定義sql語句及查詢參數執行查詢,返回唯一的對象,根據cache的值決定是否緩存結果。
uniqueResult查詢主要是用來查詢只返回一個對象的sql查詢,這個對象是一般的Java類型如Number、String,或者是表字段對應的類型,而不是域對象DOM(PO)或泛對象FO。uniqueResult中的sql是一條完整的sql語句,適用于查詢數據記錄總數、數據求合、統計結果、查詢數據表中某一行的某一個字段值的情況。
示例:
Number num=(Number)db.uniqueResult("select count(*) from Message");
返回Message表中的記錄數;
java.util.Collection paras=new java.util.ArrayList();
paras.add("1");
String title=(String)db..uniqueResult("select title from Message where cid=?",paras);
返回Message表中cid為"1"的行的title列的值。
4.6 執行自定義sql語句
int execute(java.lang.String sql)
執行指定的sql語句,返回受影響的記錄數。
int execute(java.lang.String sql, java.util.Collection params)
根據參數params中的值,執行指定的sql語句,返回受影響的記錄數。
在實際應用中,我們盡量避免直接使用硬編碼的sql語句。然而,在一些特殊情況下,我們出于性能或其它方面的考慮,需要直接傳于自己的sql語句來操作數據庫,則可以使用EasyJDB提供的execute方法,執行具體的sql語句操作,可以避免我們寫煩瑣的數據源獲取、定義Statement、釋放數據源等JDBC底層操作,大大簡化了編程。
示例:
db.execute("update Message set status=2 where status<1");
4.7 存儲過程
java.util.List callQuery(java.lang.String procedureName)
java.util.List callQuery(java.lang.String procedureName, java.lang.Object[] parameter)
void callUpdate(java.lang.String procedureName)
void callUpdate(java.lang.String procedureName, java.lang.Object[] parameter)
上面幾個方法是通過EasyDBO來調用存儲過程的接口。詳情請參考第三章第15節《關于存儲過程的使用》部分內容。
五、EasyDBO以其它框架的集成運用
5.1 EasyDBO與EasyJWeb集成
EasyDBO是EasyJWeb的一個基礎框架,EasyJWeb之所以能實現快速開發,很大一部分因素是使用了EasyDBO。可以這么說,EasyJWeb的快速數據庫應用開發功能,全是基于EasyDBO,另外EasyJWeb中的代碼生成等也是依賴于EasyDBO。因此,EasyDBO與EasyJWeb可以說是天然集成的。當然,EasyJWeb也可以選擇其它的ORM系統框架。
在EasyJWeb的CRUDAction中,每一個Action都有一個DAO操作對象IDAO,用于充當綜合數據層;IDAO接口的內容如下:
public interface IDAO {
boolean save(Object obj);
boolean update(Object obj);
boolean del(Object obj);
Object get(Class clz, Serializable id);
Object getBy(Class clz, String fieldName, Serializable value);
List query(Class clz, String scope);
List query(Class clz, String scope, Collection paras);
List query(Class clz, String scope, Collection paras, int begin, int max);
Object uniqueResult(String sql);
Object uniqueResult(String sql,Collection paras);
int execute(String sql);
int execute(String sql,Collection paras); // 執行任意SQL語句
}
在EasyJWeb中有一個基于EasyDBO的IDAO實現,類名為EasyDBODAO,其內容大致如下:
public class EasyDBODAO implements IDAO {
private static final EasyDBODAO singleton = new EasyDBODAO();
private EasyJDB db;
public EasyDBODAO() {
}
public EasyDBODAO(EasyJDB db) {
this.db = db;
}
public EasyJDB getDb() {
return db;
}
public void setDb(EasyJDB db) {
this.db = db;
}
public boolean save(Object obj) {
return db.add(obj);
}
public Object get(Class clz, Serializable id) {
return db.get(clz, (Object) id);
}
//...
}
在AbstractCrudAction中,有一個自動加載IDAO對象的方法autoLoadDAO,其中有一個自動把EasyDBO作為數據層處理的代碼,如下:
protected IDAO autoLoadDAO(Module module) {
return EasyDBODAO.getInstance();
}
清楚了這個結構及關系,我們就不難理解在EasyJWeb Tools自代碼生成工具中,生成的一個數據表添刪改查、及分頁顯示的代碼了。下面是一個EasyJWeb示例,由EasyJWeb Tools代碼生成工具生成的CRUD Action。
5.2 EasyDBO與Spring集成
在上面的EasyDBO的簡單介紹中,我們會發現,EasyDBO的構造和配置完全符合構造方法注入或者Setter方法注入的使用,這使得它能和spring完善的結合使用。下面給一個簡單的spring下使用EasyDBO的配置實例。
首先,我們配置一個EasyDBO bean。
先配置一個DataSource:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName">
<value> org.gjt.mm.mysql.Driver </value>
</property>
<property name="url">
<value> jdbc:mysql://localhost:3306/easydbotest </value>
</property>
<property name="username">
<value>root</value>
</property>
<property name="password">
<value>stef</value>
</property>
</bean>
數據源配好了,我們來配置我們需要的EasyDBO bean。如果只是配置最簡單的EasyDBO,只需這樣:
<bean id="easydbo" class="com.easyjf.dbo.EasyJDB" singleton=”true”>
<property name="dataSource" ref="dataSource"></property>
</bean>
因為一個數據庫對應一個EasyDBO,這里使用singleton就可以了。現在,我們的EasyDBO已經是可用的了,來做了測試,這里我們就使用了前面的Message那個例子:
Message類不變,編寫一個簡單的業務接口如下:
public interface IMessageService {
boolean add(Message message);
boolean del(Message message);
boolean update(Message message);
List getAllMessages();
}
然后就是這個簡單業務接口的實現:
public class MessageService implements IMessageService {
private EasyJDB db;
public EasyJDB getDb() {
return db;
}
public void setDb(EasyJDB db) {
this.db = db;
}
public boolean add(Message message){
return this.getDb().add(message);
}
public boolean del(Message message){
return this.getDb().del(message);
}
public boolean update(Message message){
return this.getDb().update(message);
}
public List getAllMessages(){
return this.getDb().query(Message.class, "1=1");
}
}
接下來就是測試類了:
public class MessageTest {
private IMessageService messageService;
public IMessageService getMessageService() {
return messageService;
}
public void setMessageService(IMessageService messageService) {
this.messageService = messageService;
}
public void test(){
Message m=new Message();
m.setTitle("留言標題");
m.setContent("留言內容");
m.setInputTime(new java.util.Date());
m.setInputUser("easyjf");
m.setStatus(new Integer(0));
if(this.getMessageService().add(m)){
System.out.println("成功寫入數據");
}
java.util.List list=this.getMessageService().getAllMessages();
Message m2=(Message)list.get(0);
System.out.println(m2.getTitle());
System.out.println(m2.getContent());
m2.setTitle("新的標題");
if(this.getMessageService().update(m2)){
System.out.println("成功修改數據");
}
}
public static void main(String[] args){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
MessageTest test=(MessageTest)context.getBean("messageTest");
test.test();
}
}
把以上幾個類都在applicationContext.xml文件里面配置好:
<bean name="messageService" class="impl.MessageService">
<property name="db" ref="easydbo"></property>
</bean>
<bean name="messageTest" class="test.MessageTest">
<property name="messageService" ref="messageService"></property>
</bean>
好了,現在可以運行下這個簡單的應用了:結果如下(去掉了spring的眾多debug信息):
成功寫入數據
留言標題
留言內容
成功修改數據
叢上面這個簡單的例子可以看到,EasyDBO和spring可以很簡單的集成,就像使用hibernate,ibaits類似。下面來看一個使用了cache的配置:
<bean id="easydbo" class="com.easyjf.dbo.EasyJDB" singleton="true">
<property name="dataSource" ref="dataSource"></property>
<property name="configFiles">
<list>
<value>/mappings.xml</value>
</list>
</property>
<property name="showSql" value="true"></property>
<property name="enableCache" value="true"></property>
<property name="innerCache" ref="dcache"></property> ???
</bean>
<bean name="dcache" class="com.easyjf.dbo.DboCache">
<property name="cache" ref="easycache"></property>
</bean>
<bean name="easycache" class="com.easyjf.cache.EasyCache" />
在上面的配置片斷中,我們把Message的mapping信息放在了mappings.xml中,所以這時的Message對象只是一個單純的javabean了。另外,我們使用setter注入了一個cache,通過使用showsql=true,和在代碼中的重復查詢操作:
for(int i=0;i<5;i++){
java.util.List list=this.getMessageService().getAllMessages();
Message m2=(Message)list.get(0);
System.out.println(m2.getTitle());
System.out.println(m2.getContent());
}
我們可以看到debug信息:
[main] INFO? com.easyjf.cache.store.MemoryStore? - defaultCache: defaultMemoryStore中找到OBJECT:domain.Message:1
1359 [main] INFO? com.easyjf.cache.store.MemoryStore? - defaultCache: defaultMemoryStore中找到OBJECT:domain.Message:2
Cache已經成功地在運行了。
5.3 在Struts中使用EasyDBO
一般來說,現在的struts都是和spring集成使用的,這種情況下,就使用上面的那個演示即可,還有一種方法就是門面模式,這種情況下,EasyDBO的使用同前面講解得EasyDBO的普通使用方法。如果應用與數據庫交道確實很簡單,那么在這種情況下可以把EasyDBO的一個實例在一個抽象的公共類中提供,而要使用數據庫的action都繼承這個類就可以了。還有一種使用方法就是類似于hibernate為struts提供的plugin一樣,可以在一個plugin中實例化一個EasyDBO的實例,并放在applicationContext或者session里面供action使用。
下面給一個例子,演示把EasyDBO的一個實例在一個抽象的公共類中提供,而要使用數據庫的action都繼承這個類的用法。例子仍然使用message。首先創建一個BaseDaoAction:
public class BaseDaoAction extends Action {
private EasyJDB easyjdb;
protected EasyJDB getEasyjdb() {
if(easyjdb==null){
easyjdb=initJdb();
}
return easyjdb;
}
private EasyJDB initJdb(){
BasicDataSource datasource = new BasicDataSource();
datasource.setDriverClassName("org.gjt.mm.mysql.Driver");
datasource.setUrl("jdbc:mysql://127.0.0.1:3306/easydbotest");
datasource.setUsername("root");
datasource.setPassword("mysql");
com.easyjf.dbo.EasyJDB db=new com.easyjf.dbo.EasyJDB(datasource);
return db;
}
}
然后創建一個ActionForm:
MessageForm:
public class MessageForm extends ActionForm {
private String title;
private String content;
public String getContent() {
return content;
}
//...
最后是用來進行持久化處理MessageAction:
public class MessageAction extends BaseDaoAction {
public ActionForward execute(
ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws Exception {
MessageForm messageform=(MessageForm)form;
Date date=new Date();
String title=messageform.getTitle();
String content=messageform.getContent();
Message message=new Message();
message.setTitle(title);
message.setContent(content);
if(this.getEasyjdb.add(message)){
return mapping.findForward("successed");
}
return mapping.findForward(“faild”);
}
這時的Message的映射方式隨便哪種都行。同樣,像延遲加載等配制都如出一轍了。
六、使用EasyDBO的開源項目介紹
6.1 EasyJF開源Blog系統
EasyJF開源Blog系統是一個由EasyJF開源團隊組織開發的基于Java平臺的開源博客系統。當前Blog已經實現了基本的基本的博客的書寫、流量統計、排名、個人像冊、RSS、支持自定義模板、靜態html文件生成、權限系統、積分系統等功能。另外還將加入博客圈、音樂、專題等功能及更強大的權限系統支持。系統使用基于OO的方法設計,采用多層B/S構架,數據庫持久層使用EasyDBO,Web層使用EasyJWeb框架,java代碼與頁面完全分離,易擴展。歡迎廣大Java開源愛好者下載交流,并請多提寶貴意見及建議!
系統演示:http://blog.easyjf.com
系統SVN:http://svn.easyjf.com/repository/easyjf/easyjfblog/
系統源碼下載:http://dl.easyjf.com/downloads/easyjf/blog/easyjf-blog-0.1.1.zip
6.2 簡易java框架開源論壇系統(最新版本0.5,更新時間10月1日)
簡易java框架開源論壇系統擁有常用論壇系統的基本功能,集前臺后臺代碼為一體,支持UBB。該論壇系統使用基于OO的方法設計,采用多層B/S構架,數據庫持久層主要使用簡易數據庫開源框架EasyDBO,Web層使用EasyJWeb框架,java代碼與頁面完全分離,易擴展。歡迎廣大Java愛好者下載使用。
系統演示:http://ent.easyjf.com
系統SVN:http://svn.easyjf.com/repository/easyjf/easyjfbbs/
系統源碼下載:http://dl.easyjf.com/downloads/easyjf-bbs-0.5.0.zip
6.3 簡易java框架開源訂銷管理系統(最新更新:2006-4-3)
該系統是一個使用Java語言開發,以國內開源Web MVC框架EasyJWeb作系統引擎的Java Web應用系統.系統主要實現的功能有訂單錄入、打印、銷售匯總、原料管理、客戶管理、生產配料計算、報表打印、匯總、系統數據管理及維護等功能,是一個使用非常簡單的編碼方式實現的Web開源應用系統。
系統采用面向對象的設計方法,頁面設計及系統邏輯分離,具有較好的擴展性。系統使用數據庫中間件技術,支持My SQL、MS SQL Server等多種數據庫系統平臺。系統涉及到復雜表單數據提交、AJAX無刷新數據提交、WEB打印等常用應用軟件中涉及到的技術。
系統在線演示地址:http://asp.easyjf.com 用戶名:test 密碼:test
源碼下載:http://www.easyjf.com/download/erp0.1.zip
6.4 EasyJF開源網上會議系統iula-0.1.0(最新更新:2006-7-13)
EasyJF開源網上會議系統iula是一個使用AJAX+EasyJWeb+EasyDBO及多線程技術技術開發的網上信息交流及互動系統,主要供EasyJF開源團隊的成員網上會議使用,會議系統模擬傳統的會議形式,可以同時開設多個不同主題的會議室,每個會議室需要提供訪問權限控制功能,會議中能夠指定會議發言模式(分為排隊發言、自由發言兩種),系統能自動記錄每個會議室的發言信息,可以供參會人員長期查閱。
作為一個開源項目,iula-0.1.0版本當前已經實現了AJAX基本框架搭建,網上文字信息的基本交流等功能,可以作為網上會議或類似信息交流系統的一個基本框架。
系統演示:http://www.easyjf.com/chatRoom.ejf?easyJWebCommand=show
源碼下載:http://dl.easyjf.com/downloads/easyjf-iula-0.1.0.zip
安裝說明:http://www.easyjf.com/html/20060713/2208218216744714.htm
svn地址:http://svn.easyjf.com/repository/easyjf/easyjfiula
更多實用項目介紹,請隨時關注EasyDBO官方網站:www.easyjf.com
七、結束語
EasyDBO從項目發起到現在,已經歷將近7個月了,這期間我們遇到了很多困難,也曾經有很多次放棄的念頭,然而在廣大開源愛好者鼓勵下,終于有了這一次比較大的更新。
感謝與EasyDBO項目組全體成員的辛勤勞動及付出,感謝EasyJF其它項目成員給們做了大量的系統測試工作,更要感謝廣大的開源愛好者對我們的建議及幫助。
當然,由于EasyDBO項目組開發人員技術水平有限,再加上本項目還沒有得到比較徹底的測試與應用實踐,因此這個項目中肯定存在著或多或少的問題。因此,我們非常需要得到廣大開源愛者的關注與支持,歡迎大家給我們的開源作品提出批評或者建議,你所做的一切努力,都將給我們更好地改進和完善框架提供莫大的幫助。
八、聯系我們
總結
- 上一篇: Linux网卡配置(二)网卡配置命令
- 下一篇: 用USB连接两台电脑