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

本文翻译自:http://cwiki.apache.org/S2WIKI/struts-2-spring-2-jpa-ajax.html

来自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

 
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值