利用Hibernate优势加快 SCA 模块实现(五)

日期: 2007-12-13 作者:张俊青 来源:TechTarget中国

  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

官方微博

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社区服务。