您的位置:首页 > 编程语言 > Java开发

问题:Spring如何支持Hibernate

2007-12-03 00:00 302 查看
 
6.6  问题:Spring如何支持Hibernate
Spring和Hibernate是目前非常流行的两个开源框架,Hibernate作为本书的重点,其在J2EE开源项目中的里程碑意义更是有目共睹,通过Spring和Hibernate的整合,将给应用程序带来高度的灵活性和强大的功能。设计师L在本节中,将以宠物店购物车中两个领域对象的关联关系展开,逐步介绍Spring集成Hibernate的方法。
6.6.1  ER分析
设计师L观察了一下宠物店购物车中的领域对象,发现有两个很适合本小节演示的领域对象,它们分别是代表商品种类的Category和代表该商品种类下具体产品的Product,这两个领域对象之间的关系相对比较简单,用Hibernate来映射这两个对象实体再恰当不过,于是L首先在宠物店的DB Schema中找到了这两个实体所对应的Table脚本,代码如下:
create table category (
  catid varchar(10) not null,
  name varchar(80) null,
  descn varchar(255) null,
  constraint pk_category primary key (catid)
);
create table product (
    productid varchar(10) not null,
    category varchar(10) not null,
    name varchar(80) null,
    descn varchar(255) null,
    constraint pk_product primary key (productid),
        constraint fk_product_1 foreign key (category)
        references category (catid)
);
有了对领域模型的抽象理解,并且有了现成的Table脚本,这使得L非常容易便画出了相对应的E-R关系图,如图6.3所示。


 
[align=center]图6.3  Category和Product 对象的E-R图[/align]
可以看到,Category和Product之间是一对多的关系。
6.6.2  领域对象映射
在有了以上的ER分析后,L明显感觉到,原来宠物店提供的Category和Product模型,已经不适合作为Hibernate的领域对象,这里假设读者已经熟悉Hibernate的相关概念。于是L动手重写了Category和Product这两个领域对象,主要的改变是在其中加入了一些对象关联代码,见例6.48和例6.49。
例6.48:Category.java
package springhibernate;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
public class Category implements Serializable {
  private String catid;
  private String name;
  private String descn;
  /**
   * 持有Product对象的集合并由Hibernate负责同步数据库
   * 此处还表示了Category和Product间的one-to-many关联
   */
  private Set products = new HashSet();
  public Category() {
  }
  public Category(String catid) {
    this.catid = catid;
  }
  public String getCatid() {
    return this.catid;
  }
 
  public void setCatid(String catid) {
    this.catid = catid;
  }
  public String getName() {
    return this.name;
  }
 
  public void setName(String name) {
    this.name = name;
  }
  public String getDescn() {
    return this.name;
  }
 
  public void setDescn(String descn) {
    this.descn = descn;
  }
  public void addProduct(Product product) {
    //这里表示Product和Category是一个双向关联
    product.setCategory(this);
    //这个方法是一个透明持久化动作,Hibernate Session会
    //同步对象状态和数据库
    this.getProducts().add(product);
  }
}
例6.49:Product.java
package springhibernate;
import java.io.Serializable;
public class Product implements Serializable {
  private String productid;
  private Category category;
  private String name;
  private String descn;
 
  public Product() {
  }
  public Product(String productid) {
    this.setProductid(productid);
  }
 
  public String getProductid(String productid) {
    return this.productid;
  }
 
  public void setProductid(String productid) {
    this.productid = productid;
  }
  public String getName() {
    return this.name;
  }
 
  public void setName(String name) {
    this.name = name;
  }
  public String getDescn() {
    return this.name;
  }
 
  public void setDescn(String descn) {
    this.descn = descn;
  }
  public Category getCategory() {
    return this.category;
  }
 
  public void setCategory(Category category) {
    this.category = category;
  }
}
有了领域对象的实体代码后,还需要给出它们各自的Hibernate映射文件,见例6.50和例6.51。
例6.50:Category.hbm.xml
<?xml version="1.0" encoding='UTF-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping package="springhibernate">
  <class name="Category" table="category">
    <id name="catid" column="catid"/>
    <property name="name" column="name"/>
    <property name="descn" column="descn"/>
    <!-- 映射集合和一对多关联
    cascade="all"代表了级联所有的操作,包括新增、修改或者删除等
    -->
    <set name="products" inverse="true" cascade="all">
      <key column="category" />
      <one-to-many class="Product" />
    </set>
  </class>
</hibernate-mapping>
例6.51:Product.hbm.xml
<?xml version="1.0" encoding='UTF-8'?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping package="springhibernate">
  <class name="Product" table="product">
    <id name="productid" column="productid"/>
    <property name="name" column="name"/>
    <property name="descn" column="descn"/>
    <!-- many-to-one元素,
          这里定义了一种与另一个持久化类的关联,即Product和Category是双向关联-->
    <many-to-one name="category"
                 column="category"
                 class="Category"
                 not-null="true"/>
  </class>
</hibernate-mapping>
至此,领域对象的映射工作就算完成了,在下文中L将通过DAO模式作进一步的封装,并且通过Spring作进一步的整合、简化和完善。
6.6.3  使用Spring简化DAO
和上文中所举的JDBC DAO辅助类JdbcDaoSupport以及IBatis DAO辅助类SqlMapClientDaoSupport一致,Spring针对Hibernate也提供了DAO辅助类,即HibernateDaoSupport。同理,继承自HibernateDaoSupport的子类可以通过调用父类方法getHibernateTemplate()来获取HibernateTemplate,从而使用其上的便利方法。
有了以上的认知后,L首先给出了一个假想的DAO接口,见例6.52。
例6.52:BusinessDao.java
package springhibernate;
import java.util.List;
public interface BusinessDao {
  public Category createCategoryAndItsProduct(Category category);
 
  public void deleteCategoryAndItsProduct(Category category);
 
  /**
   * 根据Category,查找Product集合
   */
  public List findProductsBy(Category category);
}
接着,L给出继承自HibernateDaoSupport的具体DAO实现子类,见例6.53。
例6.53:BusinessDaoImpl.java
package springhibernate;
import java.util.List;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
public class BusinessDaoImpl extends HibernateDaoSupport
  implements BusinessDao {
  public Category createCategoryCascade(Category category) {
    return (Category)getHibernateTemplate().save(category);
  }
  public void deleteCategoryCascade(Category category) {
    getHibernateTemplate().delete(category);
  }
  public List findProductsBy(Category category) {
    String categoryAlias = "springhibernate.Category";
    return
      getHibernateTemplate().find("select category.products from "+
        categoryAlias+" category where category.catid=?",
        category.getCatid());
  }
}
6.6.4  整合配置
在备齐了以上这些代码、配置后,L将给出最重要的一些关于如何整合Spring和Hibernate的配置。熟悉Hibernate的读者应该知道,Hibernate中有两个最为重要的组件:Session以及它的工厂SessionFactory,在Hibernate中最常见的配置方式是使用hibernate.cfg.xml来进行SessionFactory和一些基本参数的配置。那么在Spring介入之后,hibernate.cfg.xml将会产生什么变化,又会额外增加一些其它什么配置呢?在研究了相关的资料后,L首先对hibernate.cfg.xml进行了一些改变,见例6.54。
例6.54:hibernate.cfg.xml
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
  <session-factory>
 
    <!-- SQL dialect -->
    <property name="dialect">
      org.hibernate.dialect.PostgreSQLDialect
    </property>
   
    <!-- Echo all executed SQL to stdout -->
    <property name="show_sql">true</property>
   
    <!-- Drop and re-create the database schema on startup -->
    <property name="hbm2ddl.auto">create</property>
    <mapping resource="springhibernate/Category.hbm.xml" />
    <mapping resource="springhibernate/Product.hbm.xml" />
  </session-factory>
</hibernate-configuration>
可以发现,基本的Hibernate配置中移除了关于JDBC数据源的配置,这些配置将交由Spring进行配置,这和L猜想的也基本吻合,顺着这个思路,于是L撰写了Spring的相关配置,见例6.55。
例6.55:dao-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
  <bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations">
      <list>
        <value>classpath:jdbc.properties</value>
      </list>
    </property>
  </bean>
  <bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
    <property name="driverClassName" value="${jdbc.driverClassName}"/>
    <property name="url"
    value="jdbc:postgresql://127.0.0.1:5432/springhibernate"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
  </bean>
  <bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="configLocations">
      <value>springhibernate/hibernate.cfg.xml</value>
    </property>
  </bean>
 
  <bean id="businessDao"
  class="springhibernate.BusinessDaoImpl">
    <property name="sessionFactory" ref="sessionFactory"/>
  </bean> 
</beans>
6.6.5  事务控制
最后,和上文的方式一样,为了演示事务控制,L增加出了业务对象,以提供事务边界方法,这些方法通常是跨越多个DAO方法调用的,见例6.56和例6.57。
例6.56:Business.java
package springhibernate;
public interface Business {
  public void doSomeBusiness1(Category category, List productList);
  //省略...
}
例6.57:BusinessImpl.java
package springhibernate;
public class BusinessImpl implements Business {
 
  private BusinessDao businessDao;
 
  public void setBusinessDao(BusinessDao businessDao) {
    this.businessDao = businessDao;
  }
 
  public void doSomeBusiness1(Category category, List productList) {
    Category createdCategory = businessDao.createCategory(category);
   
    for(Iteration iter = productList.iterator();iter.hasNext()) {
      Product product = (Product)iter.next();
      createdCategory.addProduct(product);
    }
   
    businessDao.findProductsBy(category);
    businessDao.deleteCategory(category);
  } 
  //省略...
}
接着,L给出了关于事务控制的配置,其中使用了Spring给出的Hibernate TransactionManager,见例6.58。
例6.58:transaction-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
  <bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" >
      <ref bean="sessionFactory" />
    </property>
  </bean>
  <bean id="businessBean"
class="springhibernate.BusinessImpl">
    <property name="businessDao">
      <ref bean="businessDao"/>
    </property>
  </bean>
  <bean id="businessProxy"
    class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
    <property name="transactionManager">
      <ref bean="transactionManager" />
    </property>
   
    <property name="target">
      <ref bean="businessBean" />
    </property>
   
    <property name="transactionAttributes">
      <props>
        <!-- 如果当前没有启动事务,那么创建一个新的事务 -->
        <prop key="doSomeBusiness*">PROPAGATION_REQUIRED</prop>
      </props>
    </property>
  </bean>
</beans>
最后,L给出相关的客户测试代码,见例6.59。
例6.59:SpringHibernateTest.java
package springhibernate;
import java.util.List;
import java.util.ArrayList;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringHibernateTest {
  public static void main(String[] args) {
    String path = "springhibernate/";
    ApplicationContext springContext = new ClassPathXmlApplicationContext(
         new String[]{path+"dao-context.xml", path+"transaction-context.xml"});
        
    Business = (Business)springContext.getBean("businessProxy");
   
    Category category = new Category("Food1");
    category.setName("Fast Food");
    category.setDescn("Desciption of Fast Food");
    product1 = new Product("Fast Food-01");
    product2 = new Product("Fast Food-02");
    product1.setName("KFC");
    product1.setDescn("Description of KFC");
    product2.setName("McDonalds");
    product2.setDescn("Description of McDonalds");
    List productList = new ArrayList();
    productList.add(product1);
    productList.add(product2);
   
    business.doSomeBusiness1(category, productList);
   
  }
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息