随笔-10  评论-36  文章-6  trackbacks-0
 

专题五:Spring + Hibernate 编程实例

本实例中,使用Spring作为WEB层和业务逻辑层的容器,Hibernate作为持久层,另外使用JAX-WS2.1做WebService部件,为瘦客户端提供服务。其结构如下图所示:



从这个总图可以看出,这个框架需要完成下列任务:

l         编写实体类和实体主键类

l         编写实体类的持久化操作类及其接口

l         编写业务逻辑类及其接口

l         编写Web界面操作类

l         编写WebService类(给瘦客户端)

l         编写相应的配置文件

本实例使用一个简单的地址本管理应用描述相关的内容。本实例设计工作在Tomcat和glassfish服务器上。建议使用glassfish服务器以获得后续的发展,毕竟glassfish是SUN发布的全功能的应用服务器,而非只有Web容器。

1          环境准备

1.1    安装系统软件

l         安装JDK 6.0 和JAVA EE 5;

l         安装netbeans 6.0做开发工具,使用eclipse也可以;

l         安装Tomcat 6.0做应用服务器;

l         安装GlassFish 2.0做应用服务器

l         安装数据库,这里使用 MS SQL Server Express 2005

1.2    准备组件

l         Spring 2.06

l         Hibernate 3.2

l         JAX-WS 2.1

l         MS SQL Server JDBC 1.2

l         其他需要的JAR包

1.3    创建应用

l         使用netbeans创建WebApplication,命名为 AddressBook;

l         选择Tomcat做服务器;

l         JAVA平台选择 Java EE 5;

l         创建数据库及数据表。

CREATE DATABASE addressbook;

CREATE TABLE db_address

{

email     char(20) not null primary key,

name      char(20) not null,

password char(20) not null,

workphone char(20) not null,

homephone char(20) not null,

mobile    char(20) not null,

};

1.4    添加基础配置

1.4.1        WEB-INF/web.xml

web.xml文件放置到WEB-INF目录;

l         配置Spring ApplicationContext文件;

l         配置Spring ApplicationContext加载器;

l         配置log4j的文件;

l         配置Spring过虑器防止汉字乱码。

<?xml version="1.0" encoding="UTF-8"?>

<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

    <description>地址本管理应用范例</description>

<display-name>AddressBook</display-name>

    <context-param>

        <description>Spring main config</description>

        <param-name>contextConfigLocation</param-name>

        <param-value>/WEB-INF/applicationContext.xml</param-value>

</context-param>

 <context-param>

      <param-name>log4jConfigLocation</param-name>

      <param-value>/WEB-INF/log4j.properties</param-value>

 </context-param>

    <listener>

        <description>Spring applicationContext loader</description>

        <listener-class>

org.springframework.web.context.ContextLoaderListener

</listener-class>

</listener>

 

    <filter>

        <filter-name>encodingFilter</filter-name>

        <filter-class>

org.springframework.web.filter.CharacterEncodingFilter

</filter-class>

        <init-param>

            <param-name>encoding</param-name>

            <param-value>GB18030</param-value>

        </init-param>

        <init-param>

            <param-name>forceEncoding</param-name>

            <param-value>true</param-value>

        </init-param>

    </filter> 

   

    <filter-mapping>

        <filter-name>encodingFilter</filter-name>

        <servlet-name>AddressBookWeb</servlet-name>

        <url-pattern>*.do</url-pattern>

</filter-mapping>

<session-config>

        <session-timeout>

            30

        </session-timeout>

    </session-config>

    <welcome-file-list>

        <welcome-file>index.jsp</welcome-file>

    </welcome-file-list>

</web-app>

1.4.2        WEB-INF/classes/applicationContext.xml

applicationContext.xml放置到Source目录,编译时被IDE自动放置到WEB-INF/classes目录。(放置到此处JUnit测试才能通过,否则报加载配置文件错)

l         配置数据库连接池(c3p0DataSource);

l         配置Hibernate Session管理器(sessionFactory);

l         配置数据库事务管理器(transactionManager);

l         配置Spring配置式事务代理(txProxyTemplate)。

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xmlns:aop="http://www.springframework.org/schema/aop"

       xsi:schemaLocation=

       "http://www.springframework.org/schema/beans

       http://www.springframework.org/schema/beans/spring-beans.xsd

       http://www.springframework.org/schema/aop

http://www.springframework.org/schema/aop/spring-aop.xsd">

<bean id="c3p0DataSource"

class="com.mchange.v2.c3p0.ComboPooledDataSource"

destroy-method="close">

        <property name="driverClass">

            <value>com.microsoft.sqlserver.jdbc.SQLServerDriver</value>

        </property>

        <property name="jdbcUrl">

            <value>

jdbc:sqlserver://localhost:1433;databaseName=AddressBook

</value>

        </property>

        <property name="user">

            <value>sa</value>

        </property>

        <property name="password">

            <value>welcome</value>

        </property>

        <property name="minPoolSize">

            <value>10</value>

        </property>

        <property name="acquireIncrement">

            <value>10</value>

        </property>

        <property name="maxPoolSize">

            <value>200</value>

        </property>

    </bean>

<bean id="sessionFactory"

class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">

        <property name="dataSource">

            <ref bean="c3p0DataSource" />

        </property>

        <property name="mappingDirectoryLocations">

            <list>

                <!-- Hibernate Configure file location -->

                <value>classpath*:model</value>

            </list>

        </property>

        <property name="hibernateProperties">

            <props>

                <prop key="hibernate.dialect">

org.hibernate.dialect.SQLServerDialect

</prop>

                <prop key="hibernate.show_sql">false</prop>

                <prop key="hibernate.format_sql">true</prop>           

                <prop key="hibernate.default_schema"></prop>   

                <prop key="hibernate.cache.provider_class">

org.hibernate.cache.EhCacheProvider

</prop>            

            </props>

        </property>

    </bean>

<bean id="transactionManager"

class="org.springframework.orm.hibernate3.HibernateTransactionManager">

        <property name="sessionFactory">

            <ref local="sessionFactory"/>

        </property>

    </bean>

<bean id="txProxyTemplate"

class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" abstract="true">

        <property name="transactionManager" ref="transactionManager"/>

        <property name="transactionAttributes">

            <props>           

                <prop key="select*">PROPAGATION_REQUIRED,readOnly</prop>

                <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>

                <prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>

                <prop key="save*">PROPAGATION_REQUIRED</prop>

                <prop key="update*">PROPAGATION_REQUIRED</prop>

                <prop key="remove*">PROPAGATION_REQUIRED</prop>

                <prop key="del*">PROPAGATION_REQUIRED</prop>

            </props>

        </property>

    </bean>

</beans>

注意:

(1)以上系统级基础Bean均是单例的;

(2)txProxyTemplate 是一个抽象的定义,全部业务逻辑Bean的定义将继承其定义,从而获得Spring的配置式事务能力;

(3)此处指定Hibernate的配置文件要放置到WEB-INF/classes/model目录下。

1.4.3        WEB-INF/classes/log4j.properties

# TraceLevel: OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL

# Appender: ConsoleAppender、FileAppender、DailyRollingAppender、RollingFileAppender、WriterAppender

# Layout: HTMLLayout、PatternLayout、SimpleLayout、TTCCLayout

# ConversionPattern: %m(message)、%p(TraceLevel)、%r(时间片-毫秒数)、%c(类名)、%t(线程号)、%n("n)、%d{DateFmt}(日期)、%l(全信息)

# log4j.appender.A2.File=dglog.txt

#

# log4j.appender.A2=org.apache.log4j.DailyRollingFileAppender

# log4j.appender.A2.file=dglog

# log4j.appender.A2.DatePattern='.'yyyy-MM-dd

# log4j.appender.A2.layout=org.apache.log4j.PatternLayout

# log4j.appender.A2.layout.ConversionPattern= %5r %-5p %c{2} - %m%n

#

# log4j.appender.R=org.apache.log4j.RollingFileAppender

# log4j.appender.R.File= dglog.log

# log4j.appender.R.MaxFileSize=100KB

# log4j.appender.R.MaxBackupIndex=30

# log4j.appender.R.layout=org.apache.log4j.PatternLayout

# log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n

#

log4j.rootLogger=ERROR,A1,A2

log4j.appender.A1=org.apache.log4j.ConsoleAppender

log4j.appender.A1.layout=org.apache.log4j.PatternLayout

log4j.appender.A1.layout.ConversionPattern=[%d{yyyy-MM-dd HH":mm":ss}] [%c %M] %-5p %c(line":%L) %x-%m%n

#

log4j.appender.A2=org.apache.log4j.RollingFileAppender

log4j.appender.A2.File=addressbook.log

log4j.appender.A2.MaxFileSize=1024KB

log4j.appender.A2.MaxBackupIndex=7

log4j.appender.A2.layout=org.apache.log4j.PatternLayout

log4j.appender.A2.layout.ConversionPattern=[%-5p] [%d{yyyy-MM-dd HH":mm":ss}] [%c %M] %c(line":%L) %x-%m%n

1.5    测试

发布应用到服务器,检查服务器的日志信息,确认数据库连接是否成功,是否缺少JAR包,日志输出是否正常。

2          创建实体类及主键类

由于派生或替换实体类及主键类的可能性不大,因此对实体类及主键类不必定义接口。每个业务数据对象均需要设计实体类及主键类。此外,业务逻辑层与WEB层交换的model对象,如果无法使用数据库实体,则也应为其定义一个实体类(例如进行多表数据编辑。――多表数据输出则不必定义实体,只需要将多个实体对象放置到模型中即可)。

应注意的是,尽管实体的主键可能是单键,可以使用简单数据类型,但考虑到编码的一致性,建议统一使用主键类作为各实体的主键。

实体类与主键类通常放置到model包中,可以根据实体的数量和性质划分更细的包。

实体类及主键类是普通的JavaBean,只包含数据及其构造方法、Setter和Getter方法,且实现Serializable接口。实体类及主键类必须定义无参构造方法、equals方法、hashCode方法,主键类应定义一个带参构造方法。实体类及实体主键类必须对全部属性进行初始化,确保各属性的值合法,这可以避免很多意外的错误。

2.1    model.key.Address.AddressId

/*

 * To change this template, choose Tools | Templates

 * and open the template in the editor.

 */

package model.key.Address;

import java.io.Serializable;

/**

 *

 * @author

 */

public class AddressId implements Serializable

{

private String id = "";

    public AddressId()

    {

    }

    public AddressId(String id)

    {

        this.id = id.trim();

    }

    @Override

    public boolean equals(Object obj)

    {

        if (obj == null)

        {

            return false;

        }

        if (getClass() != obj.getClass())

        {

            return false;

        }

        final AddressId other = (AddressId) obj;

        if (this.id == null || !this.id.equals(other.id))

        {

            return false;

        }

        return true;

    }

    @Override

    public int hashCode()

    {

        int hash = 5;

        hash = 97 * hash + (this.id != null ? this.id.hashCode() : 0);

        return hash;

    }

    public String getId()

    {

        return id.trim();

    }

    public void setId(String id)

    {

        this.id = id.trim();

    }

}

2.2    model.entity.Address.Address

/*

 * To change this template, choose Tools | Templates

 * and open the template in the editor.

 */

package model.entity.Address;

import java.io.Serializable;

import model.key.Address.AddressId;

/**

 *

 * @author

 */

public class Address implements Serializable

{

    private AddressId id = new AddressId("");

    private String name = "";

    private String password = "";

    private String workphone = "";

    private String homephone = "";

private String mobile = "";

    public Address()

    {

    }

    @Override

    public boolean equals(Object obj)

    {

        if (obj == null)

        {

            return false;

        }

        if (getClass() != obj.getClass())

        {

            return false;

        }

        final Address other = (Address) obj;

        if (this.id != other.id && (this.id == null || !this.id.equals(other.id)))

        {

            return false;

        }

        return true;

    }

    @Override

    public int hashCode()

    {

        int hash = 3;

        hash = 97 * hash + (this.id != null ? this.id.hashCode() : 0);

        return hash;

    }

    public AddressId getid()

    {

        return id;

    }

    public void setid(AddressId id)

    {

        this.id = id;

    }

    public String getHomephone()

    {

        return homephone.trim();

    }

    public void setHomephone(String homephone)

    {

        this.homephone = homephone.trim();

    }

    public String getMobile()

    {

        return mobile.trim();

    }

    public void setMobile(String mobile)

    {

        this.mobile = mobile.trim();

    }

    public String getName()

    {

        return name.trim();

    }

    public void setName(String name)

    {

        this.name = name.trim();

    }

    public String getPassword()

    {

        return password.trim();

    }

    public void setPassword(String password)

    {

        this.password = password.trim();

    }

    public String getWorkphone()

    {

        return workphone.trim();

    }

    public void setWorkphone(String workphone)

    {

        this.workphone = workphone.trim();

    }

}

注意:各属性值的trim()可以避免变长字段很多意外情况的发生。

2.3    model/Address.hbm.xml

实体类的Hibernate映射文件放置到model目录下(见1.4.2节的配置)。每个实体类一个文件。文件命令规则为实体类名.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="model.entity.Address.Address"  table="db_address"

           dynamic-insert="true" dynamic-update="true" lazy="true">

        <composite-id name="id" class="model.key.Address.AddressId">

            <key-property name="id" type="string">

                <column name="email" length="20" />

            </key-property>

        </composite-id>

        <property name="name" type="string">

            <column name="name" length="20" not-null="true" />

        </property>

        <property name="workphone" type="string">

            <column name="workphone" length="20" not-null="true" />

        </property>

        <property name="homephone" type="string">

            <column name="homephone" length="20" not-null="true" />

        </property>

        <property name="mobile" type="string">

            <column name="mobile" length="20" not-null="true" />

        </property>

        <property name="password" type="string">

            <column name="password" length="20" not-null="true" />

        </property>       

    </class>

</hibernate-mapping>

2.4    WEB-INF/classes/applicationContext.xml

实体类通过其属性存储持久化数据,因此应是多例的。

    <bean id="Address" class="model.entity.Address.Address" scope="prototype">

        <property name="name" value= "name"/>

    </bean>

    <bean id="AddressId" class="model.key.Address.AddressId" scope="prototype">

        <property name="id" value= "email"/>

</bean>

注意:增加属性值的目的是为JUnit测试。

2.5    单元测试

Spring的spring-mock.jar提供了脱离Web容器的环境下测试Spring框架的功能,因此需将spring-mock.jar包加入测试包,并创建Spring测试类继承AbstractTransactionalSpringContextTests类,覆盖getConfigLocations()方法和runTest()方法。AbstractTransactionalSpringContextTests类提供了加载Spring环境的能力和数据库事务管理能力,它会在测试结束时自动回滚测试中的数据库事务,确保不会对数据形成影响。如果测试中需要提交数据库事务,可以调用setComplete()或setDefaultRollback(boolean defaultRollback)。

2.5.1        Case.SpringTest

/*

 * To change this template, choose Tools | Templates

 * and open the template in the editor.

 */

package Case;

import model.entity.Address.Address;

import model.key.Address.AddressId;

import org.springframework.test.AbstractTransactionalSpringContextTests;

import static org.junit.Assert.*;

/**

 *

 * @author

 */

public class SpringTest extends AbstractTransactionalSpringContextTests

{

    @Override

    protected String[] getConfigLocations()

    {

        return new String [] {"classpath*:applicationContext.xml"};

    }

   public void TestEntity()

    {

        System.out.println("TestEntity() start ..");

        System.out.println("AddressId ..");

        AddressId id = (AddressId) applicationContext.getBean("AddressId");

        assertNotNull(id);

        assertEquals(id.getId(), "email");

        System.out.println("Address ..");

        Address address = (Address) applicationContext.getBean("Address");

        assertNotNull(address);

        assertEquals(address.getName(), "name");

        

        System.out.println("TestEntiry() end.");       

    }

}

2.5.2        Test.JUnitTest

/*

 * To change this template, choose Tools | Templates

 * and open the template in the editor.

 */

package Test;

import Case.SpringTest;

import org.junit.Test;

import static org.junit.Assert.*;

/**

 *

 * @author

 */

public class JUnitTest

{

    @Test

    public void main() throws Throwable

    {

        System.out.println("Start Test ...");

        SpringTest t = new SpringTest();

        t.setName("TestEntiry");

        t.runBare();

    }

}

3          编写实体类的持久化操作类及其接口

为简化实体类的持久化操作类编写,我们创建持久化基础类完成大部分的持久化任务。各实体类的持久化类均继承持久化基础类。本例中的持久化任务是通过Hibernate完成的,因此持久化基础类以Spring提供的事务和HibernateDaoSupport作为基础的组件。网络上有比较成熟的持久化基础类,可以下载来使用。

为支持各个实体类,持久化基础类使用了JDK5.0增加的泛性类。

持久化各类均放置在dao包中。

3.1    dao.GenericDao

/*

 * To change this template, choose Tools | Templates

 * and open the template in the editor.

 */

package dao;

import java.io.Serializable;

import java.util.List;

/**

 *

 * @author

 */

public interface GenericDao<T extends Serializable, PK extends Serializable>

{

    public T Select(PK id);

    public List<T> SelectAll();

    public void Insert(T entity);

    public void Update(T entity);

    public void Delete(PK id);

}

3.2    dao.GenericHibernateDao

/*

 * To change this template, choose Tools | Templates

 * and open the template in the editor.

 */

package dao;

import java.io.Serializable;

import java.lang.reflect.ParameterizedType;

import java.lang.reflect.Type;

import java.util.List;

import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

/**

 *

 * @author

 */

public class GenericHibernateDao<T extends Serializable, PK extends Serializable>

        extends HibernateDaoSupport implements GenericDao<T,PK>

{

    private Class<T> entityClass;

    public GenericHibernateDao()

    {

       this.entityClass = null;

        Class c = getClass();

        Type t = c.getGenericSuperclass();

        if(t instanceof ParameterizedType)

        {

            Type[] p = ((ParameterizedType) t).getActualTypeArguments();

            this.entityClass = (Class) p[0];

        }       

    }

    public void Delete(PK id)

    {

        getHibernateTemplate().delete(this.Select(id));

    }

    public void Insert(T entity)

    {

        getHibernateTemplate().save(entity);

    }

    public void Update(T entity)

    {

        getHibernateTemplate().update(entity);

    }

    public T Select(Serializable id)

    {

        return (T) getHibernateTemplate().load(entityClass, id);

    }

    public List<T> SelectAll()

    {

        List<T> list;

        list = getHibernateTemplate().loadAll(entityClass);

        return list;

    }

}

3.3    dao.AddressDao

实体持久化类接口继承持久化基础类接口,将泛性转换为确定的实体类型。

/*

 * To change this template, choose Tools | Templates

 * and open the template in the editor.

 */

package dao;

import model.entity.Address.Address;

import model.key.Address.AddressId;

/**

 *

 * @author

 */

public interface AddressDao extends GenericDao<Address, AddressId>

{

}

3.4    dao.AddressDaoImp

实体持久化类继承持久化基础类,实现实体持久化类接口。

/*

 * To change this template, choose Tools | Templates

 * and open the template in the editor.

 */

package dao;

import model.entity.Address.Address;

import model.key.Address.AddressId;

/**

 *

 * @author

 */

public class AddressDaoImp extends GenericHibernateDao<Address, AddressId> implements AddressDao

{

}

3.5    WEB-INF/classes/applicationContext.xml

实体持久化类需在Spring的上下文配置中定义,以便业务逻辑类可以从Spring环境中获取并使用。由于实体持久化类没有存储任何状态数据,因此应使用单例模式。

    <bean id="AddressDao" class="dao.AddressDaoImp">

        <property name="sessionFactory">

            <ref bean="sessionFactory"/>

        </property>

    </bean>

3.6    单元测试

持久化类的测试在实体类测试的基础上进行,需在Spring测试类中增加持久化测试的相关内容。注意如果Dao类还没有纳入事务管理而hibernate配置实体是延迟加载的,则执行select时会出现could not initialize proxy - the owning Session was closed错误,此时可以临时关闭实体类的延时加载选项或将Dao类纳入事务管理(见2.5.1)。

3.6.1        Case.SpringTest

    public void TestDao()

    {

        System.out.println("TestDao() start ..");

        System.out.println("测试AddressDao配置 ..");

        AddressDao dao = (AddressDao) applicationContext.getBean("AddressDao");

        assertNotNull(dao);

        System.out.println("测试AddressDao.insert() ..");

        AddressId id = (AddressId) applicationContext.getBean("AddressId");

        id.setId("_email");

        Address address = (Address) applicationContext.getBean("Address");

        address.setid(id);

        address.setName("_name");

        address.setHomephone("_homephone");

        address.setWorkphone("_workphone");

        address.setMobile("_mobile");

        address.setPassword("_password");

        dao.Insert(address);

        System.out.println("测试AddressDao.select() ..");

        address = dao.Select(id);

        assertNotNull(address);

        assertEquals(address.getid().getId(), "_email");

        assertEquals(address.getName(), "_name");

        assertEquals(address.getHomephone(), "_homephone");

        assertEquals(address.getWorkphone(), "_workphone");

        assertEquals(address.getMobile(), "_mobile");

        assertEquals(address.getPassword(), "_password");

        System.out.println("测试AddressDao.update() ..");

        address.setName("_newname");

        dao.Update(address);

        Address addressnew = dao.Select(id);

        assertNotNull(addressnew);

        assertEquals(address.getName(), addressnew.getName());

        System.out.println("测试AddressDao.delete() ..");

        dao.Delete(id);

        System.out.println("TestDao() end.");       

    }

注意:测试时Spring会自动执行回滚操作取消对数据库的更改。但如果需要在数据库存储测试的数据,结束测试前可以调用setComplete()提交数据库事务。

3.6.2        Test.JUnitTest

    @Test

    public void main() throws Throwable

    {

        System.out.println("Start Test ...");

        SpringTest t = new SpringTest();

        t.setName("TestDao");

        t.runBare();

    }

4          编写业务逻辑类及其接口

业务逻辑类从界面获取数据,进行业务处理后存储到数据库;或接受界面的请求,从数据库获取数据提交给界面操作。业务逻辑和实体类、Dao类均存在交互。由于实体类是多例的,Dao类是单例的,为减少系统的开销,尽量将业务逻辑类设计为单例的,因此业务逻辑类可以使用Dao类、其他单例类作为属性,但不能使用实体类作为属性。实体类只能在方法中或参数中、Session环境中使用

为避免重复设计通用化的业务功能部分,建议设计业务逻辑基础类作为其他业务逻辑类的基类,并定义相应的接口。基础类是抽象类,因此不必在Spring中进行配置(因为不会创建其实例)。

业务逻辑类放置到business包及其子包。

4.1    business.GenericService

/*

 * To change this template, choose Tools | Templates

 * and open the template in the editor.

 */

package business;

/**

 *

 * @author

 */

public interface GenericService

{

}

4.2    business.GenericServiceImp

/*

 * To change this template, choose Tools | Templates

 * and open the template in the editor.

 */

package business;

/**

 *

 * @author

 */

public class GenericServiceImp implements GenericService

{

}

4.3    business.Address.AddressService

/*

 * To change this template, choose Tools | Templates

 * and open the template in the editor.

 */

package business.Address;

import business.GenericService;

import java.util.List;

import model.entity.Address.Address;

import model.key.Address.AddressId;

/**

 *

 * @author

 */

public interface AddressService extends GenericService

{

    public void save(Address address);

    public Address select(AddressId id);

    public List<Address> selectAll();

    public void update(Address address);

    public void delete(AddressId id);

}

4.4    business.Address.AddressServiceImp

/*

 * To change this template, choose Tools | Templates

 * and open the template in the editor.

 */

package business.Address;

import business.GenericServiceImp;

import dao.AddressDao;

import java.util.List;

import model.entity.Address.Address;

import model.key.Address.AddressId;

import org.hibernate.Hibernate;

/**

 *

 * @author

 */

public class AddressServiceImp extends GenericServiceImp implements AddressService

{

    private AddressDao dao;

    public void setDao(AddressDao dao)

    {

        this.dao = dao;

    }

    public void save(Address address)

    {

        dao.Insert(address);

    }

    public Address select(AddressId id)

    {

        Address address = dao.Select(id);

        Hibernate.initialize(address);

        return address;

    }

    public List<Address> selectAll()

    {

        List<Address> list = dao.SelectAll();

        Hibernate.initialize(list);

        return list;

    }

    public void update(Address address)

    {

        dao.Update(address);

    }

    public void delete(AddressId id)

    {

        dao.Delete(id);

    }

}

注意:由于实体类配置为延迟加载的(见2.3),即从数据库获取时Hibernate为提高性能,仅返回了实体的代理对象,当应用访问代理对象的数据时,才真正从数据库检索数据。为避免延迟检索数据时出现could not initialize proxy - the owning Session was closed,本例中使用Hibernate. initialize()代理业务逻辑操作,模拟业务逻辑中的使用代理对象数据,以便该实例数据能够被Hibernate从数据库中加载。

4.5    WEB-INF/classes/applicationContext.xml

业务逻辑类我们将实施Spring的配置化事务,因此其配置应扩展txProxyTemplate的配置,同时应注意业务逻辑类的方法命名应匹配txProxyTemplate定义的方法名,否则Spring无法使用AOP机制插入事务处理。

    <bean id="AddressServiceTarget" class="business.Address.AddressServiceImp">

        <property name="dao">

            <ref bean="AddressDao"/>

        </property>

    </bean>

    <bean id="AddressService" parent="txProxyTemplate">

        <property name="target">

            <ref bean="AddressServiceTarget"/>

        </property>

    </bean>

4.6    单元测试

4.6.1        Case.SpringTest

public void TestService()

    {

        System.out.println("TestService() start ..");

        System.out.println("测试AddressService配置 ..");

        AddressService svr = (AddressService) applicationContext.getBean("AddressService");

        assertNotNull(svr);

        System.out.println("测试AddressService.save() ..");

        AddressId id = (AddressId) applicationContext.getBean("AddressId");

        id.setId("_email");

        Address address = (Address) applicationContext.getBean("Address");

        address.setid(id);

        address.setName("_name");

        address.setHomephone("_homephone");

        address.setWorkphone("_workphone");

        address.setMobile("_mobile");

        address.setPassword("_password");

        svr.save(address);

        System.out.println("测试AddressService.select() ..");

        address = svr.select(id);

        assertNotNull(address);

        assertEquals(address.getid().getId(), "_email");

        assertEquals(address.getName(), "_name");

        assertEquals(address.getHomephone(), "_homephone");

        assertEquals(address.getWorkphone(), "_workphone");

        assertEquals(address.getMobile(), "_mobile");

        assertEquals(address.getPassword(), "_password");

        System.out.println("测试AddressService.update() ..");

        address.setName("_newname");

        svr.update(address);

        Address addressnew = svr.select(id);

        assertNotNull(addressnew);

        assertEquals(address.getName(), addressnew.getName());

        // this.setComplete();

        System.out.println("测试AddressService.delete() ..");

        svr.delete(id);

        System.out.println("TestService() end.");       

    }

4.6.2        Test.JUnitTest

@Test

    public void main() throws Throwable

    {

        System.out.println("Start Test ...");

        SpringTest t = new SpringTest();

        t.setName("TestService");

        t.runBare();

    }

5          编写Web界面操作类

本例中使用Spring作为Web界面部分,功能也很简单,仅提供Address实体的CRUD操作功能。Spring提供了一套创建Web MVC的类,本例中M使用实体类的实体,V使用JSP文件,输入型视图的C继承Spring的SimpleFormController,输出型视图的C实现Spring的Controller。

Spring的WEB MVC的关系图如下:

5.1    配置Spring Web MVC

Spring Web MVC需在Web.xml中配置Spring前端控制器Servlet及地址映射,同时增加Web MVC自己的上下文配置文件。

5.1.1        WEB-INF/web.xml

配置web.xml装载Spring WEB的控制器及其上下文配置,定义地址匹配,以及字符集编码转换。

    <servlet>

        <servlet-name>AddressBookWeb</servlet-name>

        <servlet-class>

org.springframework.web.servlet.DispatcherServlet

</servlet-class>

        <init-param>

            <param-name>contextConfigLocation</param-name>

            <param-value>/WEB-INF/applicationContext-web.xml</param-value>

        </init-param>

    </servlet>

    <servlet-mapping>

        <servlet-name>AddressBookWeb</servlet-name>

        <url-pattern>*.do</url-pattern>

</servlet-mapping>

    <filter>

        <filter-name>encodingFilter</filter-name>

        <filter-class>

org.springframework.web.filter.CharacterEncodingFilter

</filter-class>

        <init-param>

            <param-name>encoding</param-name>

            <param-value>GB18030</param-value>

        </init-param>

        <init-param>

            <param-name>forceEncoding</param-name>

            <param-value>true</param-value>

        </init-param>

    </filter>

    <filter-mapping>

        <filter-name>encodingFilter</filter-name>

        <servlet-name>AddressBookWeb</servlet-name>

        <url-pattern>*.do</url-pattern>

    </filter-mapping>

5.1.2        WEB-INF/applicationContext-web.xml

本文件配置活动类、视图、模型、校验器等。

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xmlns:aop="http://www.springframework.org/schema/aop"

       xsi:schemaLocation=

       "http://www.springframework.org/schema/beans

       http://www.springframework.org/schema/beans/spring-beans.xsd

       http://www.springframework.org/schema/aop

http://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="viewResolver"

          class="org.springframework.web.servlet.view.InternalResourceViewResolver">

        <property name="viewClass">

            <value>org.springframework.web.servlet.view.JstlView</value>

        </property>

        <property name="prefix">

            <value>/WEB-INF/jsp/</value>

        </property>

        <property name="suffix">

            <value>.jsp</value>

        </property>

    </bean>

    <bean id="urlMapping"

          class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">

        <property name="mappings">

            <props>

                <prop key="/append.do">AppendAction</prop>

                <prop key="/select.do">SelectAction</prop>

                <prop key="/delete.do">DeleteAction</prop>

                <prop key="/update.do">UpdateAction</prop>

                <prop key="/doUpdate.do">doUpdateAction</prop>

            </props>

        </property>

    </bean>

    <!-- Application custom configure --->

    <bean id="AppendAction" class="web.inAction.AppendAction">

        <property name="sessionForm"><value>true</value></property>

        <property name="commandName"><value>Address</value></property>

        <property name="commandClass">

<value>model.entity.Address.Address</value>

</property>

        <property name="formView"><value>append</value></property>

        <property name="successView"><value>show</value></property>

        <property name="svr">

            <ref bean="AddressService" />

        </property>

</bean>

    <bean id="SelectAction" class="web.inAction.SelectAction">

        <property name="sessionForm"><value>true</value></property>

        <property name="commandName"><value>AddressId</value></property>

        <property name="commandClass">

<value>model.key.Address.AddressId</value>

</property>

        <property name="formView"><value>select</value></property>

        <property name="successView"><value>show</value></property>

        <property name="svr">

            <ref bean="AddressService" />

        </property>

</bean>

   <bean id="UpdateAction" class="web.inAction.UpdateAction">

        <property name="sessionForm"><value>true</value></property>

        <property name="commandName"><value>AddressId</value></property>

        <property name="commandClass">

<value>model.key.Address.AddressId</value>

</property>

        <property name="formView"><value>update</value></property>

        <property name="successView"><value>doUpdate.do</value></property>

        <property name="svr">

            <ref bean="AddressService" />

        </property>

    </bean>

    <bean id="doUpdateAction" class="web.inAction.doUpdateAction">

        <property name="sessionForm"><value>true</value></property>

        <property name="commandName"><value>Address</value></property>

        <property name="commandClass">

<value>model.entity.Address.Address</value>

</property>

        <property name="formView"><value>modify</value></property>

        <property name="successView"><value>show</value></property>

        <property name="svr">

            <ref bean="AddressService" />

        </property>

</bean>

    <bean id="DeleteAction" class="web.inAction.DeleteAction">

        <property name="sessionForm"><value>true</value></property>

        <property name="commandName"><value>AddressId</value></property>

        <property name="commandClass"><value>model.key.Address.AddressId</value></property>

        <property name="formView"><value>delete</value></property>

        <property name="successView"><value>show</value></property>

        <property name="svr">

            <ref bean="AddressService" />

        </property>

</bean>

</beans>

注意:此处注册的Action Bean应与后续开发的活动类配套,而且ActionBean仅使用业务逻辑层对象作属性。ActionBean应设计为单例的。由于Web访问的并发性,因此ActionBean应注意使用的model应是Session级别的。如果model中包含了非Session或多例的bean时,操作model的该段代码应实施多线程保护。设计ActionBean时,应避免在model中使用单例对象,除非确有必要。

如果需要在多个ActionBean中共享或交换数据,可以将该数据放置到请求的属性、参数或Session环境中,通过HttpServletRequest的属性getAttribute()或参数getParameter()或arg0.getSession().getAttribute()进行访问。

5.2    编写Web活动类

活动类放置到web包。

5.2.1        web.inAction.inGenericAction

/*

 * To change this template, choose Tools | Templates

 * and open the template in the editor.

 */

package web.inAction;

import org.springframework.web.servlet.mvc.SimpleFormController;

/**

 *

 * @author

 */

public class inGenericAction extends SimpleFormController

{

}

5.2.2        web.outAction.outGenericAction

/*

 * To change this template, choose Tools | Templates

 * and open the template in the editor.

 */

package web.outAction;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.ModelAndView;

import org.springframework.web.servlet.mvc.Controller;

/**

 *

 * @author

 */

public class outGenericAction implements Controller

{

    public ModelAndView handleRequest(HttpServletRequest arg0, HttpServletResponse arg1) throws Exception

    {

        throw new UnsupportedOperationException("Not supported yet.");

    }

}

5.2.3        web.inAction.SelectAction

SelectAction根据select.jsp输入的AddressId的email值,从数据库检索数据,存储在show.jsp的模型中,供show.jsp显示。如果没有指定值,则检索全部数据。

/*

 * To change this template, choose Tools | Templates

 * and open the template in the editor.

 */

package web.inAction;

import business.Address.AddressService;

import java.util.HashMap;

import java.util.List;

import java.util.Vector;

import model.entity.Address.Address;

import model.key.Address.AddressId;

import org.springframework.web.servlet.ModelAndView;

/**

 *

 * @author

 */

public class SelectAction extends inGenericAction

{

    private AddressService svr;

    public void setSvr(AddressService svr)

    {

        this.svr = svr;

    }

    @Override

    protected ModelAndView onSubmit(Object arg0)

    {

        List<Address> list;

        AddressId id = (AddressId) arg0;

        try

        {

            if(id.getId().equals(""))

            {

                list = svr.selectAll();

            }

            else

            {

                list = new Vector<Address>();

                Address address = svr.select(id);

                list.add(address);

            }

        }

        catch(Exception e)

        {

            list = new Vector<Address>();

        }

        HashMap model = new HashMap();       

        model.put("AddressList", list);

        return new ModelAndView(getSuccessView(), model);

    }

}

注意:此处是Action返回视图,而非重新定向,因此model是输出的一部分,供Spring生成输出视图时使用。

5.2.4        web.inAction.AppendAction

AppendAction根据append.jsp视图输入的数据,创建Address对象,调用业务逻辑类将其存储到数据库中,然后从数据库获取全部数据,提交给show.jsp视图显示。

Spring 视图与控制间可以交换一个命令对象,如果需要从界面输入多个对象的值时,可以创建一个界面输入专用的实体类,或者将部分对象的值通过没有绑定的单独变量存储(这些值成为HttpServletRequest的属性或参数),在提交处理中赋值给相应的对象。

.jsp

<td>email:</td>

<td>

        <input type="text" name="_PK" value="">

    </td>

.java

String id = (String) arg0.getParameter("_PK");

本活动类定义的命令对象是Address。但Address类的主键是AddressId类,必须进行初始化,否则生成输入界面时报错。

/*

 * To change this template, choose Tools | Templates

 * and open the template in the editor.

 */

package web.inAction;

import business.Address.AddressService;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import model.entity.Address.Address;

import model.key.Address.AddressId;

import org.springframework.validation.BindException;

import org.springframework.validation.Errors;

import org.springframework.web.servlet.ModelAndView;

/**

 *

 * @author

 */

public class AppendAction extends inGenericAction

{

    private AddressService svr;

    public void setSvr(AddressService svr)

    {

        this.svr = svr;

    }

    @Override

    protected Map referenceData(HttpServletRequest arg0, Object arg1, Errors arg2) throws Exception

    {

        Address address = (Address) arg1;

        address.setid(new AddressId(""));

        address.setName("");

        address.setHomephone("");

        address.setWorkphone("");

        address.setMobile("");

        address.setPassword("");

        return null;

    }   

    @Override

    protected ModelAndView onSubmit(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, BindException arg3) throws Exception

    {

        Address address = (Address) arg2;

        svr.save(address);

        List<Address> list = svr.selectAll();       

        HashMap model = new HashMap();       

        model.put("AddressList", list);

        return new ModelAndView(getSuccessView(), model);

    }

}

5.2.5        web.inAction.UpdateAction

UpdateAction分两个步骤,第1步骤从界面获取要修改的email;第2步骤根据email从数据库检索数据供编辑。

本步骤将界面指定的email存储在新视图的模型中,然后重定向到编辑界面。此处使用session属性传递参数,还可以使用HttpServletRequest参数传递。

/*

 * To change this template, choose Tools | Templates

 * and open the template in the editor.

 */

package web.inAction;

import business.Address.AddressService;

import java.util.HashMap;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import model.entity.Address.Address;

import model.key.Address.AddressId;

import org.springframework.validation.BindException;

import org.springframework.web.servlet.ModelAndView;

import org.springframework.web.servlet.view.RedirectView;

/**

 *

 * @author

 */

public class UpdateAction extends inGenericAction

{

    private AddressService svr;

    public void setSvr(AddressService svr)

    {

        this.svr = svr;

    }

    @Override

    protected ModelAndView onSubmit(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, BindException arg3) throws Exception

    {

        AddressId id = (AddressId) arg2;

        HashMap model = new HashMap();

        arg0.getSession().setAttribute("AddressId", id);

        return new ModelAndView(new RedirectView(getSuccessView()), model);

    }

}

5.2.6        web.inAction.doUpdateAction

本步骤先从模型中得到email,然后根据email从数据库检索address数据供编辑,然后将编辑的数据存储到数据库。

/*

 * To change this template, choose Tools | Templates

 * and open the template in the editor.

 */

package web.inAction;

import business.Address.AddressService;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import model.entity.Address.Address;

import model.key.Address.AddressId;

import model.key.Address.AddressId;

import model.key.Address.AddressId;

import org.springframework.validation.BindException;

import org.springframework.validation.Errors;

import org.springframework.web.servlet.ModelAndView;

/**

 *

 * @author

 */

public class doUpdateAction extends inGenericAction

{

    private AddressService svr;

    public void setSvr(AddressService svr)

    {

        this.svr = svr;

    }

    @Override

    protected Map referenceData(HttpServletRequest arg0, Object arg1, Errors arg2) throws Exception

    {

        Address address = (Address) arg1;

        AddressId id = (AddressId) arg0.getSession().getAttribute("AddressId");

        Address old = svr.select(id);

        address.setid(old.getid());

        address.setName(old.getName());

        address.setHomephone(old.getHomephone());

        address.setWorkphone(old.getWorkphone());

        address.setMobile(old.getMobile());

        address.setPassword(old.getPassword());

        return null;

    }

    @Override

    protected ModelAndView onSubmit(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, BindException arg3) throws Exception

    {

        Address address = (Address) arg2;

        svr.update(address);

        List<Address> list = svr.selectAll();       

        HashMap model = new HashMap();       

        model.put("AddressList", list);

        return new ModelAndView(getSuccessView(), model);

    }

}

5.2.7        web.inAction.DeleteAction

/*

 * To change this template, choose Tools | Templates

 * and open the template in the editor.

 */

package web.inAction;

import business.Address.AddressService;

import java.util.HashMap;

import java.util.List;

import java.util.Vector;

import model.entity.Address.Address;

import model.key.Address.AddressId;

import org.springframework.web.servlet.ModelAndView;

/**

 *

 * @author

 */

public class DeleteAction extends inGenericAction

{

    private AddressService svr;

    public void setSvr(AddressService svr)

    {

        this.svr = svr;

    }

    @Override

    protected ModelAndView onSubmit(Object arg0)

    {

        List<Address> list;

        AddressId id = (AddressId) arg0;

        try

        {

            if(!id.getId().equals(""))

            {

                svr.delete(id);

            }

            list = svr.selectAll();

        }

        catch(Exception e)

        {

            list = new Vector<Address>();

        }

        HashMap model = new HashMap();       

        model.put("AddressList", list);

        return new ModelAndView(getSuccessView(), model);

    }

}

5.3    编写视图

本例使用JSP作为视图。非Spring管理的视图应放置到WebRoot的相对各目录下。由Spring管理的视图,应放置到WEB-INF/jsp目录下(参见5.1.2配置)。

5.3.1        WebRoot/index.jsp

index.jsp提供执行增加、修改、删除、检索的连接,应放置到WebRoot。

<%@page contentType="text/html;charset=GB18030"%>

<html>

    <head>

        <title>AddressBook Application</title>

    </head>

    <body>

        <h2 align="center">AddressBook Application</h2>

        <hr width="100%" size="2">

        <a href="select.do">检索</a>&nbsp;

        <a href="append.do">添加</a>&nbsp;

        <a href="update.do">修改</a>&nbsp;

        <a href="delete.do">删除</a>

    </body>

</html>

5.3.2        WEB-INF/jsp/select.jsp

select.jsp提供检索条件输入。

<%@page contentType="text/html" pageEncoding="GB18030"%>

<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

<%@taglib prefix="spring" uri="http://www.springframework.org/tags" %>

<%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

<html>

    <head><title>Select Address</title></head>

    <body><font size="6"><strong>

        Which email do you want to select?</strong></font>

        <form method="post">

            <table width="440" height="27">

                <tr>

                    <td>email:</td>

                    <td>

                        <spring:bind path="AddressId.id">

                            <input type="text" name="${status.expression}" value="${status.value}">

                        </spring:bind>

                        <input type="submit" alignment="center" value="Select">                    </td>

                </tr>

            </table>

        </form>

    </body>

</html>

path定义视图的模型。

注意:如果实体类没有初始化其各属性,且Action也没有调用referenceData()初始化该对象的各属性,则不能指定绑定路径为AddressId.id,也不能在<input>的name项使用${status.expression}。

5.3.3        WEB-INF/jsp/show.jsp

Show.jsp显示获取的Address数据。其数据均由其他Action类从数据库获取后填写到其模型中,供Show.jsp输出。此视图是所有活动的输出视图,只使用了JTSL标记库。

<%@ page session="false"%>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>

<html>

    <head><title>Selected Address List</title></head>

    <body>

        <h2 align="center">Selected Address List</h2><hr width="100%" size="2">

        <table width="733" border="1" height="56">

            <tbody>

                <tr>

                    <td><strong>Email</strong></td>

                    <td><strong>Name</strong></td>

                    <td><strong>HomePhone</strong></td>

                    <td><strong>WorkPhone</strong></td>

                    <td><strong>Mobile</strong></td>

                    <td><strong>Password</strong></td>

                </tr>

                <c:forEach items="${AddressList}" var="address">

                    <tr>

                        <td><c:out value="${address.id.id}" /> </td>

                        <td><c:out value="${address.name}" /></td>

                        <td><c:out value="${address.homephone}" /></td>

                        <td><c:out value="${address.workphone}" /></td>

                        <td> <c:out value="${address.mobile}" /></td>

                        <td><c:out value="${address.password}" /> </td>

                    </tr>

                </c:forEach>

            </tbody>

        </table>

    </body>

</html>

5.3.4        WEB-INF/jsp/append.jsp

<%@page contentType="text/html;charset=GB18030"%>

<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

<%@taglib prefix="spring" uri="http://www.springframework.org/tags" %>

<%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

<html>

    <head>

        <title>AddressBook Application</title>

    </head>

    <body><font size="6">

        <strong>Append New Address</strong></font>

        <spring:hasBindErrors name="Address">

            <br>

            <spring:bind path="Address.*">

                <font color="red">

                    <b>${status.errorMessage}</b>

                </font><br>

            </spring:bind>

            <br>

        </spring:hasBindErrors>

        <form method="post">

            <table width="440" height="27">

                <tr>

                    <td>email:</td>

                    <td>

                        <spring:bind path="Address.id.id">

                            <input type="text" name="${status.expression}" value="${status.value}">

                        </spring:bind>

                    </td>

                </tr>

                <tr>

                    <td>name:</td>

                    <td>

                        <spring:bind path="Address.name">

                            <input type="text" name="${status.expression}" value="${status.value}">

                        </spring:bind>

                    </td>

                </tr>

                <tr>

                    <td>workphone:</td>

                    <td>

                        <spring:bind path="Address.workphone">

                            <input type="text" name="${status.expression}" value="${status.value}">

                        </spring:bind>

                    </td>

                </tr>

                <tr>

                    <td>homephone:</td>

                    <td>

                        <spring:bind path="Address.homephone">

                            <input type="text" name="${status.expression}" value="${status.value}">

                        </spring:bind>

                    </td>

                </tr>

                <tr>

                    <td>mobile:</td>

                    <td>

                        <spring:bind path="Address.mobile">

                            <input type="text" name="${status.expression}" value="${status.value}">

                        </spring:bind>

                    </td>

                </tr>

                

                <tr>

                    <td>password:</td>

                    <td>

                        <spring:bind path="Address.password">

                            <input type="text" name="${status.expression}" value="${status.value}">

                        </spring:bind>

                    </td>

                </tr>               

                <tr>

                    <input type="submit" alignment="center" value="Append">

                </tr>

                

            </table>    

        </form>

    </body>

</html>

5.3.5        WEB-INF/jsp/update.jsp

<%@page contentType="text/html" pageEncoding="GB18030"%>

<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

<%@taglib prefix="spring" uri="http://www.springframework.org/tags" %>

<%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

<html>

    <head><title>Select Address</title></head>

    <body><font size="6"><strong>

        Which email do you want to update?</strong></font>

        <form method="post">

            <table width="440" height="27">

                <tr>

                    <td>email:</td>

                    <td>

                        <spring:bind path="AddressId">

                            <input type="text" name="${status.expression}" value="${status.value}">

                        </spring:bind>

                        <input type="submit" alignment="center" value="Update">                    </td>

                </tr>

            </table>

        </form>

    </body>

</html>

5.3.6        WEB-INF/jsp/modify.jsp

<%@page contentType="text/html" pageEncoding="GB18030"%>

<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

<%@taglib prefix="spring" uri="http://www.springframework.org/tags" %>

<%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

<html>

    <head><title>Modify Address</title></head>

    <body><font size="6">

        <strong>Modify Address</strong></font>

        <spring:hasBindErrors name="Address">

            <br>

            <spring:bind path="Address.*">

                <font color="red">

                    <b>${status.errorMessage}</b>

                </font><br>

            </spring:bind>

            <br>

        </spring:hasBindErrors>

        <form method="post">

            <table width="440" height="27">

                <tr>

                    <td>email:</td>

                    <td>

                        <spring:bind path="Address.id.id">

                            <input type="text" name="${status.expression}" value="${status.value}" disabled>

                        </spring:bind>

                    </td>

                </tr>

                <tr>

                    <td>name:</td>

                    <td>

                        <spring:bind path="Address.name">

                            <input type="text" name="${status.expression}" value="${status.value}">

                        </spring:bind>

                    </td>

                </tr>

                <tr>

                    <td>workphone:</td>

                    <td>

                        <spring:bind path="Address.workphone">

                            <input type="text" name="${status.expression}" value="${status.value}">

                        </spring:bind>

                    </td>

                </tr>

                <tr>

                    <td>homephone:</td>

                    <td>

                        <spring:bind path="Address.homephone">

                            <input type="text" name="${status.expression}" value="${status.value}">

                        </spring:bind>

                    </td>

                </tr>

                <tr>

                    <td>mobile:</td>

                    <td>

                        <spring:bind path="Address.mobile">

                            <input type="text" name="${status.expression}" value="${status.value}">

                        </spring:bind>

                    </td>

                </tr>

                <tr>

                    <td>password:</td>

                    <td>

                        <spring:bind path="Address.password">

                            <input type="text" name="${status.expression}" value="${status.value}">

                        </spring:bind>

                    </td>

                </tr>               

                

                <tr>

                    <input type="submit" alignment="center" value="Modify">

                </tr>

            </table>    

        </form>

    </body>

</html>

5.3.7        WEB-INF/jsp/delete.jsp

<%@ page language="java" pageEncoding="GB18030"%>

<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

<%@taglib prefix="spring" uri="http://www.springframework.org/tags" %>

<%@taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

<html lang="true">

    <head><title>Select Address</title></head>

    <body><font size="6"><strong>

        Which email do you want to delete?</strong></font>

        <form method="post">

            <table width="440" height="27">

                <tr>

                    <td>email:</td>

                    <spring:bind path="AddressId.id">

                        <input type="text" name="${status.expression}" value="${status.value}">

                    </spring:bind>

                    <input type="submit" alignment="center" value="Delete">

                </tr>

            </table>

        </form>

    </body>

</html>

6          编写WebService类(给瘦客户端)

本例使用JAX-WS作为WebService的发布工具。

由于WebService调用业务逻辑类完成业务处理,而业务逻辑类是由Spring管理的,因此必须将JAX-WS与Spring进行集成。(如果在WebService中脱离Spring管理直接创建业务逻辑类对象,由于业务逻辑类缺少Spring赋予的依赖关系,将导致创建的业务逻辑类对象无法工作或无法使用Spring环境的其他特性,如配置式事务)。

将JAX-WS与Spring进行集成,方法一就是在WebService中获取Spring的applicationContext,然后通过applicationContext访问Spring环境的各对象。方法二是使用jaxws-spring.jar工具包。该包使用com.sun.xml.ws.transport.http. servlet.WSSpringServlet替换JAX-WS默认的com.sun.xml.ws.transport.http. servlet .WSServlet,实现在Spring环境下创建WebService对象,从而使得WebService对象可以访问Spring环境的其他对象,包括业务逻辑类对象。jaxws-spring.jar需要xbean-spring.jar作为支持包。

方法一可以在Tomcat和glassfish中使用,但WebService对象本身是脱离Spring管理的,因此不能使用Spring的各种服务,只能通过applicationContext访问Spring环境中的对象。方法二WebService对象本身是受Spring管理的,因此可以使用Spring的各种服务,但在glassfish不支持此模式。本例中使用方法一即可满足要求。

6.1    方法一

6.1.1        Tomcat

使用netbeans创建AddressWebService,如果运行环境是Tomcat,则netbeans创建JAX-WS WebService时,会自动进行下列修改:

6.1.1.1      WEB-INF/web.xml

Web.xml增加JAX-WS的监听器、Servlet及地址映射。

    <listener>

        <listener-class>

com.sun.xml.ws.transport.http.servlet.WSServletContextListener

</listener-class>

</listener>

<servlet>

        <servlet-name>AddressWebService</servlet-name>

        <servlet-class>

com.sun.xml.ws.transport.http.servlet.WSServlet

</servlet-class>

        <load-on-startup>1</load-on-startup>

</servlet>

    <servlet-mapping>

        <servlet-name>AddressWebService</servlet-name>

        <url-pattern>/AddressWebService</url-pattern>

    </servlet-mapping>

6.1.1.2      WEB-INF/sun-jaxws.xml

JAX-WS使用本文件配置环境中存在的WebService,而该WebService具有的功能(WSDL),则是根据Java 5风格的注释信息自动生成的。

<?xml version="1.0" encoding="UTF-8"?>

<endpoints version="2.0" xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime">

 <endpoint implementation="websrv.AddressWebService" name="AddressWebService" url-pattern="/AddressWebService"/>

</endpoints>

6.1.1.3      websrv/AddressWebService

本例中我们创建下列服务:

l         public Address Select(AddressId id);

l         public void Delete(AddressId id);

l         public void Append(Address address);

l         public void Modify(Address address);

l         public List<Address> SelectAll().

/*

 * To change this template, choose Tools | Templates

 * and open the template in the editor.

 */

package websrv;

import business.Address.AddressService;

import java.util.List;

import javax.annotation.Resource;

import javax.jws.Oneway;

import javax.jws.WebMethod;

import javax.jws.WebParam;

import javax.jws.WebService;

import javax.servlet.ServletContext;

import javax.xml.ws.WebServiceContext;

import javax.xml.ws.handler.MessageContext;

import model.entity.Address.Address;

import model.key.Address.AddressId;

import org.springframework.web.context.WebApplicationContext;

import org.springframework.web.context.support.WebApplicationContextUtils;

/**

 *

 * @author

 */

@WebService()

public class AddressWebService

{

    @Resource

    private WebServiceContext context;    

    /**

     * Select Address from database.

     * @param id

     * @return

     */

    @WebMethod(operationName = "Select")

    public Address Select(@WebParam(name = "id") AddressId id)

    {

        ServletContext servletContext = (ServletContext) context.getMessageContext().get(MessageContext.SERVLET_CONTEXT);

        WebApplicationContext applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);

        AddressService svr = (AddressService) applicationContext.getBean("AddressService");

        Address address = svr.select(id);

        return address;

    }

    /**

     * Web service operation

     * @param address

     */

    @WebMethod(operationName = "Update")

    public void Update(@WebParam(name = "address") Address address)

    {

        ServletContext servletContext = (ServletContext) context.getMessageContext().get(MessageContext.SERVLET_CONTEXT);

        WebApplicationContext applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);

        AddressService svr = (AddressService) applicationContext.getBean("AddressService");

        svr.update(address);

    }

    /**

     * Web service operation

     * @param address

     */

    @WebMethod(operationName = "Insert")

    public void Insert(@WebParam(name = "address") Address address)

    {

        ServletContext servletContext = (ServletContext) context.getMessageContext().get(MessageContext.SERVLET_CONTEXT);

        WebApplicationContext applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);

        AddressService svr = (AddressService) applicationContext.getBean("AddressService");

        svr.save(address);

    }

    /**

     * Web service operation

     * @param id

     */

    @WebMethod(operationName = "Delete")

    public void Delete(@WebParam(name = "id") AddressId id)

    {

        ServletContext servletContext = (ServletContext) context.getMessageContext().get(MessageContext.SERVLET_CONTEXT);

        WebApplicationContext applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);

        AddressService svr = (AddressService) applicationContext.getBean("AddressService");

        svr.delete(id);

    }

    /**

     * Web service operation

     * @return

     */

    @WebMethod(operationName = "SelectAll")

    public List<Address> SelectAll()

    {

        ServletContext servletContext = (ServletContext) context.getMessageContext().get(MessageContext.SERVLET_CONTEXT);

        WebApplicationContext applicationContext = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);

        AddressService svr = (AddressService) applicationContext.getBean("AddressService");

        return svr.selectAll();

    }

}

@resource注释指示容器创建WebService对象时自动将指定的资源注入对象中。

6.1.2        glassfish

glassfish自身包含了JAX-WS包,部署在glassfish时不必使用JAX-WS包,而且不必加载JAX-WS的Servlet。调整如下:

(1)       删除JAX-WS包;

(2)       编辑WEB-INF/web.xml取消JAX-WS的监听器、Servlet、地址映射;

<!--

    <listener>

        <listener-class>

com.sun.xml.ws.transport.http.servlet.WSServletContextListener

</listener-class>

    </listener>

 

    <servlet>

        <servlet-name>AddressWebService</servlet-name>

        <servlet-class>

com.sun.xml.ws.transport.http.servlet.WSServlet

</servlet-class>

        <load-on-startup>0</load-on-startup>

    </servlet>

 

    <servlet-mapping>

        <servlet-name>AddressWebService</servlet-name>

        <url-pattern>/AddressWebService</url-pattern>

    </servlet-mapping>

-->

6.2    方法二

方法二使用jaxws-spring.jar和xbean-spring.jar,将创建WebService对象任务委托给Spring,因此可以使用Spring的各种服务,但WebService的发布模式与glassfish冲突,因此无法在glassfish中使用。

6.2.1        WEB-INF/web.xml

使用com.sun.xml.ws.transport.http. servlet.WSSpringServlet替换JAX-WS默认的com.sun.xml.ws.transport.http. servlet .WSServlet。

    <servlet>

        <servlet-name>AddressWebService</servlet-name>

        <servlet-class>

com.sun.xml.ws.transport.http.servlet.WSSpringServlet

</servlet-class>

        <load-on-startup>0</load-on-startup>

    </servlet>

    <servlet-mapping>

        <servlet-name>AddressWebService</servlet-name>

        <url-pattern>/AddressWebServiceService</url-pattern>

    </servlet-mapping>

注意:glassfish自动生成的服务名为AddressWebServiceService,为避免更换应用服务器导致客户端修改,因此此处修改Tomcat下的服务名保持与glassfish一致。此外,还需修改WEB-INF/sun-jaxws.xml与之配套。

<endpoint

implementation="websrv.AddressWebService"

name="AddressWebService"

url-pattern="/AddressWebServiceService"/>

6.2.2        WEB-INF/classes/applicationContext.xml

本文件注册WebService对象为bean,并由Spring注入业务逻辑层对象。

<beans

    xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xmlns:aop="http://www.springframework.org/schema/aop"

    xmlns:ws="http://jax-ws.dev.java.net/spring/core"

    xmlns:wss="http://jax-ws.dev.java.net/spring/servlet"

    xsi:schemaLocation="

    http://www.springframework.org/schema/beans

    http://www.springframework.org/schema/beans/spring-beans-2.0.xsd

    http://www.springframework.org/schema/aop   

    http://www.springframework.org/schema/aop/spring-aop.xsd"

    http://jax-ws.dev.java.net/spring/core

    http://jax-ws.dev.java.net/spring/core.xsd

    http://jax-ws.dev.java.net/spring/servlet

    http://jax-ws.dev.java.net/spring/servlet.xsd">

......

<wss:binding url="/AddressWebServiceService">

    <wss:service>

        <ws:service bean="#AddressWebService" />

    </wss:service>

</wss:binding>

   

<bean id="AddressWebService" class="websrv.AddressWebService" >

    <property name="svr">

       <ref bean="AddressService" />

    </property>

</bean>

6.2.2.1      websrv/AddressWebService

/*

 * To change this template, choose Tools | Templates

 * and open the template in the editor.

 */

package websrv;

import business.Address.AddressService;

import java.util.List;

import javax.annotation.Resource;

import javax.jws.Oneway;

import javax.jws.WebMethod;

import javax.jws.WebParam;

import javax.jws.WebService;

import javax.servlet.ServletContext;

import javax.xml.ws.WebServiceContext;

import javax.xml.ws.handler.MessageContext;

import model.entity.Address.Address;

import model.key.Address.AddressId;

import org.springframework.web.context.WebApplicationContext;

import org.springframework.web.context.support.WebApplicationContextUtils;

/**

 *

 * @author

 */

@WebService()

public class AddressWebService

{

    AddressService svr;

    public void setSvr(AddressService svr)

    {

        this.svr = svr;

}

    /**

     * Select Address from database.

     * @param id

     * @return

     */

    @WebMethod(operationName = "Select")

    public Address Select(@WebParam(name = "id") AddressId id)

    {

        return svr.select(id);

    }

    /**

     * Web service operation

     * @param address

     */

    @WebMethod(operationName = "Update")

    public void Update(@WebParam(name = "address") Address address)

    {

        svr.update(address);

    }

    /**

     * Web service operation

     * @param address

     */

    @WebMethod(operationName = "Insert")

    public void Insert(@WebParam(name = "address") Address address)

    {

        svr.save(address);

    }

    /**

     * Web service operation

     * @param id

     */

    @WebMethod(operationName = "Delete")

    public void Delete(@WebParam(name = "id") AddressId id)

    {

        svr.delete(id);

    }

    /**

     * Web service operation

     * @return

     */

    @WebMethod(operationName = "SelectAll")

    public List<Address> SelectAll()

    {

        return svr.selectAll();

    }

}

7          异常处理

Java包含两大类异常,即RuntimeException和Checked Exception异常。

Checked Exception是程序必须进行处理的异常。如果某方法调用抛出Checked Exception异常的方法,则此方法要么捕获该异常,要么声明自身可能抛出该异常。而RuntimeException异常则方法可以不必捕获或继续声明。换句话说,如果应用必须对某异常进行特殊的处理,则应将该异常定义为Checked Exception;而应用只需知道发生了异常,并不关心该异常是什么异常时,则应将该异常定义为RuntimeException。因此,业务需求规定的异常情况应定义为Checked Exception。

Spring的配置式事务环境规定,仅在Spring捕获到RuntimeException异常时才会触发事务回滚。通常情况下发生系统级或业务级异常时,均应回滚事务,显然Spring默认的模式不满足要求,需要进行调整。调整的做法是修改applicationContext.xml配置,指明当发生某Checked Exception异常时,Spring也应做回滚处理。(参见org.springframework. transaction.interceptor. TransactionAttribute)

<bean id="txProxyTemplate"

class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" abstract="true">

        <property name="transactionManager" ref="transactionManager"/>

        <property name="transactionAttributes">

            <props>           

                <prop key="select*">PROPAGATION_REQUIRED,readOnly</prop>

                <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>

                <prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>

                <prop key="save*">PROPAGATION_REQUIRED,-Exception</prop>

                <prop key="update*">PROPAGATION_REQUIRED,-Exception</prop>

                <prop key="remove*">PROPAGATION_REQUIRED,-Exception</prop>

                <prop key="del*">PROPAGATION_REQUIRED,-Exception</prop>

            </props>

        </property>

    </bean>

更改配置后,所有的异常均会导致Spring执行事务回滚处理。如果方法中希望发生某异常时能提交事务,则应在方法中捕获该异常,将其转换为非配置列表的其他异常,从而触发Spring提交事务(目前应没有这样的特殊要求)。

复杂的业务逻辑通常会有较多的业务级异常,这些异常的处理模式也基本相同,而且也需要将这些异常通知界面,以便操作员清楚发生了什么异常。如果完全使用返回码,则程序处处都需要对返回码进行处理,影响程序的质量和可靠性,因此应尽量使用抛出异常模式。

业务级异常有两种可选的方案:一是仅使用一种异常类,由异常对象的错误码判断发生了什么异常;二是为每种异常设计一个异常类,从而形成一个异常体系。前者适合需要特殊处理的异常较多的情况,后者适合个别异常需要特殊处理的情况。由于业务需求的不确定性,系统设计时很难确定需进行特别处理的异常,因此建议使用单异常类多错误码的模式,这样也可以简化程序的设计,仅需要捕获一种异常即可。

7.1    通用异常类

7.2    业务逻辑层异常处理

7.3    WEB层异常处理

7.4 WebService层异常处理

MVC

如果想在另一个页面中显示错误,则可以:

@Override

protected ModelAndView onSubmit(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, BindException arg3) throws Exception

{

    arg3.reject("AddressId", "error message"); // 创建错误对象

Map model = arg3.getModel();

    model.put("AddressId", arg2);              // 添加Command对象到模型

    model.put("errors", arg3);                 // 添加错误到模型

return new ModelAndView(getFormView(), model);

}

如果想在同一个页面中显示错误,则可以:

@Override

protected ModelAndView onSubmit(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, BindException arg3) throws Exception

{

arg3.reject("AddressId.id", "没有此数据!");

    return showForm(arg0, arg1, arg3);

}

JSP:

        <spring:hasBindErrors name="AddressId">

            <br>

            <spring:bind path="AddressId">

                <font color="red">

                    <b>${status.errorMessage}</b>

                </font><br>

            </spring:bind>

            <br>

        </spring:hasBindErrors>

错误页:

8          总结

l         如果某个类可能变更实现方法,则应为其设计接口,以便调用者可以不必修改即可使用新的实现。因此Dao类与业务逻辑类应设计接口,而实体类、主键类、活动类不必设计接口。

l         带状态的bean应配置为多例(prototype),而无状态的bean应配置为单例(single),Web MVC环境下需在多个视图间共享的bean应配置为session,单个视图中使用的bean应配置为request。

l         session和request类型的bean应定义代理类;

l         实体类和主键类是多例的,其他非WEB MVC的类是单例的;

l         为每个数据库对象建立一个实体类和主键类;

l         实体类与主键类必须初始化所有的域;字符串域应trim()。

l         主键类必须提供一个带参的构造函数初始化主键;

l         继承框架类时应增加一个基础类隔离框架类与应用类;

l         实体类、主键类、Dao类不必处理异常;业务逻辑类应截获并处理所有的异常,并将全部异常包括(系统级的异常)转换为应用级异常然后再抛出,以避免影响Spring的配置式事务;Web应截获并处理全部异常,根据异常类型决定显示在当前页做提示,还是显示给专门的错误页面。WebService应将异常转换为返回码、错误码与错误信息,传递给客户端。

posted on 2007-11-20 11:55 飞鹰 阅读(4950) 评论(1)  编辑  收藏

评论:
# re: Spring + Hibernate 实战[未登录] 2009-03-22 00:52 | Edison
1.对RuntimeException在业务逻辑中的应用说的很好,所有的业务异常应该都好包装成RuntimeException,最好在spring里通过CGLIB方式配置无侵入式的Hibernate相关Exception的Handler类,把它们包装成自己的业务异常(RuntimeException子类),呵呵,正在考虑这个问题。
2.要是再结合Spring的CGLIB的特性(XXXDAOIMP无需定义额外的接口如×××DAO),达到一种无侵入式的注入,并能够控制相关方法的事务控制就更完美了,而且可以配置这种方式,达到对所有的dao下的类进行通用配置。
<list>
<value>dao.*</value>
</list>

搜到你的文章,很有帮助。希望一起进步哈。
eMail:hejing0126@126.com msn:edison_jing@126.com  回复  更多评论
  

只有注册用户登录后才能发表评论。


网站导航: