ITPub博客

首页 > Linux操作系统 > Linux操作系统 > [转载]如何用JDO开发数据库应用(2)

[转载]如何用JDO开发数据库应用(2)

原创 Linux操作系统 作者:dinner1007 时间:2019-02-27 14:09:05 0 删除 编辑
如何用JDO开发数据库应用(2)
2. JDOGenie是哪路英雄
前面已经提过,JDOGenie是南非的一个商业化产品,可别小看南非人,他们的收入可不少!当然技术也不 错。如果读者做过UML的建模和设计,可能会知道Together Control Center这个产品,也就是最近被Borland收购的一个UML设计工具。JDOGenie的出品公司HemSphere就是Together的南非 总代理和合作伙伴。

说到这里,插句题外话,IBM收购了Rational,Borland收购了Together,Sun收购了DescribeUML,一场IDE+UML的大战又将上演。

好, 书归正传,JDOGenie是我目前最推荐的产品,原因是易学易用,简单上手,对于想学习JDO的朋友是最适合不过的了!它有一个图形界面的配置工具,可 在里面进行数据表映射、SQL操作、JDOQL查询等等功能,非常方便。对采用JDOGenie的Web服务器也可以通过这个图形工具进行监控,可以了解 哪些查询费时,哪些查询执行次数多等等,有助于数据库优化调节。

俗话说,百闻不如一见,下面先给几张图片,过把瘾先:


screen.width-333)this.width=screen.width-333;" src="http://www.java-cn.com/technology/tech_images/1972_001.jpg" alt="按此在新窗口浏览图片" />

控制台界面(WorkBench):

screen.width-333)this.width=screen.width-333;" src="http://www.java-cn.com/technology/tech_images/1972_002.jpg" alt="按此在新窗口浏览图片" />
http: //www.hemtech.co.za/jdo/download.html下载最新版本(本文使用的是1.4.7),然后点击该页面上的 “Obtain Evaluation License”链接获取一个月的试用License(一个月会不会太少了?放心,该公司在快到期时会发布一个新的License的)。在获取试用 License的时候需要填写一些资料。


3. 我们要做什么--需求描述
本节主要描述本文中将要做的应用程序的功能。这个应用程序非常简单,是基于一位网友提出的一个《银行信用卡交易系统》中提取出来的一个功能子集,并作了一定的简化和功能改动,以便能体现JDO的特点,主要完成以下功能:

录入信用卡资料。
信用卡资料包括以下信息:
卡号(自动生成)
持卡人姓名
身份证号
地址
联系电话
开户日期(自动生成)
开户金额
目前余额(自动计算)
允许透支额
浏览信用卡信息
浏览当前所有的信用卡资料
交易刷卡
针对信用卡产生一次交易,需要录入以下信息:
卡号、交易额
如果超出透支额,则提示无法完成交易。
如果交易成功,系统自动记录交易时间
存款
录入卡号和存款额
查询透支报表
查看当前所有透支的信用卡
查询交易明细
可根据持卡人身份证号查询所有的交易信息
以上就是整个应用的功能,非常简单,如果采用JDBC,我们立即会想到先建两个表:信用卡表和交易表,二者通过卡号关联,然后这些功能就是一堆SQL和这两个表组合而成的大杂烩。然而,我们现在要采用JDO来做,怎么做呢?请继续往下看……

4. 开发过程
刚才看上面这一段功能需求的功夫,JDOGenie也应该down下来了,如果还没有的话,感紧安装一个ADSL吧!(别乱猜,我可不是电信的职工!咱只有羡慕的份……)。

采用JDO进行开发的过程大致如下:

先编写原始的数据对象模型代码并编译成为.class文件,这些数据对象称作原始对象(POJO,Plain Old Java Objects)
然后编写存储描述符Metadata,也称元数据,表明一些与存储相关的设置,比如哪些类需要保存到数据库,每个类中的哪些字段需要优先读入,哪些字段延迟读入(LazyLoad)等等,这个metadata必须放到CLASSPATH中,扩展名是“.jdo”。
metadata的编写一般可以通过工具来完成,比如JDOGenie的工作台(WorkBench)就可以轻松地完成。
写 完描述符后,就采用某个JDO产品的增强器(Enhancer)来根据元数据的描述改造编译生成的.class文件,使这些类代码JDO化,然后就可以在 其它代码中调用了。应用代码中主要通过JDO存储管理器(javax.jdo.PersistenceManager,以后简称PM)来完成对数据对象的 增加、删除、修改、查询等操作。
示意图如下(摘自Versant公司的JDO教程):其中的XML Config即是指*.jdo


screen.width-333)this.width=screen.width-333;" src="http://www.java-cn.com/technology/tech_images/1972_003.jpg" alt="按此在新窗口浏览图片" />

4.1. 如何建模
当我们将自己的头脑OO化之后,以上的需求在我们头脑里变成了几个基本的对象:信用卡和交易记录,以及对这些对象进行的一些操作。(其实我们的头脑本来就是基本客观世界的对象的,应该说本来就是OO的,要不然大脑怎么会是一些环成“O”状的肠子呢?:D)

为 简单明了,我们这里的建模也不采用类似ROSE或Together这样的大型的UML工具了。在Duke或Quake中,有时候步枪,甚至是电锯,会成为 最有效最直接的杀人工具,而CS里面手枪也往往出奇制胜。现在,在程序开发的战场上,我们祭出最原始的利器:记事本!相信没有一个人不会使用它。简单点 说,我们下面的内容都直接以源代码作为建模的说明。

首先,我们分析信用卡这个类,很简单,将前面列出的字段作为属性加到类中即可,这和建表的过程其实差不了多少(只是碰到对象之间的关系时,思路会有不同)。

package credit.system;


import java.util.Date;


public class CreditCard {
String name; //姓名
String address; //地址
String idcard; //身份证号
String phone; //电话
Date createTime; //开户日期
Date lastTransactionTime; //最近一次交易的日期
float initialBalance; //开户金额
float balance; //目前余额
float allowOverDraft; //允许透支额

}

咦,好象有什么地方不对劲?不错,你的眼光真犀利!“我搞了这么多年数据库应用开发,从没见过没有关键字的表,也没见过这样一个没有标识字段的类!卡号哪儿去了??!!”

是啊,卡号哪儿去了?没有卡号的信用卡谁敢用?趁早卷铺盖回家吧!

别 急,这里先给大家介绍一下JDO的一个关于对象标识的概念:标识实际上只是一个对象的唯一标记,有点象一个对象的内存地址,对传统的数据库来说,就是一条 记录的主键。JDO认为,如果关键字只是用于标记一个对象的唯一性,而不参与业务逻辑(比如计算),则不必将它放到类代码中,这种唯一性的维护只需要由 JDO中间件(Implementation)去完成,这种对象标识叫做Datastore Identity,一般实现上是使用递增整数;如果标识也参与业务逻辑(如主键是创建时间,会用于排序或范围查找),则可以在类代码中出现,这种对象标识 叫做Application Identity。关于这些概念,请参考本文尾部参考文章中的《JDO对开发的帮助有哪些》一文。

在上面的信用卡类中,我们认为信用卡号只是对信用卡的一个标识,不参与业务逻辑,所以我们采用Datastore Identity的方式,让标识的唯一性由JDO产品去维护,就象对象在内存中的地址不需要我们在程序代码中指定,而是由JVM去维护一样。

咦,好象又有什么地方不对劲?不错,你的眼光还是这么犀利!“你的信用卡没有标识,那我的交易记录怎么去关联它??!!”

对啊,以前我写的JavaBean包装的数据对象,也需要有一个主键属性,另一个对象通过一个同样类型的属性来与这个对象关联,现在你这个主键属性都没了,我怎么去关联呢?无的放矢?

这个问题问得很好,也非常典型(注意,是非常典型,不是“非典型”)。

不 过问这个问题的人,应该都是写过多年数据库应用的富有经验的开发人员,数据表、主键、外键关联的意识已经深入头脑,就算变成Java类,主键外键还是阴魂 不散。这种方式可谓“换汤不换药”,没什么实质的变化,这样的对象模型也不能体现出对象之间的关系,只能通过程序员自己去把握。说实话,我最初也是这样去 做对象包装的,惭愧惭愧,现在让我们步子再大一点,观念再开放一点,看看JDO中的概念吧:对象之间如果有关系的话,只需要直接将关系到的对象声明为一个 该类型的属性(或属性集合)即可。

这样,我们的交易记录类就写成了下面的样子:

package credit.system;

import java.util.Date;

public class TransactionRecord {
Date createTime; //交易发生时间
float amount; //交易金额
String note; //备注

CreditCard card; //信用卡
}

在 这个类中,我们看到里面没有一个“信用卡号”的属性,取而代之的是一个信用卡对象“card”,这样,我们就不会需要在通过交易记录取得相关信用卡的时候 去调用一条查询语句来取得信用卡对象了,只需要简单地读取这个交易记录对象的card属性即可得到。这也是面向对象的便捷性之一。

有了这两个类,我们的《银行信用卡交易系统》的基础也就搭起来了。

4.2. 如果怕出乱子……建议的代码规范
记得以前听过一句话,“世间永恒不变的真理就是不存在永恒不变的真理”,这里,我也想说一句:编程世界里最完美的解决方案就是不要认为有最完美的解决方案。(说什么啊,简单听不懂。呵呵,我自己也有点听不懂)。

我 想说的是:JDO也是有一定的限制的,不能让你完全地展开双翅(注意,是“鱼翅”),在面向对象的大海中遨游。为什么JDO会有限制呢?因为它的原理是将 你的类代码进行一定的改造,将JDO涉及的一些管理和维护代码插入到类代码中,这样,你的调用代码可能需要进行一些改变。这些就是JDO的限制。简单地 说,如果你得到了一个TransactionRecord类型的对象tr,想通过它取得涉及的信用卡对象,不建议通过下面的代码:tr.card得到,而 是建议将这个属性声明为private的,然后给出一个getter来获取(getCard()),也就是进行JavaBean式的属性包装。这样,我们 的两个数据类就会变成下面的样子:

CreditCard.java:


package credit.system;


import java.util.Date;


public class CreditCard {
String name; //姓名
String address; //地址
String idcard; //身份证号
String phone; //电话
Date createTime; //开户日期
Date lastTransactionTime; //最近一次交易的时间
float initialBalance; //开户金额
float balance; //目前余额
float allowOverDraft; //允许透支额

public String toString() { return "信用卡:余额="+balance+",持卡人="+name+",身份证号="+idcard+",电话="+phone; }
public void setName(String value) { name = value; }
public String getName() { return name; }

public void setAddress(String value) { address = value; }
public String getAddress() { return address; }

public void setIdcard(String value) { idcard = value; }
public String getIdcard() { return idcard; }

public void setPhone(String value) { phone = value; }
public String getPhone() { return phone; }

public void setCreateTime(Date value) { createTime = value; }
public Date getCreateTime() { return createTime; }

public void setLastTransactionTime(Date value) { lastTransactionTime = value; }
public Date getLastTransactionTime() { return lastTransactionTime; }

public void setInitialBalance(float value) { initialBalance = value; }
public float getInitialBalance() { return initialBalance; }

public void setBalance(float value) { balance = value; }
public float getBalance() { return balance; }

public void setAllowOverDraft(float value) { allowOverDraft = value; }
public float getAllowOverDraft() { return allowOverDraft; }
}


TransactionRecord.java:


package credit.system;


import java.util.Date;


public class TransactionRecord {
Date createTime; //交易发生时间
float amount; //交易金额
String note; //备注

CreditCard card; //信用卡

public String toString() { return "交易记录:持卡人="+card.name+",身份证号="+card.idcard
+",交易额="+amount+",时间="+createTime; }
public void setCreateTime(Date value) { createTime = value; }
public Date getCreateTime() { return createTime; }

public void setAmount(float value) { amount = value; }
public float getAmount() { return amount; }

public void setNote(String value) { note = value; }
public String getNote() { return note; }

public void setCard(CreditCard value) { card = value; }
public CreditCard getCard() { return card; }
}

实际上,这些增加getter和setter的过程有很多工具可以帮忙,比如JBuilder,或者是Together,甚至是最小而精的免费工具Gel!

这样的采用访问器包装私有属性的建议,一般来说绝大多数Java开发人员都还是可以接受的。

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/374079/viewspace-132079/,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2018-08-23

  • 博文量
    1714
  • 访问量
    1294954