构建Portlet应用
5. 通过配置,将Spring框架与Hibernate框架结合使用。
如果你以前使用过Hibernate,你现在该感到有些迷惑:使用Hibernate框架的时候,需要提供的hibernate.cfg.xml配置文件应该放在哪里呢?为了使Spring框架能够真正的感知到Hibernate对象,为其添加事务管理,SessionFactory管理等功能,我们需要添加一个Spring的配置文件,而且,Spring提供了一个便捷的方式-----在Spring内部配置中并入了Hibernate的hibernate.cfg.xml配置文件。首先在Sample项目的JavaSource/dao目录下,建立文件applicationContext-hibernate.xml,在文件中添加如下内容:
代码清单5:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
![]()
<beans>
<!-- ==================== Start of PERSISTENCE DEFINITIONS ================== -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"
destroy-method="close">
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost:3306/infos</value>
</property>
<property name="username">
<value>root</value>
</property>
<property name="password">
<value></value>
</property>
</bean>
![]()
<!-- Choose the dialect that matches your "dataSource" definition -->
<bean id="mySessionFactory"
class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
<property name="dataSource">
<ref local="dataSource" />
</property>
![]()
<property name="mappingResources">
<list>
<value>com/ibm/sample/bo/InfoObject.hbm.xml</value>
</list>
</property>
![]()
<property name="hibernateProperties">
<props>
<prop
key="hibernate.dialect">net.sf.hibernate.dialect.MySQLDialect</prop>
</props>
</property>
</bean>
![]()
<!-- DAO object: Hibernate implementation -->
<bean id="infoObjectDAO"
class="com.ibm.b2e.app.itsubsystem.dao.hibernate.InfoObjectDAOHibernate">
<property name="sessionFactory"><ref
local="mySessionFactory"/></property>
</bean>
![]()
</beans>
Spring的核心功能就是Bean管理,在这个配置文件中,我们配置了3个Java对象,id分别为:dataSource, mySessionFactory和infoObjectDAO。mySessionFactory的dataSource属性引用了dataSource对象,infoObjectDAO的sessionFactory属性又引用了mySessionFactory对象。在Spring框架启动的时候,会自动的根据这个配置文件,生成相应的对象,并将生成的对象注入到对应的属性中去,这就是所谓的"依赖注入"(dependency injection)。通过这样的方式,可以将我们从 单例模式(singleton objects)和工厂模式(factories)中解放出来,降低代码的维护成本。在这里,mySessionFactory中配置的属性,对应于Hibernate的hibernate.cfg.xml配置文件。
dataSource中的属性设置是针对MySql数据库的,你需要将其改成与你的测试环境数据库一致。
好了,至此,我们完成了示例应用的DAO层创建工作,是不是感到有些心神不宁?是的,虽然我们用的不是测试驱动开发(Test Driver Development),可现在也该写点测试用例来测试一下我们刚刚新建的DAO层了。
建立JUnit单元测试来测试DAO层
1. 测试前的数据准备工作。
如果你使用的是MySql数据库,运行ant任务setup-db,此任务将创建一个名为infos的数据库,并在数据库中建立数据表infoobject。如果你使用的不是MySql数据库,你需要手工建立测试数据库,然后运行ant任务db-prepare,将会在数据库中自动建立表infoobject。
2. 编写单元测试基类
代码清单6:
package com.ibm.sample.dao;
public class BaseDAOTestCase extends TestCase...{
protected final static XmlBeanFactory factory ;
![]()
static ...{
Resource rs1 = new ClassPathResource("applicationContext-hibernate.xml");
factory = new XmlBeanFactory(rs1);
}
}
这个基类非常简单,完成的功能就是通过读入Spring的配置文件,构建一个Spring的bean管理工厂,通过将这段代码包含在静态段中,可以确保Spring的Bean工厂对所有的测试只装载了一次。
3. 编写DAO的单元测试
本示例中用到的对InfoObjectDAO进行测试的类为InfoObjectDAOTest,具体代码可以查看附件中的InfoObjectDAOTest.java类代码。测试覆盖了InfoObject的各个方法,你可以简单的将其拷贝到自己的项目中并运行,如果数据库都配置正确的话,你将看到绿色的状态条,OK,测试通过,我们的DAO层已经可以顺利运行了,下面我们进入业务层代码的编写。
创建业务层
现在,我们需要来构建我们的BSO(business service objects)了,用来执行程序的逻辑,调用持久层,得到UI层的requests,处理transactions,并且控制exceptions。 在这里,我们将使用Spring框架,很快,你就会感受到使用Spring框架来管理业务层,将给你的应用程序带来极大的灵活性,和更松散的耦合度。
1. 建立业务服务对象接口
首先我们需要做的,还是要定义出我们在业务层提供的接口。在Spring框架中,任何注册到Spring框架中的bean,如果实现了某个接口,那么在得到这个bean的时候,只能将其下溯造型成其接口进行操作,而不能直接下溯造型成具体的类型进行操作。原因在于Spring的AOP实现机制, Spring中的Bean管理实际上是基于动态AOP机制实现,为了实现动态AOP,Spring在默认情况下会使用Java Dynamic Proxy,但是,Dynamic Proxy要求其代理的对象必须实现一个接口,该接口定义了准备进行代理的方法。而对于没有实现任何接口的Java Class,需要采用其他方式,Spring通过CGLib实现这一功能。当类实现了一个接口之后,Spring将通过Java Dynamic Proxy机制实现代理功能,此时返回的Bean,是通过java.lang.reflect.Proxy.newProxyInstance方法创建的其接口的一个代理实现,这个实例实现了其接口,但与类已经没有继承关系,因此无法通过下溯造型进行强制转型,如果进行转换,则会抛出异常。这也就强制要求编程人员要面向接口编程,使程序员能够从接口的角度考虑程序设计,从而降低了程序的耦合度。
代码清单7:
通过接口可以看到,在业务层对底层的Exception进行了捕捉,并进行了统一的封装,再用定义好的业务服务级别的Exception抛出。注意到这段代码里有一个 setInfoObjectDAO(),它就是一个DAO Object设置方法,将DAO的实现注射到Service对象中。 但这里并没有一个getInfoObjectDao的方法,这不必要,因为并不会在外部访问这个DAO。Service层将调用这个DAO Object和持久层通信。我们将用Spring把DAO Object 和 business service object搭配起来的。因为我们是面向接口编程的,所以并不需要将实现类紧密的耦合在一起。package com.ibm.sample.service;
![]()
public interface IInfoObjectService ...{
![]()
public abstract InfoObject saveInfoObject(InfoObject infoObject) throws InfoObjectException;
![]()
public abstract InfoObject findInfoObjectById(Long id) throws InfoObjectException;
![]()
public abstract List findAllInfoObjects() throws InfoObjectException;
![]()
public abstract void removeInfoObject(Long deleteId) throws InfoObjectException;
![]()
public abstract void setInfoObjectDAO(IInfoObjectDAO infoObjectDAO);
}
2. 实现业务服务对象接口,并通过Spring将其和DAO对象关联起来因为本例比较简单,所以接口的实现也很简单,并没有什么复杂的操作,通过调用InfoObjectDAO对象上的方法,就可以实现服务对象接口,具体的代码见附件中的com.ibm.sample.service.impl. InfoObjectServiceImpl.java文件。我们主要需要关注的是如何通过Spring将业务对象与DAO对象关联起来,并实现事务。在Spring的配置文件中添加如下代码:
代码清单8:
<!-- Transaction manager for a single Hibernate SessionFactory (alternative to JTA) -->
<bean id="myTransactionManager"
class="org.springframework.orm.hibernate.HibernateTransactionManager">
<property name="sessionFactory"><ref local="mySessionFactory"/></property>
</bean>
![]()
<!-- ***** InfoObject SERVICE *****-->
<bean id="infoObjectService"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager"><ref
local="myTransactionManager"/></property>
<property name="target"><ref local="infoObjectTarget"/></property>
<property name="transactionAttributes">
<props>
<prop key="find*">PROPAGATION_REQUIRED,readOnly,-InfoObjectException</prop>
<prop key="save*">PROPAGATION_REQUIRED,-InfoObjectException</prop>
</props>
</property>
</bean>
![]()
<!-- InfoObjectTarget primary business object implementation -->
<bean id="infoObjectTarget" class="com.ibm.sample.service.impl. InfoObjectServiceImpl">
<property name="infoObjectDAO"><ref local="infoObjectDAO"/></property>
</bean>
