JDO示例 - JPOX

JDO示例 JPOX

(ALin 2006-12-27 00:28)

 
第一部分:相關概念
[JDO的實現版本之一:JPOX]
JPOXJDO的一個具體實現版本。其官方主頁爲:http://www.jpox.org/index.jsp
JDO現在是2.0版,以前的1.0版的Sun官方有具體的實現,不過後來移交給Apache了,所以Apache網站上也有JDO的實現。這種標準的API加上具體的實現的方式在Java相關技術中很常見。下載JPOX1.1.4版。
 
[可持久化類]
JDO規範定義了作爲Java接口的可持久約定,稱之爲PersistenceCapable,同時還定義了類實現必須遵守的編程風格。遵守該約定的類被定義爲是“可持久的”。JOD作爲一種ORM解決方案,使用JDO可以有3種方法生成可持久類:

1.     源代碼生成,即是實現相關的接口,自己編寫相關的方法,在點類似於BMP EJB

2.     源代碼處理。

3.     字節碼增強(使用字節碼增強工具:byte-code enhancer)

JDO要求可持久類必須有一個不帶參數的構造函數,可以是public, protected, private,只要保證定義的類及其潛在的子類能夠訪問即可。
 
[JDO元數據]
JDO無數據即是描述可持久化類與數據庫相互關係的文件,是以jdo爲擴展名XML文件。假如有一個類爲:
alin.scut.jdo.Author,則描述該類的元數據可以在以下文件中:

Ø         package.jdo

Ø         alin/package.jdo

Ø         alin/scut/package.jdo

Ø         alin/scut/jdo/package.jdo

Ø         alin/scut/jdo/Author.jdo

按以上順序搜索元數據,並使用第一個發現的定義。
 
[JDO配置文件]
和其他的ORM框架一樣,JDO也有一個配置文件,具體的實現可能有不同的配置文件。JPOX的配置文件爲:jpox.properties,作用和Hibernatehibernate.cfg.xml文件類似。同時,還應該爲JPOX提供一個Log4j的配置文件,用來輸出日誌。
 
 
第二部分:示例
這個例子只簡單地示範可持久化類的創建、刪除、更新和查詢,不涉及關係部分。當然,爲了直觀起見,照樣不使用IDE,而使用簡單的文本編輯器和相關的命令行工具。
 
一、建立本項目的目錄結構,本項目中一共只有一個可持久類,alin.demo.Author。項目的根文件夾下面一共3個文件夾:bin, src, lib。具體各個目錄放置什麼地球人都知道吧!?
lib目錄下的JAR文件就是JDO2.0的標準APIJPOX的實現,由於使用到Log4j,因此還要把Log4jJAR文件也放到此目錄下。最終的目錄結構如下:
<Project>
lib
jdo.jar(或者叫jdo2-api.jar)
JDO2.0 AIPI
 
jpox.jar
JPOX1.1.4實現
jpox-enhancer.jar
JPOX的字節碼增強器
log4j.jar
Log4j
bcel.jar
(jboss-4.0.2/server/default/lib目錄下面有)
Byte Code Engineering Library
 (BCEL),這是Apache Software
 Foundation Jakarta 項目的一部分。
src
alin/demo/Author.java
 
 
alin/main/…….java
這個包下面的源文件是示例應用程序
 
alin/demo/Author.jdo
 
jpox.properties
注意數據庫URL後面部分的作用:
SelectMethod=cursor(後面會說明)
log4j.properties
 
bin
alin/demo/Author.class
 
 
alin/main/…….class
 
alin/demo/Author.jdo
這些文件都是直接從src目錄下面複製過來的。
jpox.properties
log4j.properties
 
二、各文件具體內容:
(1) 可持久類Author.java

代碼1 Author.java

// Author.java -- Demostrate JDO.
// 2006-11-25 20:55
 
package alin.demo;
 
//import javax.jdo.spi.PersistenceCapable;
//import javax.jdo.JDOHelper;
public class Author {

   private int books;

   private String name;

  

   public Author(String name, int books) {

       this.name = name;

       this.books = books;

   }
  

   protected Author() {

   }
 

   public String getName() {

       return name;

   }
 

   public void setName(String name) {

       this.name = name;

   }
 

   public int getBooks() {

       return books;

   }
 

   public void setBooks(int books) {

       this.books = books;

   }
}
 
(2) 元數據文件
代碼2Author.jdo

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE jdo PUBLIC

    "-//Sun Microsystems, Inc.//DTD Java Data Objects Metadata 2.0//EN"

    "http://java.sun.com/dtd/jdo_2_0.dtd">

<jdo>

   <package name="alin.demo">

       <class name="Author" identity-type="datastore">

           <field name="books" persistence-modifier="persistent">

            </field>

           <field name="name" persistence-modifier="persistent">

                <column length="50" jdbc-type="VARCHAR"/>

            </field>

       </class>
   </package>
</jdo>
 
(3)JPOX配置文件,注意這個屬性:javax.jdo.PersistenceManagerFactoryClass,代表的是JDOPersistenceManagerFactory的具體實現,這個值依賴於具體的實現。前面的一組屬性是JDO的標準屬性,任何實現都必須支持的,後面的是JPOX的擴展屬性。
代碼3jpox.properties
# jpox.properties -- jpox configuration.
# 2006-11-28 19:24
 
# -- Standard JDO properties. --
javax.jdo.PersistenceManagerFactoryClass=org.jpox.PersistenceManagerFactoryImpl
javax.jdo.option.ConnectionDriverName=com.microsoft.jdbc.sqlserver.SQLServerDriver
javax.jdo.option.ConnectionURL=jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=JDO;SelectMethod=cursor
javax.jdo.option.ConnectionUserName=sa
javax.jdo.option.ConnectionPassword=sqlserversaps
 
# -- JPOX properties. --
org.jpox.autoCreateSchema=true
org.jpox.validateTables=false
org.jpox.validateConstraints=false
 
(4) log4j配置文件
代碼4log4j.properties
# LOG4J Configuration
# ===================
 
# Basic logging goes to "jpox.log"
log4j.appender.A1=org.apache.log4j.FileAppender
log4j.appender.A1.File=jpox.log
log4j.appender.A1.layout=org.apache.log4j.PatternLayout

log4j.appender.A1.layout.ConversionPattern=%d{HH:mm:ss,SSS} (%t) %-5p [%c] - %m%n

#log4j.appender.A1.Threshold=INFO
 
# Categories

# Each category can be set to a "level", and to direct to an appender

#log4j.rootCategory=INFO, A1
 
log4j.category.JPOX.JDO=DEBUG, A1
log4j.category.JPOX.Cache=DEBUG, A1
log4j.category.JPOX.MetaData=DEBUG, A1
log4j.category.JPOX.General=DEBUG, A1
log4j.category.JPOX.Utility=DEBUG, A1
log4j.category.JPOX.Transaction=DEBUG, A1
log4j.category.JPOX.Store.Poid=DEBUG, A1
 
log4j.category.JPOX.RDBMS=DEBUG, A1

# Commented out sub-categories since the above setting applies to all subcategories

#log4j.category.JPOX.RDBMS.Schema=DEBUG, A1
#log4j.category.JPOX.RDBMS.DDL=DEBUG, A1
#log4j.category.JPOX.RDBMS.SQL=DEBUG, A1
 
log4j.category.JPOX.ClassLoading=INFO, A1
log4j.category.JPOX.Plugin=DEBUG, A1
log4j.category.JPOX.Enhancer=DEBUG, A1
log4j.category.JPOX.SchemaTool=DEBUG, A1
 
log4j.category.JPOX.TEST=DEBUG, A1
 
#
# C3P0 logging
#
log4j.category.com.mchange.v2.c3p0=INFO, A1

log4j.category.com.mchange.v2.resourcepool=INFO, A1

 
#
# Proxool logging
#
log4j.category.org.logicalcobwebs.proxool=INFO,A1
這個文件是個什麼意思我也不太清楚。
 
三、把編譯後的類文件正確放到bin目錄下。進行一下步工作:字節碼增強。JPOX的字節碼增強工具主類爲:org.jpox.enhancer.JPOXEnhancer
使用方法(在項目的bin目錄下運行)

java org.jpox.enhancer.JPOXEnhancer alin/jpox/package.jdo

這裏有個問題了,因爲org.jpox.enhancer.JPOXEnhancer不在標準的Java類庫中,而在jpox-enhancer.jar包中,因此呢要加上cp參數才能正確的運行。我自己直接把lib目錄下面的包放置到了Java2個擴展包目錄(ext目錄,JDK下有一個,JRE下面有一個,兩個都要放才能正確編譯和運行)下面,就可以直接像上面那樣進行字節碼增強。
一切正常的話應該會看到如下的輸出:

JPOX Enhancer completed with success for 2 classes. Consult the log for full details

 
字節碼增強後要進行的工作是:生成數據庫Schema,即創建可持久化類對應的數據庫表。使用如下命令進行(同樣在bin目錄下進行)

java org.jpox.SchemaTool -props jpox.properties -create alin/jpox/package.jdo

 
最終會看到如下的輸出:
代碼5:生成數據庫Schema時的輸出

JPOX SchemaTool (version 1.1.4) : Creation of the schema

 

2006-11-28 19:29:52 org.jpox.util.JDK14Logger info

信息: PersistenceManagerFactory - Vendor: JPOX Version: 1.1.4

2006-11-28 19:29:52 org.jpox.util.JDK14Logger info

信息: PersistenceManagerFactory initialised for datastore URL=jdbc:microsoft:sql

server://localhost:1433;DatabaseName=JDO driver=com.microsoft.jdbc.sqlserver.SQL

ServerDriver userName=sa

2006-11-28 19:29:53 org.jpox.util.JDK14Logger info

信息: ================ DatabaseAdapter ==================

2006-11-28 19:29:53 org.jpox.util.JDK14Logger info

信息: Adapter : org.jpox.store.rdbms.adapter.MSSQLServerAdapter

2006-11-28 19:29:53 org.jpox.util.JDK14Logger info

信息: Datastore : name="Microsoft SQL Server" version="Microsoft SQL Server 200

0 - 8.00.2039 (Intel X86)

        May 3 2005 23:18:38

        Copyright (c) 1988-2003 Microsoft Corporation

        Developer Edition on Windows NT 5.1 (Build 2600: Service Pack 2)

" (major=2000, minor=0, revision=-1)

2006-11-28 19:29:53 org.jpox.util.JDK14Logger info

信息: Driver : name="SQLServer" version="2.2.0022" (major=2, minor=2)

2006-11-28 19:29:53 org.jpox.util.JDK14Logger info

信息: ===================================================

2006-11-28 19:29:53 org.jpox.util.JDK14Logger info

信息: Initialising Catalog "JDO", Schema "" using "None" auto-start option

2006-11-28 19:29:53 org.jpox.util.JDK14Logger info

信息: Catalog "JDO", Schema "" initialised - managing 0 classes

2006-11-28 19:29:53 org.jpox.util.JDK14Logger warn

警告: No manager for annotations was found in the CLASSPATH so all annotations a

re ignored.

2006-11-28 19:29:53 org.jpox.util.JDK14Logger info

信息: Managing Persistence of Class : alin.jpox.Product [Table : PRODUCT, Inheri

tanceStrategy : new-table]

2006-11-28 19:29:53 org.jpox.util.JDK14Logger info

信息: Managing Persistence of Class : alin.jpox.Book [Table : BOOK, InheritanceS

trategy : new-table]

2006-11-28 19:29:53 org.jpox.util.JDK14Logger info

信息: Creating table BOOK

2006-11-28 19:29:53 org.jpox.util.JDK14Logger info

信息: Creating table PRODUCT

2006-11-28 19:29:54 org.jpox.util.JDK14Logger info

信息: Validated 1 unique key(s) for table BOOK

2006-11-28 19:29:54 org.jpox.util.JDK14Logger info

信息: Creating foreign key constraint : "BOOK_FK1" in catalog "" schema ""

2006-11-28 19:29:54 org.jpox.util.JDK14Logger info

信息: Validated 1 index(es) for table BOOK

2006-11-28 19:29:54 org.jpox.util.JDK14Logger info

信息: Validated 1 unique key(s) for table PRODUCT

2006-11-28 19:29:54 org.jpox.util.JDK14Logger info

信息: Validated 1 index(es) for table PRODUCT

2006-11-28 19:29:54 org.jpox.util.JDK14Logger info

信息: SchemaTool completed successfully
SchemaTool completed successfully
 
四、 示例JDO用法。
(1) 持久化一個Author
代碼6MakePersistent.java

// MakePersistent.java -- Create an Author object and persist it.

// 2006-11-29 10:32
 
package alin.main;
 
import javax.jdo.*;
import alin.demo.*;
 

public class MakePersistent {

 
   public static void main(String[] args) {
       PersistenceManagerFactory pmf =
           JDOHelper.getPersistenceManagerFactory("jpox.properties");

       PersistenceManager pm = pmf.getPersistenceManager();

       Transaction tx = pm.currentTransaction();
       tx.begin();

       Author au = new Author("Eric Ben", 5);

       System.out.println("Author: " + au.getName() + "/t" + au.getBooks()

               + " Books");
      
       pm.makePersistent(au);
       tx.commit();
//     tx.rollback();
      

//     Can not read fields outside of transactions. Or set:

//     pmf.setNontransactionalRead(true);

//     System.out.println("Author: " + au.getName() + "/t" + au.getBooks()

//             + " Books");
       tx.begin();
       String name = au.getName();

       System.out.println("Author: " + name);

       tx.commit();
      
       pm.close();
       pmf.close();
   }
}
 
所有的JDO應用程序基本的代碼框架應該像下面這個樣子:

// MakePersistent.java -- Create an Author object and persist it.

// 2006-11-29 10:32
 
package alin.main;
 
import javax.jdo.*;
import alin.demo.*;
 
public class MakePersistent {
 

    public static void main(String[] args) {

        PersistenceManagerFactory pmf =

            JDOHelper.getPersistenceManagerFactory("jpox.properties");

        PersistenceManager pm = pmf.getPersistenceManager();

        Transaction tx = pm.currentTransaction();

        tx.begin();

        // Do other things.

        tx.commit();
//      tx.rollback();
       
        pm.close();
        pmf.close();
    }
}
 
通過JDOHelper獲得一個PersistenceManagerFactory(PMF),提供配置文件即可,可以參考具體API文檔查看相關的方法。再通過PMF得到PersistenceManager(PM)。只有在事務提交後纔會真正寫入到數據庫中,如果事務回滾,則會丟棄所做的更改。
 
 
(2) 刪除指定名字的第一個Author
代碼7DeleteExample.java
// DeleteExample.java -- Delete an Author.
// 2006-11-29 16:26
 
package alin.main;
 
import java.util.Collection;
 
import javax.jdo.JDOHelper;
//import javax.jdo.JDOUserException;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.Query;
import javax.jdo.Transaction;
 
import alin.demo.Author;
 
public class DeleteExample {
 

   public static void main(String[] args) {

       PersistenceManagerFactory pmf = JDOHelper

               .getPersistenceManagerFactory("jpox.properties");

       PersistenceManager pm = pmf.getPersistenceManager();

       Transaction tx = pm.currentTransaction();

       tx.begin();
 

       Query query = pm.newQuery(Author.class, "name == /"Eric Ben/"");

       Collection result = (Collection)query.execute();

       Author au = null;

       if(result.iterator().hasNext()) {

           au = (Author)(result.iterator().next());

       }
       query.close(result);

       // query.closeAll();

 

       if(au == null) {

           System.out.println("No Author found!");

           System.exit(1);
       }
 
       pm.deletePersistent(au);
      

       // Can npt read fields from deleted objects. So this statement below

       // will throws Runtime Exception: JDOUserException.

       // System.out.println(au.getName());

      

       // If we don't want update, we can use tx.rollback() instead of

       // tx.commit()

       tx.commit();
 
       tx.begin();
      

       String name = au.getName();

       System.out.println("Author: " + name + " |/t" + au.getBooks());

  
       tx.commit();
       pm.close();
       pmf.close();
   }
}
 

Query query = pm.newQuery(Author.class, "name == /"Eric Ben/"");這一句創建了一個查詢,第二個參數是JDO QL語句,具體語法這裏不說了,較多。有的實現可以設置成使用SQL語句。

查詢執行返回的是一個集合,集合爲空表示沒有任何結果。
 
 
(3) 更新示例
代碼8UpdateExample.java
// UpdateExample.java -- Update.
// 2006-11-29 16:11
 
package alin.main;
 
import java.util.Collection;
import javax.jdo.*;
import alin.demo.*;
 
public class UpdateExample {
 

   public static void main(String[] args) {

       PersistenceManagerFactory pmf = JDOHelper

               .getPersistenceManagerFactory("jpox.properties");

       PersistenceManager pm = pmf.getPersistenceManager();

       Transaction tx = pm.currentTransaction();

       tx.begin();
 

       Query query = pm.newQuery(Author.class, "name == /"WeiQin Sun/"");

       Collection result = (Collection) query.execute();

       Author au = null;

       if (result.iterator().hasNext()) {

           au = (Author) (result.iterator().next());

       }
       query.close(result);

       // query.closeAll();

      

       if(au == null) {

           System.out.println("No Author named [WeiQin Sun] found!");

           System.exit(1);
       }
 

       au.setName("Jie Hou");

       // If we don't want update, we can use tx.rollback() instead of

       // tx.commit()

       tx.commit();
 
       tx.begin();

       String name = au.getName();

       System.out.println("Author: " + name + " |/t" + au.getBooks());

       tx.commit();
      
       pm.close();
       pmf.close();
   }
}
 
 
(4) 查詢示例,JDO中一共可以按照3種方式讀取對象:

Ø         使用導航

Ø         Extent查詢,Extent代表某個可持久類的所有實例(可以包括其子類)

Ø         Query查詢,Query則代表滿足一些條件的實例,如叫某個特定名字

使用導航大致是個什麼意思呢?就是說如果已經在數據庫在查詢得出了一個Author對象,而Author有一個String屬性,對應也有一個對象,我就使用Author對象的引用即可得到String對象。像下面這樣,假設已經查詢取得了author對象。
tx.begin();

String name = author.getName(); // author是導航引用

……
tx.commit();
 
通過2個例子來分別示範ExtentQuery查詢方式。
代碼9ReadExtent.java

// ReadExtent.java -- Read by Extent.

// 2006-11-29 15:56
 
package alin.main;
 

import java.util.Iterator;

 
import javax.jdo.*;
import alin.demo.*;
 

public class ReadExtent {

 
   public static void main(String[] args) {
       PersistenceManagerFactory pmf =
           JDOHelper.getPersistenceManagerFactory("jpox.properties");

       PersistenceManager pm = pmf.getPersistenceManager();

       Transaction tx = pm.currentTransaction();
       tx.begin();
      
       // 參數false表示不包括子類的實例

       Extent extent = pm.getExtent(Author.class, false);

       Iterator itor = extent.iterator();
       Author au;
       while(itor.hasNext()) {
           au = (Author)itor.next();

           System.out.println("Author: " + au.getName() + " |/t"

                   + au.getBooks());
       }
       extent.close(itor);
       tx.commit();
      
       pm.close();
       pmf.close();
   }
}
 
代碼10ReadQuery.java
// ReadQuery.java -- Read by Query.
// 2006-11-29 16:05
 
package alin.main;
 
import java.util.Collection;
import java.util.Iterator;
 
import javax.jdo.*;
import alin.demo.*;
 
public class ReadQuery {
 

   public static void main(String[] args) {

       PersistenceManagerFactory pmf =

           JDOHelper.getPersistenceManagerFactory("jpox.properties");

       PersistenceManager pm = pmf.getPersistenceManager();

       Transaction tx = pm.currentTransaction();

       tx.begin();

       Query query = pm.newQuery(Author.class, "books == 5");

       Collection result = (Collection)query.execute();

       Iterator itor = result.iterator();

       Author au;

       while(itor.hasNext()) {

           au = (Author)itor.next();

           System.out.println("Author: " + au.getName() + " |/t"

                   + au.getBooks());

       }
       query.close(result);
       tx.commit();
      
       pm.close();
       pmf.close();
   }
}
 
 
五、可能錯誤情況:

出現:Can't start a cloned connection while in manual transaction mode

原因一般是當你在一個SQL SERVERJDBC連接上執行多個STATEMENTS的操作,或者是手動事務狀態(AutoCommit=false) 並且使用 direct (SelectMethod=direct) 模式.
Direct 模式是默認的模式.
 
解決辦法:
當你使用手動事務模式時,必須把SelectMethod 屬性的值設置爲 Cursor, 或者是確保在你的連接只有一個STATEMENT操作。
修改url,加入SelectMethod=cursor即可
例:原來爲:
jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=JDO
修改後爲:
jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=JDO;SelectMethod=cursor
 
 
[參考資料]
1. JOD核心技術》,本文的代碼大部分都是參考本書上面的代碼。
2. JPOX文檔,官方主頁有相關鏈接。
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章