JDO示例 - JPOX

来源:互联网 发布:linux node.js 安装 编辑:程序博客网 时间:2024/06/05 14:11
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文档,官方主页有相关链接。
 
原创粉丝点击