O/R Mapping实际开发经验之谈(转)

http://www.host01.com/Print.Html?69311
h

http://www.host01.com/Print.Html?70964

一、概述

     O/R Mapping全称Object Relational Mapping,就是对象关系映射。把对表直接进行的操作,变成对持久化类的属性和方法的直接操作。

 

很多的项目是基于数据库的开发,程序中要大量进行表的增、删、改、查询的工作。

 

     例如下面一段C#代码,从数据库CustomerDemo取表Customer数据:

     string ConnectionString = "data source=WILLIAM;persist security info=True;initial catalog=CustomerDemo;user id=sa;password=sasa";

     SqlConnection theSqlConnection = new SqlConnection(ConnectionString);

     string query = "select CustomerName from Customer where CustomerID=1";

 

     SqlDataAdapter theSqlDataAdapter = new SqlDataAdapter();

     theSqlDataAdapter.SelectCommand = new SqlCommand(query, theSqlConnection);

     DataSet customerDataSet = new DataSet();

 

     theSqlDataAdapter.Fill(customerDataSet);

     DataTable cusomerDataTable = new DataTable();

     cusomerDataTable = customerDataSet.Tables[0];

     //判断数据集是否为空

     if (cusomerDataTable.Rows.Count>0)

     {

         DataRow dr = cusomerDataTable.Rows[0];

         //不进行取数据的是否为空的判断,很容易留下隐患

if (! dr.IsNull("CustomerName"))

         {

              txtCustomerName.Text = dr["CustomerName"].ToString();

         }

     }

 

     theSqlConnection.Close();

 

如果用O/R Mapping对表封装成持久化类,读取表的数据就变为访问持久化类的属性。例如:

   //以下只是一段伪代码的模拟,不同的O/R Mapping技术封装,代码的写法会有不同。

 

   //建立数据连接Connection

   Connection conn = DataSource.GetConnection();

 

   //建立可持久化类工厂

   SessionFactory theSessionFactory = ConfigurationFactory.BuildSessionFactory(conn);

 

   //实例化一个Customer,并取CustomerID=1的对象

   Customer theCustomer = (Customer)theSessionFactory.CreateObject(typeof(Customer),”1”);

 

   //取对象的属性进行赋值

 txtCustomerName.Text = theCustomer.Name;

 

 conn.Close();

 

以上的代码相当的简洁,对表的一些常用的操作,都封装在类里面。O/R Mapping对整个项目的开发都有相当的益处:

1、   程序员

(1)         一般的数据库应用的项目,无非是大量的表、字段的select、insert、delete、edit的操作。这些操作没有多大的技术难度,就是耗时间去实现,还要小心谨慎处理,到处用if语句来进行的判断。把大量重复劳动进行类的封装,提高开发质量和效率是显而易见的。

(2)         O/R Mapping所需要编写的持久化类和对应的XML映射文件,都可以通过工具自动的生成,极大的减少程序的代码量。实际的使用中,大约会减少20%的代码量。

(3)         程序员的重复劳动减少,程序员能有更多的精力放在其它技术方面。

(4)         代码风格统一,源程序清晰简洁,可读性强。程序员的水平高低不一,编码风格各有各的特点,读其他人写的源程序,相信对谁都是一件痛苦的事。而用O/R Mapping封装后,从源头上规范大家的开发风格,读懂和修改别人的源程序会容易很多。

(5)         让程序员在一个良好的面向对象开发的环境中成长,培养他们面向对象开发的思维和编码经验,使面向对象的思想在平时的项目开发中潜移默化。

 

2、   系统分析员

(1)         有利于系统分析员用在系统分析、设计、编码、测试中全部用面向对象来解决。不会再出现分析设计文档写了大量的类,到编码阶段,还是在源程序看到大量的SQL语句满天飞。有了对象的持久化的解决方案,系统分析员能没有后顾之忧,全面用面向对象来设计分析。并且编写出来的UML文档非常明了简洁。

(2)         由于O/R Mapping把表的操作完全封装在类的层次,使表跟源程序耦合性大大的降低。能明显提高项目结构的扩展性和柔韧性,更加容易修改和升级。

(3)         O/R Mapping不是一堆操作表的函数库,还可以把项目中常用的逻辑进行封装复用,如:角色分配、权限控制、用户登录注册等身份认证、树型结构的部门等,都可以支持在数据库级进行组件重用。这种数据库级组件,通过O/R Mapping跟实际的数据库完全隔离,复用性很强。

(4)         能降低一些附属性强的功能模块的开发难度,如聊天室、论坛、购物系统、短信频道等,通过O/R Mapping,源程序不再直接跟数据库打交道,只需要通过更改表的映射文件,就可以把这些功能挂到其它现有的系统上。并减低项目产品化的工作难度。

(5)         数据库和项目源程序耦合性大大的降低后,使项目能更容易的移植到其它数据库中。

 

3、   项目经理

(1)         O/R Mapping的使用并不复杂,无论有没有开发经验,面向对象熟悉与否的,都可以快速上手。程序员不必理会程序中对数据库的操作部分,相对来说可以减少程序员的技术要求(我曾经组织个用O/R Mapping开发的项目,带几个初入行的程序员开发,大约开发了4个月,项目开发完了,那几个程序员还不知道怎样用ADO.NET)。项目经理可以有更大的空间去分配工作。

(2)         项目更容易的实现框架、组件重用和积累,提高开发的质量和效率。

(3)         可以实现部分界面(如ASP、JSP)的源程序自动生成。

 

当然,O/R Mapping只是一个技术的解决方案,缺点和局限性会在文章的最后总结时讲述。

 

二、O/R Mapping的基本框架

     O/R Mapping的重要部分是表与持久化类之间的映射,现在主要有两种方式:

 

一种是单纯的持久化类映射:表与持久化类之间的映射是通过硬编码的方式写成类,编译后运行的。这种方式用起来直观明了,程序员可以控制的部分多,运行速度快。缺点是如果更改表的字段、类型等,需要直接更改类里面的代码,再编译后才能运行。

 

另外的一种是通过XML和持久化类一起来实现映射。持久化类是映射出来的实体类,大部分关于类属性的类型、长度、是否能修改、是否可以插入等,和表字段的类型、长度、是否允许为空等,都通过XML的文件来表达。表的映射关系需要改动时,只需改XML部分,持久化类不需要改动及重新编译。现在流行的是这种方式,它很灵活,耦合性更加低。以下是Grove.Net映射的一段的一段代码:

持久化类:

     public class Customer

{

int CustomerID;

string Name;



[KeyField("CustomerID")]

public int CustomerID

{

get{return this.CustomerID;}

set{this.CustomerID=value;}

}

[DataField("CustomerName")]

public string Name

{

get{return this.Name;}

set{this.Name=value;}

}

}





XML文件的映射部分:

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

<Entity xmlns="http://tempuri.org/Customer.xsd">

<TableName>Customer</TableName>

<OperationTypes>

<OperationType Name="select" />

<OperationType Name="insert" />

<OperationType Name="update" />

<OperationType Name="delete" />

</OperationTypes>

<DataFields>

<DataField Name="CustomerID" MapName="CustomerID" IsKey="true" />

<DataField Name="CustomerName" MapName="Name" IsKey="false" />

</DataFields>

</Entity>

从表初始化一个持久化类的步骤:

1 读取数据库的表 -> 2 在XML文件找出所映射的持久化类,并初始化该类 -> 3 逐个读取表的字段名,在XML中找出所映射的持久化类的属性 -> 4 通过类的反射,把表的字段的数据赋值给类的属性



把一个持久化类的数据保存到表的步骤:

1 通过XML文件,找出持久化类所映射的表 -> 2 通过反射,逐一读取类属性所映射的表字段 -> 3 把属性的值赋值给所映射的表字段 -> 4 保存表的数据到数据库



以上的只是一般的处理步骤,在实际的O/R Mapping中,对其开发语言有技术的优化而不同。 

 

三、现有的O/R Mapping产品介绍

     O/R Mapping已经出现很久了, 产品很多:Java方面的有Hibernatejdo等。.Net的有ObjectSpacesGrove.NetOJB.NetAtomsFrameworkObjectz.NetOPF.Net等。

 

     1Hibernate

     Hibernate是一个java开放源代码的O/R Mapping,它对JDBC进行了轻量级的对象封装,可以非常灵活的用对象编程思维来操纵数据库。现在以一个简单的demo看下Hibernate是怎样使用的:

 

     首先Hibernate需要一个hibernate.cfg.xml配置文件

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

<!DOCTYPE hibernate-configuration PUBLIC"-//Hibernate/Hibernate Configuration DTD//EN"    "http://hibernate.sourceforge.net/hibernate-configuration-2.0.dtd">

<hibernate-configuration>

    <session-factory>

        <!-数据库的配置 -->

         <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/quickstart </property>

         <property name="hibernate.connection.username">dbusername</property>

         <property name="hibernate.connection.password">dbpassword</property>

         <property name="hibernate.connection.pool.size">30</property>

         <property name="hibernate.dialect">net.sf.hibernate.dialect.MySQLDialect</property>

        <!-持久化类映射的XML文件名 -->

        <Mapping resource=" ormappingdemo.hibernate.Customer.xml"/>

    </session-factory>

</hibernate-configuration>

    

一个需要持久化类的表:Customer

CREATE TABLE dbo.Customer (

  customerID int,

  CustomerName varchar(100) NULL,

  )

    

Hibernate需要定义一个Customer.xml映射文件

<?xml version="1.0"?>

<!DOCTYPE hibernate-Mapping

    PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"

    "http://hibernate.sourceforge.net/hibernate-Mapping-2.0.dtd">

 

<hibernate-Mapping>

 

    <!-定义持久化类的类名和所映射的表名 -->

    <class name="ormappingdemo.hibernate.Customer" table="Customer">

 

        <!-定义主键 -->

        <id name="customerID" type="int" unsaved-value="null" >

            <column name="customerID " sql-type="int" not-null="true"/>

 

            <!-主键的生成规则,"increment"表示该主键是自递增,hibernate10多种的主键生成方法-->

            <generator class="increment">

            </generator>

        </id>

         <!-定义类的属性  -->

        <property name="Name">

             <!-所映射的字段,这里可以看出表的字段名可以跟类属性名完全不同 -->

            <column name="CustomerName" sql-type="varchar(100)" not-null="true"/>

        </property>

    </class>

</hibernate-Mapping>

    

Customer.xml里面定义了很多类的属性和表的字段的详细信息,如果有变动的,只需要更改这个xml文件就行。Hibernate定义mapping比较灵活, property也可以不定义property里面的内容,用缺省的形式。

除了这个xml文件,还要有一个持久化类:Customer

package ormappingdemo.hibernate;

 

public class Customer {

 

    private int customerID;

    private String name;

 

    public Customer() {

    }

    //全部的属性都要通过getset方法来访问

    public int getCustomerID() {

        return customerID;

    }

 

    public void setCustomerID(int customerID) {

        this.customerID = customerID;

    }

 

    public String getName() {

        return name;

    }

 

    public void setName(String name) {

        this.name = name;

    }

}

    

Customer是个“瘦”类,持久化类不需要实现什么特别的接口,也不需要从一个特别的持久化父类继承下来。到现在为止,一个Customer的映射的持久化类工作完成,而这部分工作,可以通过Hibernate的一个Schema 生成器的工具自动完成。现在,我们可以使用这个类了:

  //建立一个Session工厂

  SessionFactory sessionFactory =

            new Configuration().configure().buildSessionFactory();

 

  //打开一个Session

  Session session = sessionFactory.openSession();

 

  //开始事务

  Transaction tx = session.beginTransaction();

 

  //初始化一个持久化类

  Customer theCustomer = new Customer();

 

  //赋值

  theCustomer.setName("Karl");

 

  //保存新建的持久化类

  session.save(theCustomer);

 

  //提交事务

  tx.commit();

 

  //关闭Session

  session .close();

    

     这段代码,你看不出任何传统跟数据库打交道的代码,需要的只是用持久化类和几个工厂化的类,就可以实现全部的功能。Hibernate还有一种极其强大的查询语言HQL,看上去很像SQL。但HQL是完全面向对象的。

  Transaction tx = session.beginTransaction();

 

//通过HQL语言进行查询。注意,这里的HQL是对象与属性,不是表和字段。如Name是持久化类的属性,真正所映射的表字段名是CustomerName

Query query = session.createQuery("select Name from Customer as customer where customer.customerID>=:ID");

 

query.setInteger("ID", 1);

 

//用迭代遍历Customer

for (Iterator it = query.iterate(); it.hasNext();) {

    Customer theCustomer = (Customer) it.next();

    out.println("Customer : "  +  theCustomer.getName() );

}

 

tx.commit();

   Hibernate的功能极其强大,结构合理,并且完全开发源代码,不需要license。无论是否用java,仔细研究Hibernate,对学习和开发O/R Mapping都有莫大的益处。

 

2、Castor JDO

     Castor JDO(Java 数据对象 (Java Data Objects))也是一种开放源码的、百分之百 Java 数据绑定框架。

Castor JDO 最早发布于 1999 年 12 月,它是第一批可用的开放源码数据绑定框架之一。自那时以来,这项技术已获得了长足的发展。现在,往往将 Castor JDO 同许多其它技术(既有开放源码的,也有商业的)结合使用,以将 Java 对象模型绑定到关系数据库、XML 文档以及 LDAP 目录上。

     同样这一节也是以一个demo来讲一下Castor JDO,

     Castor JDO 叫做 Java 数据对象 同样用了类似于 类似 javabean 的类来存储字据并表示数据之间的关系。

应用程序主要负责设置数据库连接和管理事务处理。数据库的设置是通过一个与映射文件相连接的独立的XML文件来实现的。

看看下面如同Hibernate的sessionFactory一样,Castor JDO 也封装了一个处理数据库资源的类,是org.exolab.castor.jdo.JDO,在里面定义了数据库的名字和属性,并被用来打开一个数据库连接。

我们可以通过使用setConfiguration命令来指定配置文件URL,我们可以装载数据库的配置。使用同一个配置来创建多个JDO对象只会装载配置文件一次。

 

下面的代码片展示了在如何打开一个数据库,并进行一个事务处理,然后关闭数据库的过程。

// 定义JDO对象

jdo = new JDO();

jdo.setDatabaseName( "CustomerDemo" );

jdo.setConfiguration( "database.xml" );

jdo.setClassLoader( getClass().getClassLoader());

// 获取一个新的数据库

db = jdo.getDatabase();

// 开始事务处理

db.begin();

 

// 事务过程

. . .

// 提交事务,关闭数据库

db.commit();

db.close();


...

...
...
(接上)

 

     具体过程如下:

     (1)首先建立数据库配置文件,我们在这里定为database.xml,当然也可以改成是其它名字。

<?xml version="1.0" encoding="gb2312"?>

<database name="CustomerDemo" engine="mysql">

   <driver url="jdbc:mysql://cwb:3306/quickstart" class-name="org.gjt.mm.mysql.Driver">

       <param name="user" value="dbusername"/>

       <param name="password" value="dbpassword "/>

   </driver>

   <mapping href="http://dev.csdn.net/article/Customer.xml/>

</database>

 

     建立影射文件Customer.xml

<?xml version="1.0" encoding="gb2312"?>

   <class name="Demo.Customer" access="shared" identity="customerID">

       <map-to table="users"/>

       <field name="customerID" type="integer">

            <sql name="customerID" type="integer"/>

       </field>

       <field name="name" type="string">

            <sql name="name" type="varchar"/>

       </field>

   </class>

 

     建立持久化类,与hibernate的是一样的类似javabean的类

package Demo;

public class Customer {

  private String name;

  private int customerID;

  public Customer() {

  }

 

  public int getCustomerID() {

    return customerID;

  }

 

  public void setCustomerID(int customerID) {

    this.customerID = customerID;

  }

 

  public String getName() {

    return name;

  }

 

  public void setName(String name) {

    this.name = name;

  }

}

 

     基本的实现后,我们可以看看这个demo怎么运行。

import java.util.*;

import org.exolab.castor.jdo.*;

import java.net.*;

 

public class CustomerManager {

  JDO jdo;

  Database db;

  public CustomerManager() throws DatabaseNotFoundException,

      PersistenceException {

   

    //定义一个JDO对象

    jdo = new JDO();

    jdo.setDatabaseName("CustomerDemo");

    jdo.setConfiguration("database.xml");

    jdo.setClassLoader(getClass().getClassLoader());

   

    //获得连接数据库

    db = jdo.getDatabase();

  }

  /**

   * 用于读取用户

   * @param id Customer 对象的主键

   */

  public Customer loadCustomer(Integer id) throws DatabaseNotFoundException,

      PersistenceException {

  

    Customer result = null;

  

    //开始事务

    db.begin();

    result = (Customer) db.load(Customer.class, id);

   

    //完成事务,关闭数据库

    db.commit();

    db.close();

    return result;

  }

 

  /**

   * 用于建立用户

   * @param Customer newCustomer 新对象

   */

  public void createCustomer(Customer newCustomer) throws

      DatabaseNotFoundException,

      PersistenceException {

   
      Customer result = null;

    db.begin();

   

    //新建Customer

    db.create(newCustomer);

   

    db.commit();

    db.close();

  }

 

  /**

   * 更新旧的对象

   */

  public Customer updateCustomer(Customer updateCustomer) throws

      DatabaseNotFoundException,

      PersistenceException {

   

    db.begin();

   

    //更新Customer

    db.update(updateCustomer);

   

    db.commit();

    db.close();

    return null;

  }

 

  public void removeCustomer(Customer removeCustomer) throws

      DatabaseNotFoundException,

      PersistenceException {

   

    db.begin();

   

    //删除Customer

    db.remove(removeCustomer);

   

    db.commit();

    db.close();

  }

}

 

     Castor JDO对象模型上执行查询

     Castor 实现了对象查询语言(OQL)的 ODMG 3.0 规范的一个子集。OQL 的语法类似于 SQL 的语法,但它却使您能够查询对象模型,而不是直接查询数据库。在支持多个数据库时,这可能是一项强大的功能。Castor OQL 实现在内部将 OQL 查询转换成用于数据库的适当的 SQL。使用 bind() 方法将参数绑定到查询上。以下是 OQL 查询的一些简单示例。

    Castor OQL 实现并不在整个查询中继续使用全限定对象名,相反它支持对象别名的使用。在下面的这些查询中,c 就是这样的一个别名。

如果想要查询以找出所有 Customer,可以执行下列查询:

SELECT c FROM Demo.Customer  c

如果想要查询以找出标识等于 1234 Customer,可以以:

SELECT c FROM Demo.Customer c WHERE c.CustomerID= $1

开始,后跟:

query.bind( 1234 )

 

要查询名称与特殊字符串相似的  Customer,可以执行下列查询:

SELECT c FROM Demo.Customer c WHERE c.name LIKE $1

后跟:

query.bind( "%abcd%" )

 

 

3ObjectSpaces

     ObjectSpaces是微软.Net下面的O/R Mapping,到目前为止还是Beta版,相信会在VS.Net 2004出现正式版。.Net下的O/R Mapping没有像java方面那样的兴旺,开放源码的也不多,OJB. NetAtomsFrameworkOPF.Net等,都有相当的知名度,但还在不断的成熟之中。ADO.Net功能强大,与JDBC有很多不同的地方,所以.Net下的O/R Mapping有很多自己的特色。

 

     现在简单的介绍下ObjectSpaces的用法,大家可以跟HibernateJDO比较一下。

 

     ObjectSpaces同样有一个配置Source.xml文件:

<sources xmlns="http://www.microsoft.com/ObjectSpaces-v1">

 

<!-数据连接的配置-->

<source name="Demo" adapter="sql" connection="Data Source=LocalHost; Integrated Security=SSPI; Database=CustomerDemo"/>

</sources>

    

     每个持久化类也有对应的一个map.xml

<map xmlns="http://www.microsoft.com/ObjectSpaces-v1">

   <type name="Customer" dataSource="customer">

        <property name="customerID" dataSource="customerID"/>

        <property name="Name" dataSource="CustomerName"/>

</type>
</map>

 

     大家有Hibernate上面的例子,相信很容易看得懂这段xml,很多都是大同小异。同样,也需要一个持久化类:

public abstract class Customer

{

    //定义主键

    [UniqueId] public abstract int customerID { get; set; }

    //同样定义属性

public abstract string Name { get; set; }

    public void OnCreate(int newId)

    {

         customerID = newId;

    }

}

  

     使用的例子:

    //装入Source.xml,建立ObjectSpace工厂

    IObjectSpace os = ObjectSpaceFactory.CreateObjectSpace("Source.xml");

   

    //新建一个Customer

    Customer theCustomer = (Customer) os.CreateObject( typeof(Customer), "1" );

    theCustomer.Name = "Karl";

   

    //保存新增的Customer

    os.UpdateAll();

    

     如果需要用数据库保存持久化类,写法有点不同:

    //建立Connection  

    string ConnectionString = "Data Source=localhost;Integrated Security=SSPI;Initial Catalog=CustomerDemo;";

    SqlConnection Connection =  new SqlConnection(ConnectionString);

    SqlDataAdapter theSqlDataAdapter = new SqlDataAdapter("select * from Customer",

                                                    Connection);

    DataSet ds = new DataSet("Customer");

    theSqlDataAdapter.Fill(ds, "Customer");

    //建立一个DataSpace实例

    DataSpace theDataSpace = new DataSpace("Map.xml", ds);

    //DataSpaceName"Karl" Customer.

    Customer theCustomer = (Customer) theDataSpace.GetObject(typeof(Customer), "Name='Karl' ");

 

    //修改Name

    theCustomer.Name = "little karl";

 

     以上简单的介绍了一下HibernateJDOObjectSpaces的使用,要想更加的深入理会,那要好好自己研究下了。

 

       

 

 

 

转载于:https://www.cnblogs.com/lxinxuan/archive/2006/09/06/496335.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值