Eclipse开发: Struts 2 + Spring 2 + JPA + AJAX

SSH整合实战
本文介绍如何在Eclipse中配置Struts2、Spring2、JPA及Struts2 Ajax标签的整合开发,实现数据库增删查改功能,并利用Ajax优化用户体验。

来自Struts2 WiKi上的一篇文章,讲解如何在Eclipse + WTP中进行Spring2, Struts2, JPA的整合开发。很基础的说,希望对于想学习SSH的人能有一定的帮助。我在翻译的过程中,也尝试使用MyEclipse来进行整合开发。但由于水平有限,翻译质量不敢恭维,有何错误地方,请尽量告知。谢谢!

晚上调试成功了,现在把几点需要注意的地方说一下!希望你也能顺利的通过调试,有什么问题欢迎大家一起讨论。

 

本指南演示了如何在Eclipse中配置Struts2,并让它与Spring2,Java Persistence API(使用Hibernate)和Struts2 Ajax 标签一起工作。

注意:按指南一步一步来需要Struts2.0.3或更高版本

 

Show me the code

你可以在 zipped Eclipse project下载源代码,必需的依赖库在/WebContent/WEB-INF/lib目录下,需要导入到Eclipse中。

Prerequisites

 

Tomcat

首先要安装好Tomcat,如果在安装的时候遇到任何问题,请查看Tomcat的安装指南

 

MySql

安装并配置MySQL。创建一个名为“quickstart”的数据库,并运行下面脚本来创建“Person”表。后面在applicationContext.xml里,我们将使用"root"数据库用户名和密码,记得用你自己的数据库设置来替换它们。


   
CREATE   TABLE   ' quickstart ' . ' Person '  (
  
' id '   INTEGER  UNSIGNED  NOT   NULL  AUTO_INCREMENT,
  
' firstName '   VARCHAR ( 45 NOT   NULL ,
  
' lastName '   VARCHAR ( 45 NOT   NULL ,
  
PRIMARY   KEY ( ' id ' )
)
ENGINE 
=  InnoDB;

 注:上面的DDL需要保存在文件中,然后在MySQL中导入。我直接复制然后在查询分析器中执行失败

创建Eclipse项目

  1. 打开Eclipse,我是认真的,你必须打开Eclipse
  2. 点击File -> New -> Project. 选择"Dynamic Web Project"并点击下一步(注:如果使用MyEclipse,这里不太一样)
  3. 输入项目名,这里我使用"quickstart"。这个项目将要在Tomcat中运行,所以我们需要为它创建应用服务器配置
    1. 在"Target Runtime"下面点击"New",选择"Apache Tomcat5.5"并点击下一步
    2. 输入Tomcat的安装路径并选择一下已安装的JRE(需要1.5)
  4. 现在你应该回到了项目创建向导,并且Tomcat是你的Target Runtime。点击下一步,选择"Dynamic Web Module"和"Java"facets,最后点"finish"。

(上面讲的都是Eclipse WTP中的配置,如果使用MyEclipse请自行修正)

 

库依赖关系

你的项目应该包含"src","build"和"WebContent"目录。我们把所有必需的jar文件放在"/WebContent/WEB-INF/lib"目录下。请复制它们到${workspace}/quickstart/WebContent/WEB-INF/lib目录。jar文件名的版本号已经被去除了!

JarFrom
xwork.jarStruts 2
struts2-api.jarStruts 2
struts2-core.jarStruts 2
struts2-Spring-plugin.jarStruts 2
ognl.jarStruts 2
freemarker-2.3.4.jarStruts 2
mysql-connector-java.jarMySql JDBC Driver
spring.jarSping 2.0
antlr.jarHibernate Core
asm.jarHibernate Core
asm-attrs.jarHibernate Core
cglib.jarHibernate Core
dom4j.jarHibernate Core
jdbc2_0-stdext.jarHibernate Core
ehcache.jarHibernate Core
hibernate3.jarHibernate Core
xml-apis.jarHibernate Core
commons-collections.jarHibernate Core
ejb3-persistence.jarHibernate Annotations
jta.jarHibernate Annotations
hibernate-annotations.jarHibernate Annotations
hibernate-entitymanager.jarHibernate Entity Manager
javassist.jarHibernate Entity Manager
jboss-archive-browsing.jarHibernate Entity Manager

右击项目点“刷新”,通知Eclipse我们加入了很多的jar文件。

我使用Struts2.0.6, Spring2.0.3, Hibernate3.2。struts2-api.jar找不到,没有也可以运行成功;Hibernate Annotations和Hibernate Entity Manager需要在Hibernate的主页上下载,不包括在Core里面;另外jta.jar和javassist.jar在Hibernate Tools里面,同样要下载;最后,上面列表并缺少一个包,因为Hibernate3.2对此有一点小小的修改,你需要把Hibernate Annotations里面的hibernate-commons-annotations.jar拷贝进来。

 

领域模型

我们的领域模型只有一个简单的"Person"类,它包含少量的实例变量。

  1. 创建一个新类并命名为"Person",然后输入"quickstart.model"作为包名。
  2. 添加"id"(int), "firstName"(String)和"lastName"(String)三个实例变量,并为它们加上setter/getter方法。
  3. 为你的类加上"@Entity"annotation,给"id" 加上 "@Id"和"@GeneratedValue" 注解

你的类如下:

Person.java

   
package  quickstart.model;

import  javax.persistence.Entity;
import  javax.persistence.GeneratedValue;
import  javax.persistence.Id;

@Entity
public   class  Person {
    @Id
    @GeneratedValue
    
private  Integer id;
    
private  String lastName;
    
private  String firstName;

    
public  String getFirstName() {
        
return  firstName;
    }

    
public   void  setFirstName(String firstName) {
        
this .firstName  =  firstName;
    }

    
public  String getLastName() {
        
return  lastName;
    }

    
public   void  setLastName(String lastName) {
        
this .lastName  =  lastName;
    }

    
public  Integer getId() {
        
return  id;
    }

    
public   void  setId(Integer id) {
        
this .id  =  id;
    }
}

@Entity让JPA服务Provider知道这个类可以被持久化。@Id标识"id"域为这个类的主键,@GeneratedValue使id域被提供者(Hibernate)自动生成。类和实例变量默认都被映射到同名的表和列上,详细情况请查看JPA文档。

 

Person service.

我们现在来写对"Person"对象进行CRUD操作的类。

  1. 创建一个接口,命名为"PersonService",包名为"quickstart.service"
PersonService.java

   
package  quickstart.service;

import  java.util.List;

import  quickstart.model.Person;

public   interface  PersonService {
    
public  List < Person >  findAll();

    
public   void  save(Person person);

    
public   void  remove( int  id);

    
public  Person find( int  id);
}

    

     2.  创建一个类,命名为"PersonServiceImpl",包名为"quickstart.service"

PersonServiceImpl.java
package  quickstart.service;

import  java.util.List;

import  javax.persistence.EntityManager;
import  javax.persistence.PersistenceContext;
import  javax.persistence.Query;

import  org.springframework.transaction.annotation.Transactional;

import  quickstart.model.Person;

@Transactional
public   class  PersonServiceImpl  implements  PersonService {
    
private  EntityManager em;

    @PersistenceContext
    
public   void  setEntityManager(EntityManager em) {
        
this .em  =  em;
    }

    @SuppressWarnings(
" unchecked " )
    
public  List < Person >  findAll() {
        Query query 
=  getEntityManager().createQuery( " select p FROM Person p " );
        
return  query.getResultList();
    }

    
public   void  save(Person person) {
        
if  (person.getId()  ==   null ) {
            
//  new
            em.persist(person);
        } 
else  {
            
//  update
            em.merge(person);
        }
    }

    
public   void  remove( int  id) {
        Person person 
=  find(id);
        
if  (person  !=   null ) {
            em.remove(person);
        }
    }

    
private  EntityManager getEntityManager() {
        
return  em;
    }

    
public  Person find( int  id) {
        
return  em.find(Person. class , id);
    }

}
@PersistenceContext会让Spring在实例化的时候给服务注入一个EntityManager。@PersistenceContext注解可以放在实例变量,或者setter方法前面。如果一个类被注解为@Transactional,Spring将会确保类的方法在运行在一个事务中。

JPA 配置

  1. 在"src"目录下创建一个"META-INF"目录
  2. 在"META-INF"目录下创建一个名为"persistence.xml"的文件。
persistence.xml

    
< persistence  xmlns ="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation
="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
    version
="1.0" >
    
< persistence-unit  name ="punit" >
    
</ persistence-unit >
</ persistence >

JPA configuration can be set on this file. On this example it will be empty because the datasource configuration will be on the Spring configuration file. JPA的配置信息可以在这个文件中设置。本例中该文件为空,因为数据源(datasource)配置放在Spring的配置文件中。

 

Spring

  1. 更新/WebContent/WEB-INF/web.xml文件为以下内容:
web.xml
<? xml version="1.0" encoding="UTF-8" ?>
< web-app  id ="person"  version ="2.4"  xmlns ="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation
="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" >
    
< display-name > person </ display-name >
    
< filter >
        
< filter-name > struts2 </ filter-name >
        
< filter-class >
            org.apache.struts2.dispatcher.FilterDispatcher
        
</ filter-class >
    
</ filter >

    
< filter-mapping >
        
< filter-name > struts2 </ filter-name >
        
< url-pattern > /* </ url-pattern >
    
</ filter-mapping >


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

    
< listener >
        
< listener-class >
            org.springframework.web.context.ContextLoaderListener
        
</ listener-class >
    
</ listener >
</ web-app >
这会使容器将所有请求转发给Struts的"FilterDispatcher"类。"index.jsp"被设为主页,Spring的"ContextLoaderListener"被配置为listener(监听器)
  1. 在/WebContent/WEB-INF目录下创建一个名为"applicationContext.xml"的文件,内容如下:
applicationContext.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"
    xmlns:tx
="http://www.springframework.org/schema/tx"
    xsi:schemaLocation
="
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"
>

    
< bean
        
class ="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"   />

    
< bean  id ="personService"  class ="quickstart.service.PersonServiceImpl"   />

    
< bean  id ="entityManagerFactory"
        class
="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" >
        
< property  name ="dataSource"  ref ="dataSource"   />
        
< property  name ="jpaVendorAdapter" >
            
< bean
                
class ="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" >
                
< property  name ="database"  value ="MYSQL"   />
                
< property  name ="showSql"  value ="true"   />
            
</ bean >
        
</ property >
    
</ bean >

    
< bean  id ="dataSource"
        class
="org.springframework.jdbc.datasource.DriverManagerDataSource" >
        
< property  name ="driverClassName"  value ="com.mysql.jdbc.Driver"   />
        
< property  name ="url"  value ="jdbc:mysql://localhost/test"   />
        
< property  name ="username"  value ="root"   />
        
< property  name ="password"  value ="root"   />
    
</ bean >

    
< bean  id ="transactionManager"
        class
="org.springframework.orm.jpa.JpaTransactionManager" >
        
< property  name ="entityManagerFactory"  ref ="entityManagerFactory"   />
    
</ bean >

    
< tx:annotation-driven  transaction-manager ="transactionManager"   />

    
< bean  id ="personAction"  scope ="prototype"
        class
="quickstart.action.PersonAction" >
        
< constructor-arg  ref ="personService"   />
    
</ bean >
</ beans >
注意"personAction"bean的"class"属性被设为Action类的名字,并且"personService"bean会作为参数传递到action的构造器中。改变"dataSource"Bean的"url", "username"和"passwrod"属性为你数据库的值。更多beans设置的细节,请参看Spring的文档。"scope"是Spring2新增的属性,它意味着Spring会在该类型的对象被请求时创建一个新的PersonAction对象。在Struts2里,一个新的action对象被创建,用来为每个请求服务,这就是我们为什么需要scope="prototype"。

Struts

 现在我们需要创建一个简单的Struts action,它封装了PersonServices的方法。并且我们配置Struts使用Spring作为对象工厂。

  1. 打开新建类对话框,输入"PersonAction"作为类名,包名为"quickstart.action",内容如下:
PersonAction.java

   
package  quickstart.action;

import  java.util.List;

import  quickstart.model.Person;
import  quickstart.service.PersonService;

import  com.opensymphony.xwork2.Action;
import  com.opensymphony.xwork2.Preparable;

public   class  PersonAction  implements  Preparable {
    
private  PersonService service;
    
private  List < Person >  persons;
    
private  Person person;
    
private  Integer id;

    
public  PersonAction(PersonService service) {
        
this .service  =  service;
    }

    
public  String execute() {
        
this .persons  =  service.findAll();
        
return  Action.SUCCESS;
    }

    
public  String save() {
        
this .service.save(person);
        
this .person  =   new  Person();
        
return  execute();
    }

    
public  String remove() {
        service.remove(id);
        
return  execute();
    }

    
public  List < Person >  getPersons() {
        
return  persons;
    }

    
public  Integer getId() {
        
return  id;
    }

    
public   void  setId(Integer id) {
        
this .id  =  id;
    }

    
public   void  prepare()  throws  Exception {
        
if  (id  !=   null )
            person 
=  service.find(id);
    }

    
public  Person getPerson() {
        
return  person;
    }

    
public   void  setPerson(Person person) {
        
this .person  =  person;
    }
}

看,我的action是一个简单的POJO!

"Preparable"接口指示Struts去调用"prepare"方法,如果"PrepareInterceptor"被应用在action上(默认就是这样子的)。action的构造器有一个"PersonService"参数,在action被实例化的时候Spring会负责注入。

 

  1. 在"src"目录下创建一个名为"struts.xml"的文件,内容如下:
struts.xml

    
<? xml version="1.0" encoding="UTF-8"  ?>
<! DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd"
>
< struts >
    
< constant  name ="struts.objectFactory"  value ="spring"   />
    
< constant  name ="struts.devMode"  value ="true"   />

    
< package  name ="person"  extends ="struts-default" >

        
< action  name ="list"  method ="execute"  class ="personAction" >
            
< result > pages/list.jsp </ result >
            
< result  name ="input" > pages/list.jsp </ result >
        
</ action >

        
< action  name ="remove"  class ="personAction"  method ="remove" >
            
< result > pages/list.jsp </ result >
            
< result  name ="input" > pages/list.jsp </ result >
        
</ action >

        
< action  name ="save"  class ="personAction"  method ="save" >
            
< result > pages/list.jsp </ result >
            
< result  name ="input" > pages/list.jsp </ result >
        
</ action >
    
</ package >

</ struts >

设置"struts.objectFactory"为"spring"会强制Struts使用Spring来实例化action,并注入所有定义在applicationContext.xml中的依赖关系。每个action别名的"class"属性被设置为"personAction",这也就是我们在applicationContext.xml中定义的PersonAction bean。要让Struts与Spring一起工作,我们仅仅需要做上面这点事情。

 

页面设计

我们只有两个页面,"index.jsp"和"list.jsp"。"list.jsp"返回数据库中所有person的列表。我们把这个列表放到一个不同的页面上,是因为我们将要添加一点ajax来改进它。

  1.  在/WebContent/pages目录下创建一个名为"list.jsp" 的新文件,它的内容如下:
list.jsp
<% @ taglib prefix = " s "  uri = " /struts-tags " %>

< p > Persons </ p >
< s:if  test ="persons.size > 0" >
    
< table >
        
< s:iterator  value ="persons" >
            
< tr  id ="row_<s:property value=" id" /> ">
                
< td >
                    
< s:property  value ="firstName"   />
                
</ td >
                
< td >
                    
< s:property  value ="lastName"   />
                
</ td >
                
< td >
                    
< s:url  id ="removeUrl"  action ="remove" >
                        
< s:param  name ="id"  value ="id"   />
                    
</ s:url >
                    
< s:a  href ="%{removeUrl}"  theme ="ajax"  targets ="persons" > Remove </ s:a >
                    
< s:a  id ="a_%{id}"  theme ="ajax"  notifyTopics ="/edit" > Edit </ s:a >
                
</ td >
            
</ tr >
        
</ s:iterator >
    
</ table >
</ s:if >
上面代码呈现了一个表格,每行代表一个Person,包含first name,last name,以及一个链接用来删除person,一个链接用来编辑person。删除链接有一个"targets"属性,设置为"persons",表示当用户点击它时,一个异步的请求会被传递给"remove" action(struts.xml配置"remove"指向PersonAction的 remove方法),传递person  id作为参数。
注意list.jsp是在pages目录下,如果你放在其它目录,则需要相应修改struts.xml文件

当编辑链接被点击时,它会打开/edit主题,这将触发一个javascript函数来组装各个域。

  1. 在/WebContent目录下创建一个新文件"index.jsp",它的内容如下:
index.jsp
<% @ taglib prefix = " s "  uri = " /struts-tags " %>
< html >
    
< head >
        
< s:head  theme ="ajax"  debug ="true" />
        
< script  type ="text/javascript" >
            dojo.event.topic.subscribe(
" /save " function (data, type, request) {
                
if (type  ==   " load " ) {
                    dojo.byId(
" id " ).value  =   "" ;
                    dojo.byId(
" firstName " ).value  =   "" ;
                    dojo.byId(
" lastName " ).value  =   "" ;
                }
            });

            dojo.event.topic.subscribe(
" /edit " function (data, type, request) {
                
if (type  ==   " before " ) {
                    
var  id  =  data.split( " _ " )[ 1 ];

                    
var  tr  =  dojo.byId( " row_ " + id);
                    
var  tds  =  tr.getElementsByTagName( " td " );

                    dojo.byId(
" id " ).value  =  id;
                    dojo.byId(
" firstName " ).value  =  dojo.string.trim(dojo.dom.textContent(tds[ 0 ]));
                    dojo.byId(
" lastName " ).value  =  dojo.string.trim(dojo.dom.textContent(tds[ 1 ]));
                }
            });
        
</ script >
    
</ head >
    
< body >
        
< s:url  action ="list"  id ="descrsUrl" />

        
< div  style ="width: 300px;border-style: solid" >
            
< div  style ="text-align: right;" >
                
< s:a  theme ="ajax"  notifyTopics ="/refresh" > Refresh </ s:a >
            
</ div >
            
< s:div  id ="persons"  theme ="ajax"  href ="%{descrsUrl}"  loadingText ="Loading..."  listenTopics ="/refresh" />
        
</ div >

        
< br />

        
< div  style ="width: 300px;border-style: solid" >
            
< p > Person Data </ p >
            
< s:form  action ="save"  validate ="true" >
                
< s:textfield  id ="id"  name ="person.id"  cssStyle ="display:none" />
                
< s:textfield  id ="firstName"  label ="First Name"  name ="person.firstName" />
                
< s:textfield  id ="lastName"  label ="Last Name"  name ="person.lastName" />
                
< s:submit  theme ="ajax"  targets ="persons"  notifyTopics ="/save" />
            
</ s:form >
        
</ div >
    
</ body >
</ html >

看,并没有页面刷新!

"personx" div会异步地装载它的内容,当请求被处理时显示"Loading..."提示(你可以使用"indicator"属性得到更佳的进度反馈),你可以通过点击"Refresh"链接来强制页面进行刷新。"submit"按钮,向action "save"提交一个异步的请求(PersonAction的"save"方法)

 

Validation验证

我们并不想我们的数据库存在任何的无名氏,所以我们给Form增加一点基本的客户端验证。在Struts2中,验证可以被放在xml文件里,命名模式为:ActionName-validation.xml,放在与action相同的包路径下。要给action的特定别名添加验证(比如方法),validation文件的命名必须为:ActionName-alias-validation.xml,这里的"alias"就是你的action的别名(这里也就是方法名,如"save")。在src/quickstart/action目录下添加一个名为"PersonAction-save-validation.xml"文件,它的内容如下:

<! DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN"
       "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd"
>
< validators >
    
< field  name ="person.firstName" >
        
< field-validator  type ="requiredstring" >
            
< message > First name is required! </ message >
        
</ field-validator >
    
</ field >
    
< field  name ="person.lastName" >
        
< field-validator  type ="requiredstring" >
            
< message > Last name is required! </ message >
        
</ field-validator >
    
</ field >
</ validators >

关于现有的validator,以及如何编写和插入你自己的validator,请查看Struts文档

要运行项目,右击你的项目并选择Run AS -> Run on Server。你也可以通过相同的办法来调试它,右击项目 Debug As -> Debug on Server。下载并安装Struts 2的 showcase演示项目来查看更多的例子。

参考资料

Struts
Spring JPA Doc
JPA and Spring Tutorial
Eclipse Dali

 
 
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值