3.连接到sample数据库并创建所需数据表
在本文所附的StockModule.rar中,在StockModulegensrcmodulestockimplementation路径下包含一个名为 create_db_objects.sql的文件,运行db2cmd并连接到sample数据库上,
db2 connect to sample
然后使用下列命令提交create_db_objects.sql文件来创建所需的表:
db2 –td; -f create_db_objects.sql
项目中还有一个init_db_objects.sql文件来向所创建的表中插入一些测试数据,请执行下列命令:
db2 –td; -f init_db_objects.sql
4.创建Java类
在创建图-2所示的类图的同时,我们已经创建了下列JavaBean:
module.stock.implementation.StockType
module.stock.implementation.StockMemo
module.stock.implementation.StockHolder
module.stock.implementation.StockAccount
module.stock.implementation.Stock
使用WebSphere Integration Developer,程序员能够以模型驱动的方式在建模的过程中创建类图和对应的Java类。
5.创建Hibernate映射文件
Hibernate映射文件定义的是数据库表的字段和JavaBean的属性之间的映射关系,这种映射关系除了一般字段与字段的直接对应关系外,还包括表与表之间的多对一,一对多,多队多的对应关系。Hibernate将根据这些映射文件,自动生成SQL语句,并把从数据库中取得的对象填充到JavaBean的简单属性或复杂属性中。
这一节中,我们将介绍如何为本例中的各个JavaBean构造Hibernate映射文件,并将揭示以下技巧:
如何映射id主键以及id值的生成方法
如何使用数据库提供的sequence对象生成id
如何映射多对一关系
如何定义组合主键映射
创建StockType.hbm.xml – 最一般的情形
StockType是最简单的JavaBean,没有复杂的映射关系,StockType.hbm.xml定义StockType这个JavaBean和数据库表StockType之间的映射关系:
清单-4:stockType表定义
CREATE TABLE stockType(
id VARCHAR(10) NOT NULL,
typeName VARCHAR(20),
CONSTRAINT pk_stockType PRIMARY KEY(id)
);
清单-5:StockType类定义
package module.stock.implementation;
import java.io.Serializable;
public class StockType implements Serializable {
private String id;
private String typeName;
//Getters and setters…
… …
}
清单-6:StockType.hbm.xml定义
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="module.stock.implementation">
<class name="StockType" table="StockType"
lazy="true">
<comment>Stock Type table</comment>
<id name="id">
<generator class="assigned" />
</id>
<property name="typeName"/>
</class>
</hibernate-mapping>
- package: 指明 class name=”StockType”所代表的JavaBean的package路径。
- table: 指明与StockType JavaBean建立映射关系的表名。
- comment: 对此映射文件的注释
- id: 主健定义,必须唯一,如果StockType表中的字段名不是id,则需要用column=”idName”来说明,例如:<id name="id" column="idName">
- generator: 指定主键值的产生算法,assigned为用户指定。可供选择的还有: sequence(使用数据库提供的sequence对象,Oracle, DB2等数据库支持,本例将用到sequence), increment, identity, hilo, seqhilo, uuid, guid, native,select foreign等,关于这些id生成算法的具体情况,请参考Hibernate reference documentation。
- property:指定JavaBean属性与数据表字段之间的映射,如果列名与JavaBean属性名不相同,则需要增加column=”” 来说明。如<property name="typeName" column="name"/>。
创建Stock.hbm.xml – 定义多对一关系
Stock(证券)类有一个stkType属性,类型为StockType(证券类别),一个证券类别可以有多个证券,多个证券可能属于同一个证券类别,所以它们之间的关系是多(Stock)对一(StockType)的关系,我们使用many-to-one来定义这种关系。其他字段直接映射即可。
清单-7:stock表定义
CREATE TABLE stock(
id VARCHAR(10) NOT NULL,
stkName VARCHAR(40),
stkTypeId VARCHAR(10),
CONSTRAINT pk_stock PRIMARY KEY (id)
);
清单-8:Stock类定义-6:StockType.hbm.xml定义
package module.stock.implementation;
import java.io.Serializable;
public class Stock implements Serializable {
private String id;
private String stkName;
private String stkTypeId;
private StockType stkType;
//Getters and setters
……
}
清单-9:Stock.hbm.xml定义
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="module.stock.implementation">
<class name="Stock" table="Stock" lazy="true">
<comment>Stock table</comment>
<id name="id">
<generator class="assigned" />
</id>
<property name="stkName"/>
<property name="stkTypeId" />
<many-to-one name="stkType" class="StockType"
insert="false" update="false">
<column name="stkTypeId"/>
</many-to-one>
</class>
</hibernate-mapping>
-many-to-one:定义stkType属性与StockType类之间的多对一关系。Name为Stock类的stkType属性,class指定该属性的类型, <column>指定在Stock表中与StockType进行连接的属性名,在本例中为stkTypeId。
创建StockHolder.hbm.xml – 使用数据库的sequence
StockHolder存放股东信息,该表中的主键id的值由一个用户定义的sequence产生,为了让Hibernate能够自动调用这个sequence的 nextval获取下一个值,我们必须指定id元素的generator的class为sequence,并且在param中指定sequence的名称,如清单-10所示:
清单-10:StockHolder表定义
CREATE TABLE StockHolder(
id INTEGER NOT NULL,
name VARCHAR(40),
gender CHAR(2),
idCardNum VARCHAR(18),
email VARCHAR(50),
address VARCHAR(100),
CONSTRAINT pk_stockHolder PRIMARY KEY (id)
);
CREATE SEQUENCE stockHolder_seq
START WITH 1005
INCREMENT BY 1
NOMAXVALUE
NOCYCLE
CACHE 24 ;
清单-11:StockHolder类定义
public class StockHolder implements Serializable {
private String id;
private String name;
private String gender;
private String idCardNum;
private String email;
private String address;
//Getters and setters
……
}
清单-12:StockHolder.hbm.xml定义
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="module.stock.implementation">
<class name="StockHolder" table="StockHolder"
lazy="true">
<comment>Stock Holder table</comment>
<id name="id">
<generator class="sequence">
<param name="sequence">stockHolder_seq</param>
</generator>
</id>
<property name="name"/>
<property name="gender"/>
<property name="idCardNum"/>
<property name="email"/>
<property name="address"/>
</class>
</hibernate-mapping>
StockMemo.hbm.xml – 使用identity
StockMemo表存放每个交易日的证券价格信息,StockMemo表的主键id在数据库中是一个identity的自增字段类型,因此在做Hibernate映射时,必须使用<generator class="native" />来对id的generator算法进行定义, native将使得Hibernate在插入数据时,使用数据库内置的identity属性来生成唯一的id值。
清单-13:stockMemo表定义
CREATE TABLE stockMemo(
id INTEGER NOT NULL
GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1),
stockId VARCHAR(10) NOT NULL,
currentPrice DECIMAL(17,3),
highestPrice DECIMAL(17,3),
lowestPrice DECIMAL(17,3),
tradeDate VARCHAR(10) default char(current date),
FOREIGN KEY fk_stockMemeo (stockId)
REFERENCES Stock ON DELETE NO ACTION
);
清单-14:StockMemo类定义
public class StockMemo implements Serializable {
private String id;
private String stockId;
private String currentPrice;
private String highestPrice;
private String tradeDate;
private Stock stock;
//Getters and setters
……
}
StockMemo类还有一个stock属性,类型为Stock,很明显对应于stockId,StockMemo与Stock之间是多对一的关系,因此,我们使用 many-to-one元素来定义stock属性。如清单-13所示:
清单-15:StockMemo.hbm.xml定义
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="module.stock.implementation">
<class name="StockMemo" table="StockMemo"
lazy="true">
<comment>Stock memo table</comment>
<id name="id">
<generator class="native" />
</id>
<property name="stockId"/>
<property name="currentPrice"/>
<property name="highestPrice"/>
<property name="lowestPrice"/>
<property name="tradeDate"/>
<many-to-one name="stock" class="Stock"
insert="false" update="false">
<column name="stockId"/>
</many-to-one>
</class>
</hibernate-mapping>
StockAccount.hbm.xml – 使用compose-id和多个many-to-one映射
StockAccount类比较复杂,对应的Accout表存放的是股东拥有证券的情况,除了要对stockHolderId, stockId, balance, profit等四个一般属性进行映射外,还必须为三个复杂数据类型的属性进行映射。
在定义映射关系之前,需要明确StockAccount与StockHolder,Stock以及StockMemo之间的对应关系:
一个股东可以买卖多个证券,因此StockAccount与StockHolder之间是多对一关系
一个证券可以被多个股东所买卖,因此StockAccount与Stock之间是多对一关系
一个证券在不同的交易日有不同的价格,因此StockMemo与Stock之间是多对一关系
值得注意的是通过StockAccount,我们实际上定义了StockHolder与Stock之间的多对多关系。
清单-16:Account表定义
CREATE TABLE account(
stockHolderId INTEGER NOT NULL,
stockId VARCHAR(10) NOT NULL,
balance INT,
profit DECIMAL(17,3),
FOREIGN KEY fk_account1 (stockHolderId)
REFERENCES StockHolder ON DELETE NO ACTION,
FOREIGN KEY fk_account2 (stockId)
REFERENCES stock ON DELETE NO ACTION
);
对于Account表,stockHolderId和stockId组合可以被视为该表的主键,这一点将使用composite-id元素在Hibernate映射文件中进行表达。
清单-17:StockAccount类定义
public class StockAccount implements Serializable {
private String stockHolderId;
private String stockId;
private int balance;
private float profit;
private StockHolder stockHolder;
private Stock stock;
private StockMemo stockMemo;
//Getters and setters
……
public boolean equals(StockAccount sa){
boolean isEquals=false;
if(sa!=null){
if(stockHolder.getId().equals(sa.getStockHolder().getId()) &&
stockHolder.getIdCardNum().equals(
sa.getStockHolder().getIdCardNum()) &&
stock.getId().equals(sa.getStock().getId()) &&
stock.getStkName().equals(sa.getStock().getStkName())
)
isEquals = true;
}
return isEquals;
}
public int hashCode() {
int h=0;
h = stockHolder.hashCode()+19*stock.hashCode();
return h;
}
}
清单-18:StockAccount.hbm.xml定义
<a name="<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="module.stock.implementation">
<class name="StockAccount" table="Account" lazy="true">
<comment>Stock Account table</comment>
<composite-id >
<key-property name="stockHolderId"/>
<key-property name="stockId"/>
</composite-id>
<many-to-one name="stockHolder" class="StockHolder"
insert="false" update="false">
<column name="stockHolderId"/>
</many-to-one>
<many-to-one name="stock" class="Stock"
insert="false" update="false">
<column name="stockId"/>
</many-to-one>
<many-to-one name="stockMemo" class="StockMemo"
insert="false" update="false">
<column name="stockId"/>
</many-to-one>
<property name="balance"/>
<property name="profit"/>
</class>
</hibernate-mapping>
"></a>
-composite-id定义了一个由stockHolderId和stockId组成的组合主键。和一般id不同,在包含composite-id的持久类中,必须重载equals()和hashCode()这两个方法。如上面的清单-15所示。
-三个many-to-one元素,把stockHolder, stock和stockMemo分别映射到StockHolder类,Stock类和StockMemo类上。
6.实现StockService模块
StockServiceImp是StockService模块的实现类,我们使用Hibernate充当数据访问服务层,通过JavaBean可以很方便的实现底层数据与SDO之间的交换。
图-18 SDO,JavaBean与数据库的交互过程
首先,我们需要创建一个叫做HibernateUtil的类,如清单-17所示,它以Singleton的模式实例化一个SessionFactory,为Hibernate API调用提供基础。在实例化SessionFactory时,指定hibernate.xfg.xml配置文件(见清单-3)。
清单-19: HibernateUtil.java
public class HibernateUtil {
private static final SessionFactory sessionFactory;
static {
try {
// Create the SessionFactory from hibernate.cfg.xml
sessionFactory = new Configuration()
.configure("/gen/src/module/stock/implementation/hibernate.cfg.xml")
.buildSessionFactory();
} catch (Throwable ex) {
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}
然后,实现addStock, addStockType,getStockTypeList等一系列方法。限于篇幅,本文只介绍两个典型的方法 addStockAccount和getStockList的实现,这两个方法的实现包括了实现其他几个方法会涉及到的问题。
1) addStockAccount方法:
该方法的功能是:将StockHolder SDO和Stock SDO以及stock的数量qty等数据存储到account表中,业务上表现为股东StockHolder买进数量为qty的Stock类证券。
输入参数有三个:
DataObject stockHolder, 股东信息
DataObject stock,证券信息
Integer qty,买入的证券数量
我们的方法是把DataObject中的数据转换到JavaBean中,然后再利用Hibernate,把数据持久化到DB中,代码如清单-18所示:
清单-20:addStockAccount的实现代码
public String addStockAccount(DataObject stockHolder, DataObject stock,Integer qty) {
String message = null;
if (stockHolder != null && stock != null) {
try {//得到SessionFactory的实例
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
//这一句是必需的,表明Transaction的开始
session.beginTransaction();
//将输入参数DataObject分别转换为JavaBean
StockHolder sh = convertToStockHolder(stockHolder);
Stock st = convertToStock(stock);
//新建一个StockAccount Bean对象
StockAccount sa = new StockAccount();
//把入参都set进去
sa.setStock(st);
sa.setStockHolder(sh);
sa.setStockHolderId(sh.getId());
sa.setStockId(st.getId());
sa.setBalance(qty.intValue());
sa.setProfit(0); //初始化为0
//调用session.persist()方法,指定StockAccount将被持久化
session.persist("StockAccount", sa);
//调用session.flush()方法将memory中的数据保存到数据库中
session.flush();
//关闭session
session.close();
message = "StockAccount saved successfully!";
} catch (Exception e) {
e.printStackTrace();
message = "Faile to save StockAccount to database: "+ e.getMessage();
}
}
return message;
}
下面对上述代码进行简要说明:
首先,我们把输入参数中的两个DataObject转换为JavaBean:sh(StockHolder)和st(Stock)
对于StockHolder,我们定义以下方法来完成DataObject向JavaBean转化:
清单-21:convertToStockHolder(DataObject stockHolderBO)方法
private StockHolder convertToStockHolder(DataObject stockHolderBO) {
StockHolder stockHolder = null;
if (stockHolderBO != null) {
try {
stockHolder = new StockHolder();
stockHolder.setId(stockHolderBO.getString("id"));
stockHolder.setName(stockHolderBO.getString("name"));
stockHolder.setGender(stockHolderBO.getString("gender"));
stockHolder.setIdCardNum(stockHolderBO.getString("idCardNum"));
stockHolder.setEmail(stockHolderBO.getString("email"));
stockHolder.setAddress(stockHolderBO.getString("address"));
} catch (Exception e) {
e.printStackTrace();
stockHolder = null;
}
}
return stockHolder;
}
同样,为Stock定义一个方法实现DataObject与JavaBean之间的相互转化,这里从略。
然后创建一个StockAccount的实例,并把StockHolder的实例sh和Stock的实例st 放到StockAccount类中。
最后调用Hibernate的Session.persiste等方法将StockAccount数据存储到数据库中。
清单-22所列的代码体现了Hibernate快速实现JavaBean到数据库持久层转化的能力。这里见不到任何与JDBC相关的操作,只是针对 JavaBean的OO操作,因为Hibernate已经为我们完成了底层的JDBC API调用。
清单-22:调用Hibernate API持久化JavaBean
1 Session session = HibernateUtil.getSessionFactory().getCurrentSession();
2 session.beginTransaction();
3 StockAccount sa = new StockAccount();
… sa.setXXX(…)
4 session.persist("StockAccount", sa);
5 session.flush();
6 session.close();
如果StockHolder所包含的股东信息在数据库中不存在,则在调用session.persist(“StockAccount”,sa);之前,需要调用以下方法,先将新的StockHolder信息存到数据库中:
session.persist(“StockHolder”,sh);
可以用下列方法判断StockHolder所包含的股东信息是否已经存在:
if(session.load(StockHolder.class, st.getId()) == null)
2) getStockList方法 – 演绎DB-JavaBean-SDO之路
该方法的功能是:根据股东代码idNum得到属于该股东的所有证券信息的详细列表,如清单-21所示:
清单-23: getStockList(String idNum)定义
public DataObject getStockList(String idNum) {
DataObject stockListBO = null;
DataObject stockBO = null;
ArrayList stockList = new ArrayList();
if (idNum != null) {
try {
stockListBO = boFactory.create("http://StockModule/sdo", "StockList");
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
Iterator iter;
iter = session.createQuery(
"select sa.stock from StockAccount sa where sa.stockHolder.id = ?")
.setString(0, idNum).iterate();
while (iter.hasNext()) {
Stock stock = (Stock) iter.next();
stockBO = convertToStockBO(stock);
stockList.add(stockBO);
}
stockListBO.setList("stocks", stockList);
session.close();
} catch (Exception e) {
e.printStackTrace();
stockListBO = null;
}
}
return stockListBO;
}
-boFactory是com.ibm.websphere.bo.BOFactory的一个实例,在StockServiceImpl的initialize方法中进行实例化, BOFactory是Business Object的实例化工厂,用来创建BO的实例。
7.模块测试
完成StockService的实现后,使用WID提供的Module Test工具对模块进行测试。在Assembly Diagram中,点击StockService 模块右键菜单中的"Test Component",即可打开如图-19所示的模块测试界面,然后选择对应的操作并填写request入参,再点"continue"按钮运行测试。图-20显示了测试结果。
图-19:选择被测试方法并且填写request参数
图-20:测试结果
五、总结
本文结合示例详细探讨了使用Hibernate构建SCA Module持久层的方法,并且在创建示例的过程中,使用了基于模型驱动的设计方法构建SCA Module和Java实现。
本文在使用Hibernate构建SCA Module的持久层时,结合示例探讨了处理O/R映射的各种情况的方法,包括如何正确定义id的 generator,如何定义many-to-one映射关系,如何定义composite-id,如何使用数据库内置的sequence和identity属性等。
在用Java对象实现SCA Module时,使用Hibernate构建SCA的持久层可以解耦SCA Module的Java实现和DBMS类型间的关系:通过修改配置 Hibernate的配置文件(hibernate.cfg.xml),即可与不同类型的数据库进行交互,而不用修改Java代码,这一特性可以使得 SCA Module的松耦合特性进一步加强。
我们一直都在努力坚持原创.......请不要一声不吭,就悄悄拿走。
我原创,你原创,我们的内容世界才会更加精彩!
【所有原创内容版权均属TechTarget,欢迎大家转发分享。但未经授权,严禁任何媒体(平面媒体、网络媒体、自媒体等)以及微信公众号复制、转载、摘编或以其他方式进行使用。】
微信公众号
TechTarget
官方微博
TechTarget中国
作者
相关推荐
-
事件驱动框架和SOA在空军的应用
空军正在利用SOA来改善数据共享,并实时跟踪战机,美国空军机动司令部的Michael Marek解释了企业可从中学习的经验。
-
揭秘New Relic APM技术细节
New Relic应性能管理(APM)套件主要用于Web软件开发。它允许用户在面向服务的架构(SOA)上跟踪关键事务性能,并且支持代码级别的可见性来评估特定代码段和SQL语句对性能的影响
-
仅凭SOA和云无法解决业务数据管理风险问题
SOA和云可以是某些恼人问题高效的解决方案;这一点我们已经知道了。但是也要记住它们并不是所有事情的直接答案,特别是当你的问题是业务数据管理风险,而不是技术问题时。
-
内存数据网格提供商一头扎进Java
10年的时间里,应用性能解决方案提供商Alachisoft一直在用NCache(针对N-Tier和网格计算.NET应用的内存计算和数据网格产品)为.NET社区服务。