CSDN博客

img wzh695

struts2+spring+jpa+ajax

发表于2008/10/1 10:13:00  1453人阅读

译文(字数3501

Musachy Barroso添加, Dave Newton 2008216日最后便捷,  (查看更新) 显示内容hide comment

 

Comment: "on the Spring..." – "in the Spring" fix as per comment.
View page history

在这篇教程中将为大家展示如何在eclipse中使用struts2, 并用它与spring整合,使用java 持久化api (使用hibernate) Struts 2 Ajax 标签.

Hibernate 已经 加入了 LGPL , 任何使用Hibernate 开发的应用程序都应遵循 LGPL.

 

接下来这篇教程回详细介绍struts2的部署比2.03需要更多的地方

先决条件

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;

获取源代码

观看源代码

你可以直接下载 这个 被压缩的工程 , 然后将需要的包放到/WebContent/WEB-INF/lib 文件夹下 (  工程的根目录下) 然后将它载入到eclipse.

使用 maven 的方式

为了能够运行这个工程你需要安装maven.

1.     下载压缩好的 工程

2.     在这里下载 here jta jar .

o    注意下载管理器可能会将这个文件保存在根目录上, 并且这个文件是以 a .ZIP作为扩展名的 . 你可以将这个文件重命名为 jta-1.1-classes.jar.

o    如果有新的版本可以使用,你可以在下一步更新.

3.     安装jta jar 文件使它运行:

$ mvn install:install-file -DgroupId=javax.transaction -DartifactId=jta -Dversion=1.1 -Dpackaging=jar -Dfile=c:/path/to/jar/jta-1.1-classes.jar

4.     要保持耐心, 我们都使一样的心情

5.     切换到 quickstart 然后 运行:

$ mvn jetty:run

6.     将你的浏览器输入如下地址http://localhost:8080/quickstart

7.     创建一个eclipse运行的工程:

$ mvn eclipse:eclipse

或者(创建一个web工程使用wtp):

mvn eclipse:eclipse -Dwtpversion=1.0

自己动手完成

创建 Eclipse 工程

1.     打开 Eclipse. 当然, 你必须打开 Eclipse.

2.     选择 File -> New -> Project. "Web" 文件夹下, 选择 "Dynamic Web Project" 然后 "Next".

3.     输入工程的名字, "quickstart"按照这个名字输入. 工程会在Tomcat中运行, 搜易我们必须为工程配置一个服务器.

1.     "Target Runtime", "New", 选择 "Apache Tomcat 5.5" and 点击 next.

2.     Enter Tomcat's installation directory and select an installed JRE (1.5 is required)

4.     Now you should be back to the project creation wizard, with Tomcat as your Target Runtime. Click "Next". Select "Dynamic Web Module" and "Java" facets, and click "Finish".

依赖

你的工程应该包含"src", "build" and "WebContent"这个三个文件夹. 我们将会吧所有需要的 jar 放到 "/WebContent/WEB-INF/lib" 想吧文件添加到 "lib" 文件夹下, 只需要把他们拷到 ${workspace}/quickstart/WebContent/WEB-INF/lib, ${workspace}是值eclipse工作台的目录.

在这张表中, 是在这个版本中被移除的jar,因为在不久的将来就会有新的变化.选择任何你熟悉的产品.

JAR

From

License

xwork.jar

Struts 2

Apache License

struts2-core.jar

Struts 2

 

struts2-spring-plugin.jar

Struts 2

 

ognl.jar

Struts 2

 

freemarker.jar

Struts 2

 

commons-logging-api.jar

Struts 2

 

mysql-connector-java.jar

MySql JDBC Driver

MySQL licensing policy

spring.jar

Spring 2.0

Apache License

antlr.jar

Hibernate Core

LGPL

asm.jar

Hibernate Core

 

asm-attrs.jar

Hibernate Core

 

cglib.jar

Hibernate Core

 

dom4j.jar

Hibernate Core

 

jdbc2_0-stdext.jar

Hibernate Core

 

ehcache.jar

Hibernate Core

 

hibernate3.jar

Hibernate Core

 

xml-apis.jar

Hibernate Core

 

commons-collections.jar

Hibernate Core

 

ejb3-persistence.jar

Hibernate Annotations

LGPL

jta.jar

Hibernate Annotations

 

hibernate-commons-annotations.jar

Hibernate Annotations

 

hibernate-annotations.jar

Hibernate Annotations

 

hibernate-entitymanager.jar

Hibernate 实体管理

LGPL

javassist.jar

Hibernate Entity Manager

 

jboss-archive-browsing.jar

Hibernate Entity Manager

 

在工程上点击右键选择更新(通知 Eclipse更新我们刚刚安装的jar文件).

领域

我们的领域模型只是一个有两个字段的简单的类

1.     创建一个名叫 "Person" 的类(File -> New -> Class), 输入 "quickstart.model" 作为包名.

2.     添加变量 "id" (int), "firstName" (String), and lastName ("String")和他们的 setter/getter 方法.

3.     为你的类标注 "@Entity" 注释,  "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 可以让支持着知道这个实体可以被持久化,@Id标注 "id" 字段 意思为该字段为主键. @GeneratedValue 意思这个字段的值将会被持久化工具创建(Hibernate). 类和字段都以相同的名字默然映射到表上,详细信息 可以参考 JPA's 文档

Person 服务.

我们将要编写一个管理person对象的 CRUD 操作的类.

1.     创建一个新的接口 (File -> New -> Interface), 输入 "PersonService" 作为名字, 然后 "quickstart.service" 用作 namespace. 把它的内容修改成如下所示样子:

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);

}

1.     创建一个新的类 (File -> New -> Class), 输入 "PersonServiceImpl" 作为名字然后 "quickstart.service" 作为 namespace. 它的内容如下:

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 service当它初始化的时候.  @PersistenceContext 注释 是能够替换的 或者使用setter方法 ,如果一个类被注释成 @Transactional, Spring 会确保它的方法在transaction之中运行.

JPA 配置

1.     创建一个名叫 "META-INF" 的文件夹在 "src" 文件夹中.

2.     创建 "persistence.xml" "META-INF" 文件夹之下,它的内容如下:

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的配置文可以在个文件中设置. 在这个例子中它是空的,因为datasource 将会配配置到Spring 的配置文件中.

Spring

1.     更新 web.xml ,它在/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>

 

    <!-- Include this if you are using Hibernate -->

    <filter>

        <filter-name>Spring OpenEntityManagerInViewFilter</filter-name>

        <filter-class>

            org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter

        </filter-class>

    </filter>

 

    <filter-mapping>

        <filter-name>Spring OpenEntityManagerInViewFilter</filter-name>

        <url-pattern>/*</url-pattern>

    </filter-mapping>

 

    <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" c. "index.jsp" 被设置为首页, 然后使用 Spring's "ContextLoaderListener" 作为监听器.

1.     创建一个名字叫 "applicationContext.xml" 配置文件,它在/WebContent/WEB-INF,将它的内容设置如下:

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/quickstart" />

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

注意bean "personAction""class" 属性 被设置为 action 的名字, 然偶 "personService" bean 将会传递一个参数用作action的构建. 修改 "url", "username" and "password" "dataSource" bean 按照你数据库的配置. 如果想要了解其他bean的配置可以参考spring的文档. "scope" 属性 是新在spring2中新添加的, 它的意思是说Spring 会为每一个请求创建一个新的 PersonAction 对像.但是在struts2中创建的action是不间断服务的, 因此需要吧这个属性设置为"prototype".

Struts

下面我将创建一个action去实现刚才创建服务类, 接下来我们来配置strutsspring作为对象工厂.

1.     打开创建新类的对话框(File -> New -> Class) 输入 "PersonAction" 作为类名,  "quickstart.action" 作为 namespace. 它的内容如下:

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;

    }

}

看看我写的类就是一个简单的POJO!
 "Preparable"
将会构建 Struts 调用 "prepare"方法 iaction 使用了"PrepareInterceptor" 拦截器 (但是默认是实现的).这个action的构建用 "PersonService" 作为一个参数, 这个参数将会在action初始化的时候由spring注入.

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

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的工厂进行初始化, 将会注入所有配置在applicationContext.xml文件中的bean. "class" 属性用的是 "personAction"的别名这个 别名在applicationContext.xml文件中 PersonAction class定义的.做到这些久足以将strutsspring放在一起使用.

页面

我们仅仅只有两个页面, "index.jsp" "list.jsp". "list.jsp" 返回数据库中的一个表的值.我们会让这个结果在不同的页面显示,因为我们将要使用ajax技术.

1.     新建一个文件命名为"list.jsp" 将它放在 /WebContent/pages/ 之下,将它的内容设置为:

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的连接, 一个用来修改的链接. 这个链接有个"targets"属性, 将它的值设置为 "persons",这意味着当用户点击这个请求, 一个异步的请求将会发送到  "remove" action (  已经在struts.xml配置, "remove" 指向"remove" PersonActionremove方法), personid作为传递参数.

当点击编辑按钮的时候, 它将会发布 "/edit" topic, 这个topic将会出发js函数去天厨表单域.

1.     创建一个新的页面"index.jsp" 把它放在 /WebContent 之下,将它的内容设置如下:

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>

看啊,没有页面的刷新!
T "persons"
这个div会异步更新它的信息, 它还会显示 "Loading..." 提示,当请求正在进行的时候(你也可以使用一个指示器,这样会有更好的效果), 你也可以强制它刷新通过使用"Refresh" 链接.这个 "submit" 按钮, 将会向 "save" ("save" method on PersonAction)发送一个异步的请求,然后会发布 "/save" 的主题,这个消息会被预定它的事件接受,使用"dojo.event.topic.subscribe"清除表单域.

验证

因为我不想在数据库花费精力, 我们可以对表单进行简单的客户端校验. Struts 2, 可以配置一个路径文件形如 ActionName-validation.xml的文件,把它放到你action的所在的包中.如果要对你个action的别名进行验证 (像是一个方法), 验证文件的配置可以类似这样 ActionName-alias-validation.xml, "alias" action的别名 (在这次情况下是一个方法名, "save").将文件名字设定为"PersonAction-save-validation.xml" ,放在 /src/quickstart/action之下, 它的内容是这样的:

<!DOCTYPE validators PUBLIC

"-//OpenSymphony Group//XWork Validator 1.0//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>

你可以通过查看struts的文档了解已经存在的验证器, 学习如何去写,如何使插件, 和你自己的验证器.

想运行这个工程,在你工程上点击右键 Run As -> Run on Server.你也可以使用同样的方法对它进行排错, 工程上点击右键Debug As -> Debug on Server.下载安装Struts 2示例可以学习更多的例子.

Using Toplink Essentials instead of Hibernate

1.     将下面的放入pom.xml

2.  <repositories>

3.       <repository>

4.           <id>java.net</id>

5.           <url>https://maven-repository.dev.java.net/nonav/repository</url>

6.           <layout>legacy</layout>

7.       </repository>

 </repositories>

8.     将这个节点放入 pom.xml

9.  <dependency>

10.     <groupId>toplink.essentials</groupId>

11.     <artifactId>toplink-essentials</artifactId>

12.     <version>2.0-38</version>

13.     <exclusions>

14.         <exclusion>

15.             <groupId>javax.transaction</groupId>

16.             <artifactId>jta</artifactId>

17.         </exclusion>

18.     </exclusions>

</dependency>

19.  替换 jpaVendorAdapter 元素,它位于 applicationContext.xml 中,修改后的内容如下:

20.<property name="jpaVendorAdapter">

21.    <bean class="org.springframework.orm.jpa.vendor.TopLinkJpaVendorAdapter">

22.        <property name="databasePlatform" value="oracle.toplink.essentials.platform.database.MySQL4Platform" />

23.        <property name="generateDdl" value="true" />

24.        <property name="showSql" value="true" />

25.    </bean>

</property>

 

0 0

相关博文

我的热门文章

img
取 消
img