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数据存储到数据库中。