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

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

2007-07-11 22:32 561 查看
本文翻译自: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

Struts 2



Tomcat 5.5



Eclipse



Eclipse WTP



Hibernate Core



Hibernate Annotations



Hibernate Entity Manager



MySql Server



Mysql JDBC Driver



Spring 2.0



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项目

打开Eclipse,我是认真的,你必须打开Eclipse

点击File -> New -> Project. 选择"Dynamic Web Project"并点击下一步(注:如果使用MyEclipse,这里不太一样)

输入项目名,这里我使用"quickstart"。这个项目将要在Tomcat中运行,所以我们需要为它创建应用服务器配置

在"Target Runtime"下面点击"New",选择"Apache Tomcat5.5"并点击下一步

输入Tomcat的安装路径并选择一下已安装的JRE(需要1.5)

现在你应该回到了项目创建向导,并且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"类,它包含少量的实例变量。

创建一个新类并命名为"Person",然后输入"quickstart.model"作为包名。

添加"id"(int), "firstName"(String)和"lastName"(String)三个实例变量,并为它们加上setter/getter方法。

为你的类加上"@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操作的类。

创建一个接口,命名为"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 配置

在"src"目录下创建一个"META-INF"目录

在"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

更新/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(监听器)

在/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作为对象工厂。

打开新建类对话框,输入"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会负责注入。

在"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来改进它。

在/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函数来组装各个域。

在/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



Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1572277
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: