posts - 48,  comments - 14,  trackbacks - 0
Struts+Spring+Hibernate 的示例(一)

先说说《Wiring Your Web Application with Open Source Java by Mark Eagle》这个示例,网上有这个的中英文文章。在后续的说明中我会将文章的部分内容引用进来。我使用的开发工具是 Eclipse3.1 + MyEclipse4.0M2。


针对一个简单或者复杂的 Web 应用程序,我们需要考虑诸如是怎样建立用户接口?在哪里处理业务逻辑?怎样持久化的数据?而针对这三个层次,每个层次我们都要仔细考虑:各个层该使用什么技术? 怎样的设计能松散耦合还能灵活改变? 怎样替换某个层而不影响整体构架?应用程序如何做各种级别的业务处理(比如事务处理)?等等。

对于这个示例我们采用当前流行的三种框架来做到 Web 应用程序的松散耦合:表示层我们用 Struts;业务层我们用 Spring;而持久层则用 Hibernate。 

Struts 负责管理用户的请求,做出响应;提供控制器,委派调用业务逻辑;处理异常;UI 验证等。
Spring 负责处理应用程序的业务逻辑和业务校验;管理事务;提供与其它层相互作用的接口;管理业务层级别的对象的依赖等。
Hibernate 负责存储、更新、删除数据库记录等。

这篇文章举例说明如何使用这三个框架整合开发,并揭示一个请求是如何贯穿于各个层的。(从用户的加入一个Order到数据库,显示;进而更新、删除)。 

1. 首先创建一组对象,这些对象有的需要持久化,有的提供业务逻辑,有的是显示接口的设计。Hibernate 允许你将数据库中的信息存放入对象,可以在连接断开的情况下把这些数据显示到UI层。而那些对象也可以返回给持续层,从而在数据库里更新。

使用 myeclipse 的 Web  Project 新建一个项目 SSHTest:

创建 Order 类:

利用 eclipse 生成 Getters 和 Setters。

同样创建 OrderLineItem 类。

com.ivan.ssh.bo.Order 包含一个订单的主要信息:订单号,订单总价,订单客户名。
com.ivan.ssh.bo.OrderLineItem 包含订单的详细信息:订单单项号,单价,描述。一个订单对应于多个订单项。

Order 与 OrderLineItem 的关系是一对多的关系,这里为它们建立双向一对多的关系。
在类中我们需要分别为 Order 和 OrderLineItem 添加属性 orderLineItems(java.util.Set 类型) 和 order(Order 类型)及其 getter 和 setter 方法。

2. 配置持久层,Hibernate 通过 XML 文件来映射 (OR) 对象,以下两个 xml 文件分别映射了Order 和 OrderItem 对象。
Order.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>
    <class
        name="com.meagle.bo.Order"
        table="Orders"
        dynamic-update="false"
        dynamic-insert="false">

        <id
            name="id"
            column="Order_ID"
            type="int"
            unsaved-value="0">
            <generator class="native">
            </generator>
        </id>

        <set
            name="orderLineItems"
            table="OrderLineItem"
            lazy="true"
            inverse="true"
            cascade="save-update"
            sort="unsorted">
              <key column="Order_ID"/>
              <one-to-many class="com.meagle.bo.OrderLineItem"/>
        </set>

        <property
            name="userName"
            type="string"
            update="true"
            insert="true"
            column="UserName"
            not-null="true"
            unique="false"/>
        <property
            name="total"
            type="double"
            update="true"
            insert="true"
            column="Total"
            not-null="false"
            unique="false"/>
    </class>
</hibernate-mapping>

OrderLineItem.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>
    <class
        name="com.meagle.bo.OrderLineItem"
        table="OrderLineItem"
        dynamic-update="false"
        dynamic-insert="false">

        <id
            name="id"
            column="OrderLineItem_ID"
            type="int"
            unsaved-value="0">
            <generator class="native">
            </generator>
        </id>

        <many-to-one
            name="order"
            class="com.meagle.bo.Order"
            cascade="none"
            outer-join="auto"
            update="true"
            insert="true"
            column="Order_ID"/>

        <property
            name="description"
            type="string"
            update="true"
            insert="true"
            column="Description"
            not-null="false"
            unique="false"/>

        <property
            name="lineItemPrice"
            type="double"
            update="true"
            insert="true"
            column="LineItemPrice"
            not-null="false"
            unique="false"/>
    </class>
</hibernate-mapping>

稍后我们介绍怎样配置 SessionFactory 和 Session,前者说明与哪个数据库通信,使用哪个连接池或使用了 DataSource,加载哪些持久对象,后者用来完成查找、保存、删除和更新这些操作。

3. 配置业务层,这里要创建业务服务对象(Business Service Object),用他们来执行程序的逻辑,调用持久层,得到 UI 层的 requests,处理 transactions,并且控制 exceptions。为了将这些连接起来并且易于管理,我们将使用面向方面的 SpringFramework。Spring 提供了控制倒置(Inversion of Control)和注射依赖设置(Setter Dependency Injection) 这些方式(可供选择),用 XML 文件将对象连接起来。IoC 是一个简单概念(它允许一个对象在上层接受其他对象的创建),用 IoC 这种方式让你的对象从创建中释放了出来,降低了偶合度。

我们将用一个 business service object 接收一个 DAO,用它来控制 domain objects 的持久化。 由于在这个例子中使用了 Hibernate,我们可以很方便的用其他持久框架实现同时通知 Spring 有新的 DAO 可以使用了。在面向接口的编程中,你会明白 “注射依赖”模式是怎样松散耦合你的业务逻辑和持久机制的。

public interface IOrderService {
  public abstract Order saveNewOrder(Order order)
     throws OrderException, OrderMinimumAmountException;
  public abstract List findOrderByUser(String user) throws OrderException;
  public abstract Order findOrderById(int id) throws OrderException;
  public abstract void setOrderDAO(IOrderDAO orderDAO);
}

注意到这段代码里有一个 setOrderDao(),它就是一个DAO Object设置方法(注射器)。

其实现类:
public class OrderServiceSpringImpl implements IOrderService {

 private static final double ORDER_MINIMUM = 100.0;

 private IOrderDAO orderDAO;

 public Order saveNewOrder(Order order)
  throws OrderException, OrderMinimumAmountException {

  // do some business logic
  if (order != null && order.getTotal() == 0) {

   double total = 0.0;

   Set items = order.getOrderLineItems();
   Iterator iter = items.iterator();
   while (iter.hasNext()) {
    OrderLineItem item = (OrderLineItem) iter.next();
    total += item.getLineItemPrice();
   }

   if (total < OrderServiceSpringImpl.ORDER_MINIMUM) {
    throw new OrderMinimumAmountException("Order did not exceed the order minimum");
   } else {
    order.setTotal(total);
   }
  }

  Order savedOrder = null;
  try {
   savedOrder = getOrderDAO().saveOrder(order);
  } catch (RuntimeException e) {
   throw new OrderException("Could not save order " + e.toString());
  }

  return savedOrder;
 }

 public List findOrderByUser(String user) throws OrderException {

  List orders = null;
  try {
   orders = getOrderDAO().findOrdersPlaceByUser(user);
  } catch (RuntimeException e) {
   // should really use a logger instead of System.out
   System.out.println(
    "Could not locate order by user " + e.getMessage());
   throw new OrderException(
    "Could not locate order by user " + e.getMessage());
  }
  return orders;
 }

 public Order findOrderById(int id) throws OrderException {

  Order order = null;
  try {
   order = getOrderDAO().findOrderById(id);
  } catch (RuntimeException e) {
   // should really use a logger instead of System.out
   System.out.println(
    "Could not locate order by ID " + e.getMessage());
   throw new OrderException(
    "Could not locate order by ID " + e.getMessage());
  }
  return order;
 }

 public IOrderDAO getOrderDAO() {
  return orderDAO;
 }

 public void setOrderDAO(IOrderDAO orderDAO) {
  this.orderDAO = orderDAO;
 }
}

接下去对 DAO 的实现类进行编码。既然 Spring 已经有对 Hibernate 的支持,那这个例子就直接继承 HibernateDaoSupport类了,这个类很有用,我们可以参考 HibernateTemplate(它主要是针对 HibernateDaoSupport 的一个用法,具体可以查看 Spring 的 API)。

public interface IOrderDAO {
 public  Order findOrderById(final int id);
 public abstract List findOrdersPlaceByUser(final String placedBy);
 public abstract Order saveOrder(final Order order);
}

此接口的实现类:
public class OrderHibernateDAO
 extends HibernateDaoSupport
 implements IOrderDAO {

 public OrderHibernateDAO() {
  super();
 }

 public Order findOrderById(final int id) {

  Order order = (Order) getHibernateTemplate().load(Order.class, new Integer(id));
   
  if(order.getOrderLineItems().size() > 0){
   // collection initialized. 
  }   
  return order;
 }

 public List findOrdersPlaceByUser(final String placedBy) {

  return getHibernateTemplate().executeFind(new HibernateCallback() {
   public Object doInHibernate(Session session)
    throws HibernateException, SQLException {

    StringBuffer sb = new StringBuffer(100);
    sb.append("select distinct order ");
    sb.append("from Order order ");
    sb.append("join order.lineItems lineItems ");
    sb.append("where order.placedBy = :placedBy");

    sb.append("order by order.id");

    Query query = session.createQuery(sb.toString());
    query.setString("placedBy", placedBy);

    List list = query.list();

    return list;
   }
  });
 }

 public Order saveOrder(final Order order) {
  getHibernateTemplate().save(order);
  return order;
 }
文章来源:http://www.javaresearch.org/article/showarticle.jsp?column=23&thread=53684
posted on 2006-08-05 16:12 逍遥草 阅读(416) 评论(0)  编辑 收藏 引用 所属分类: J2EE
只有注册用户登录后才能发表评论。