前面几篇博客学习了spring中IOC,和springAOP以及spring通过jdbc操作数据库的方法。我们的目标是学习springMVC,那么在正式学习springMVC之前呢,我还是想到先写一篇文章来讲解S2SH整合吧,这样大家简单了解了structs2,对于后面学习springMVC也是有很大好处的。
我们先来整合spring和hibernate吧,我这里新建一个web工程叫做ssh。
将spring和hibernate需要的jar文件添加到WEB-INFO下的lib目录中。需要的jar文件如下:
hibernate3.jar
hibernate-jpa-2.0-api-1.0.0.Final.jar
antlr-2.7.6.jar
commons-collections-3.1.jar
dom4j-1.6.1.jar
javassist-3.12.0.GA.jar
jta-1.1.jar
slf4j-api-1.6.1.jar
spring.jar
commons-logging.jar
log4j-1.2.15.jar
mysql-connector-java-3.0.10-stable-bin.jar
commons-dbcp.jar
commons-pool.jar
创建实体类和映射对象,这里我依然通过一个user实体来操作,因此我需要新建一个Userinfo实体类,如下:
package com.test.entity;
import java.io.Serializable;
public class Userinfo implements Serializable {
private int uid;
private String uname;
private String upass;
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
public String getUname() {
return uname;
}
public void setUname(String uname) {
this.uname = uname;
}
public String getUpass() {
return upass;
}
public void setUpass(String upass) {
this.upass = upass;
}
}
这里,由于UserInfo实体类,以后会在网络上传输,所以需要实现Serializable接口。然后需要创建对应的实体类映射,我们需要在实体类同样的包下,新建一个Userinfo.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.test.entity.Userinfo" table="userinfo">
<id name="uid" column="uid">
<generator class="native"></generator>
</id>
<property name="uname"></property>
<property name="upass"></property>
</class>
</hibernate-mapping>
这里class标签中的name的值就是需要映射的实体类的全类名,table的值就是数据库中该实体类对应的表的名称,有一个id,标签,就是该userinfo表所对应的主键,这里我们逐渐的生成测试采用”native”,该中方式可以根据不同的底层数据库采用不同的主键生成方式,说明一下genernator的属性吧。
在hibernate中genernator的属性一共有七种,我们来看几个常用的:
1.identity用于mysql数据库的递增,注意需要在mysql中建表的时候将对应的字段设置为auto_increment
2.sequence用于orecale数据库
3.native 跨数据库时候使用,屏蔽了底层的方言
4.assigned 用户自定义id
5.foreign 用于外键关联时候使用
6.increment 用于为long,short,int类型生成唯一的表示,在高并发下不能使用
表示实体类对应到表中的字段,name表示实体类中的属性,如果我们不为该property标签指定column=”“属性,那么对应的userinfo表中的字段名称和类中的属性名是相同的,如果指定了column=”“,比如 此时userinfo表中uname属性对应的字段名称就叫做”t_uname”
编写dao以及实现类,用来操作数据库的(dao需要集成HibernateDaoSupport),首先编写一个UserinfoDao的接口:
package com.test.dao;
import java.util.List;
import com.test.entity.Userinfo;
public interface UserinfoDao {
public int addUserinfo(Userinfo user);
public int updateUserinfo(Userinfo user);
public int deleteUserinfo(int uid);
public List<Userinfo> getAll();
public Userinfo getUserinfoById(int uid);
}
可以看到,在该接口中定义了”增删改查”四个方法,然后编写UserinfoDao的实现类,UserinfoDaoImpl.java
package com.test.dao.impl;
import java.util.List;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import com.test.dao.UserinfoDao;
import com.test.entity.Userinfo;
public class UserinfoDaoImpl extends HibernateDaoSupport implements UserinfoDao {
public int addUserinfo(Userinfo user) {
int count=0;
try {
this.getHibernateTemplate().save(user);
count=1;
} catch (DataAccessException e) {
e.printStackTrace();
}
return count;
}
public int deleteUserinfo(int uid) {
int count=0;
Userinfo user = getUserinfoById(uid);
try {
this.getHibernateTemplate().delete(user);
count=1;
} catch (DataAccessException e) {
e.printStackTrace();
}
return count;
}
public List<Userinfo> getAll() {
return this.getHibernateTemplate().find("from Userinfo");
}
public Userinfo getUserinfoById(int uid) {
return (Userinfo)this.getHibernateTemplate().get(Userinfo.class, uid);
}
public int updateUserinfo(Userinfo user) {
int count=0;
try {
this.getHibernateTemplate().update(user);
count=1;
} catch (DataAccessException e) {
e.printStackTrace();
}
return count;
}
}
我们可以看到通过this.getHibernateTemplate();就可以得到HibernateTemplate 对象,然后通过该对象操作数据库是很方便的,HibernateTemplate 为我们提供的方法,我们只需要将对应的实体类,或该实体类的id传进去就可以操作数据库了,大大的提高了开发效率。
下面我写一个UserinfoBiz接口,并且让UserinfoBizImpl实现该接口同时在UserinfoBizImpl中声明一个UserInfoDao的属性,并且设置set方法,这样做是为了后边将UserInfoDao通过IOC注入给UserinfoBizImpl。
package com.test.biz;
import java.util.List;
import com.test.entity.Userinfo;
public interface UserinfoBiz {
public int addUserinfo(Userinfo user);
public int updateUserinfo(Userinfo user);
public int deleteUserinfo(int uid);
public List<Userinfo> getAll();
public Userinfo getUserinfoById(int uid);
}
package com.test.biz.impl;
import java.util.List;
import com.test.biz.UserinfoBiz;
import com.test.dao.UserinfoDao;
import com.test.entity.Userinfo;
public class UserinfoBizImpl implements UserinfoBiz {
private UserinfoDao userDao;
public void setUserDao(UserinfoDao userDao) {
this.userDao = userDao;
}
public int addUserinfo(Userinfo user) {
return userDao.addUserinfo(user);
}
public int deleteUserinfo(int uid) {
return userDao.deleteUserinfo(uid);
}
public List<Userinfo> getAll() {
return userDao.getAll();
}
public Userinfo getUserinfoById(int uid) {
return userDao.getUserinfoById(uid);
}
public int updateUserinfo(Userinfo user) {
return userDao.updateUserinfo(user);
}
}
创建spring容器,配置数据源和sessionFactory,在src下新建spring的配置文件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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<!-- 驱动名称 -->
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<!-- url -->
<property name="url" value="jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=UTF-8"></property>
<!-- 用户名 -->
<property name="username" value="root"></property>
<!-- 密码 -->
<property name="password" value="root"></property>
</bean>
</beans>
接下来配置sessionFactory,并将datasource注入:
<!-- 配置sessionFactory,注入dataSource -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<!-- 注入dataSource -->
<property name="dataSource" ref="dataSource"></property>
<!-- 引入映射文件的位置 -->
<property name="mappingResources">
<list>
<value>com/test/entity/Userinfo.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
</props>
</property>
</bean>
在org.springframework.orm.hibernate3.LocalSessionFactoryBean中有一个dataSource属性,就是将上面配置的dataSource注入给该属性。
<property name="mappingResources">
<list>
<value>com/test/entity/Userinfo.hbm.xml</value>
</list>
</property>
mappResources,就是映射所有的实体类到数据库中的,注意这里是一个list,由于我们现在只有一个UserInfo,所以只写了这么一个,这里value的值,是实体类对应的映射文件的路径。
<property name="hibernateProperties">
<props>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect
</prop>
</props>
</property>
hibernateProperties配置的是数据库的方言,即hibernate.dialect的值。这里是mysql的方言。hibernate.show_sql的值是true表示在程序运行期间是否打印sql语句,在开发调试期间最好打开,这样便于观察生成的sql语句是否正常。
接下来需要配置Dao,并将sessionFactory注入:
<!-- 配置Dao,并注入sessionFactory -->
<bean id="userDao" class="com.test.dao.impl.UserinfoDaoImpl">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
这里整个过程和spring的IOC配置是一样的,其实这里就是spring的ioc配置,最后需要配置Biz,并将Dao注入。
<!-- 配置Biz,并注入Dao -->
<bean id="userBiz" class="com.test.biz.impl.UserinfoBizImpl">
<property name="userDao" ref="userDao"></property>
</bean>
这里,因为我在UserinfoBizImpl类中声明了userDao属性,并且为该属性设置了set方法,可以通过spring来为其赋值,这里我是通过ref引用上一步定义的userDao,可以发现我们整个过程都是一步一步通过IOC赋值的:
dataSource–>sessionFactory–>Dao–>Biz
此时,万事俱备,只欠东风,新建一个dataSource中配置的数据库mydb,然后新建一个userinfo表,该表有三个字段uid,uname,upass,注意建表的时候字段类型需要和实体类中对应哦。创建好了表以后,就可以编写测试类来测试我们的spring和hbiernate整合是否成功。
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
UserinfoBiz userBiz = (UserinfoBiz)ac.getBean("userBiz");
Userinfo user = new Userinfo();
user.setUname("bb");
user.setUpass("123");
userBiz.addUserinfo(user);
List<Userinfo> list = userBiz.getAll();
for (Userinfo userinfo : list) {
System.out.println(userinfo.getUid()+"\t"+userinfo.getUname()+"\t"+userinfo.getUpass());
}
这里,我首先插入一条记录到userinfo表中,然后调用getAll()查询所有数据,打印如下:
Hibernate: insert into userinfo (uname, upass) values (?, ?)
Hibernate: select userinfo0_.uid as uid0_, userinfo0_.uname as uname0_, userinfo0_.upass as upass0_ from userinfo userinfo0_
1 bb 123
可以看到,这时查询所用的sql语句也一并给我们打印出来了,此时spring和hibernate整合就成功了,可是有时候在对数据库进行增删改查的时候,难免会需要使用事物,接下来我们就来配置hibernate事物。
<!-- 配置声明式事务 -->
<!-- 配置事务管理器,注入sessionFactory -->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!-- 配置事务通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="*" propagation="SUPPORTS"/>
</tx:attributes>
</tx:advice>
<aop:config>
<!-- 定义切入点 -->
<aop:pointcut expression="execution(* com.test.biz.*.*(..))" id="bizMethods"/>
<!-- 将事务通知和切入点告知给通知者 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="bizMethods"/>
</aop:config>
配置hibernate事物主要有一下几步:
1.配置事务管理器,注入sessionFactory
2.配置事务通知
3.定义切入点,将事务通知和切入点告知给通知者
在配置事物通知的时候,我将以add,update,delete开头的方法是必须开启事物的,另外一个”*”表示如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行,propagation有一下一些值:
REQUIRED: 如果存在一个事务,则支持当前事务。如果没有事务则开启
SUPPORTS: 如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行
MANDATORY: 如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常
REQUIRES_NEW: 总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起
NOT_SUPPORTED: 总是非事务地执行,并挂起任何存在的事务
NEVER: 总是非事务地执行,如果存在一个活动事务,则抛出异常
NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按 TransactionDefinition.PROPAGATION_REQUIRED 属性执行
这里我们的切入点是com.test.biz包下的所有方法,在说一遍吧:
第一个”*”表示方法的返回值类型
第二个”*”表示类名
第三个”*”表示方法名
第四个”..”表示方法中存在0个或者多个参数。
记住,添加aop所需要的jar:aspectjrt.jar,aspectjweaver.jar
此时hibernate的事物就配置好了,此时当我运行add,update,delete方法时都会开启事物,如果操作失败,事物将会回滚。接下来就是将struts2整合进来拉。
同样将strust2整合进来,
首先需要添加struts2的jar文件:
commons-fileupload-1.2.2.jar
commons-io-2.0.1.jar
commons-lang3-3.1.jar
commons-logging-1.1.1.jar
freemarker-2.3.19.jar
javassist-3.11.0.GA.jar
ognl-3.0.5.jar
sqljdbc.jar
struts2-core-2.3.4.1.jar
struts2-dojo-plugin-2.3.4.1.jar
xwork-core-2.3.4.1.jar
同时需要添加struts2和spring整合的jar:struts2-spring-plugin-2.3.4.1.jar,有一些jar在spring和hibernate整合的时候已经添加过了,这里就不需要重复添加了,否则可能引起jar包冲突。
在web.xml中配置struts2的核心过滤器和ContextLoaderListener
<?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">
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!-- 加载spring的配置文件 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 指定spring配置文件的位置 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:applicationContext.xml</param-value>
</context-param>
<!-- OSIVFilter:解决hibernate懒加载问题 -->
<filter>
<filter-name>osivFilter</filter-name>
<filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>osivFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- struts2核心过滤器 -->
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
编写UserInfoAction,我们在编写action的时候,为该UserInfoAction声明一个UserInfoBiz属性,并且设置set方法,这样做是为了后边通过springIOC将UserInfoBiz注入给UserInfoAction,完美的实现了三层架构,注意:UserInfoAction必须继承自ActionSupport,如果我们需要用到HttpServletRequest,那么UserInfoAction需要声明一个HttpServletRequest request;属性,并且实现ServletRequestAware接口,实现了ServletRequestAware接口之后,需要重写一下方法:
public void setServletRequest(HttpServletRequest arg0) {
request=arg0;
}
这里是将arg0赋值给我们自己的属性。
package com.test.action;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.apache.struts2.interceptor.ServletRequestAware;
import com.opensymphony.xwork2.ActionSupport;
import com.test.biz.UserinfoBiz;
import com.test.entity.Userinfo;
public class UserinfoAction extends ActionSupport implements ServletRequestAware{
private UserinfoBiz userBiz;
private HttpServletRequest request;
private Userinfo user;
public Userinfo getUser() {
return user;
}
public void setUser(Userinfo user) {
this.user = user;
}
public void setUserBiz(UserinfoBiz userBiz) {
this.userBiz = userBiz;
}
public String getAll() throws Exception {
List<Userinfo> list = userBiz.getAll();
request.setAttribute("list", list);
return "list";
}
public String doRegister() throws Exception {
int result = userBiz.addUserinfo(user);
if(result>0){
return this.SUCCESS;
}
return this.ERROR;
}
public String doDelete() throws Exception {
int result = userBiz.deleteUserinfo(user.getUid());
if(result>0){
return this.SUCCESS;
}
return this.ERROR;
}
public String doDetail() throws Exception {
Userinfo userinfo = userBiz.getUserinfoById(user.getUid());
request.setAttribute("userinfo", userinfo);
return "detail";
}
public String doUpdate() throws Exception {
int result = userBiz.updateUserinfo(user);
if(result>0){
return this.SUCCESS;
}
return this.ERROR;
}
public void setServletRequest(HttpServletRequest arg0) {
request=arg0;
}
}
这里我们每个方法都返回的是一个string类型的数据,这是因为structs2会根据该返回值来跳转到对应的界面。如果需要将数据传递到jsp页面来显示的话,可以使用request.setAttribute(“list”, list);这样的形式来传递。其他的没什么可说的了。
在spring的配置文件中配置action并设置biz
<!-- 配置Action,并注入Biz -->
<bean id="userAction" class="com.test.action.UserinfoAction">
<property name="userBiz" ref="userBiz"></property>
</bean>
这里我们是将structs2的action交给spring来管理。
配置structs2的配置文件structs.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" "http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
<package name="sshDemo" extends="struts-default">
<!-- class="伪类(spring配置文件中action的id)" -->
<action name="user-*" class="userAction" method="{1}">
<result name="list">/list.jsp</result>
<result name="success" type="redirectAction">user-getAll</result>
<result name="error">/error.jsp</result>
<result name="detail">/detail.jsp</result>
</action>
</package>
</struts>
这里”package”标签的名称可以随便起,但是必须继承自”struts-default”,action标签中的class是spring中配置的id,因为我们完全将action交给spring来处理了,我可看到我在这里利用”*”通配符和method=”{1}”来匹配,什么意思呢?举个栗子,比如我传入的是”http://localhost:8080/xiangmuming/user-add.action“,此时将会执行UserinfoAction.java类中的add方法,如果该方法返回”list”这个字符串,那么此时系统将会跳转到根目录下的”list.jsp”页面。
下面是时候编写我们的页面了。在web.xml中配置我的项目的默认主页面是index.jsp,如下:
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
我在index.jsp中创建一个连接,通过该连接可以查询所有的数据:
<a href="user-getAll.action">用户列表</a>
可以看到,”用户列表”会执行到UserInfoAction.java中的getAll方法,通过返回值和structs.xml分析,此方法会跳转到list.jsp,下面是list.jsp的代码:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<body>
<table width="70%" align="center" border="1">
<tr>
<td>编号</td><td>用户名</td><td>密码</td><td>操作</td>
</tr>
<s:if test="#request.list.size==0">
<tr>
<td colspan="4">暂无用户信息!</td>
</tr>
</s:if>
<s:else>
<s:iterator value="#request.list" var="user">
<tr>
<td><s:property value="#user.uid"/> </td>
<td><s:property value="#user.uname"/></td>
<td><s:property value="#user.upass"/></td>
<td>
<a href="user-doDelete.action?user.uid=<s:property value="#user.uid"/>">删除 </a>
<a href="user-doDetail.action?user.uid=<s:property value="#user.uid"/>">修改</a></td>
</tr>
</s:iterator>
</s:else>
</table>
</body>
</html>
在list.jsp中使用了structs2的标签,因此需要导入struct2的标签”<%@ taglib uri=”/struts-tags” prefix=”s”%>”
在structs2标签中有和以及等标签。
<s:if test="#request.list.size==0">
<tr>
<td colspan="4">暂无用户信息!</td>
</tr>
</s:if>
上面的if表示如果request中存在list的长度==0的情况,如果request中的list是有数据的:
<s:else>
<s:iterator value="#request.list" var="user">
<tr>
<td><s:property value="#user.uid"/> </td>
<td><s:property value="#user.uname"/></td>
<td><s:property value="#user.upass"/></td>
<td>
<a href="user-doDelete.action?user.uid=<s:property value="#user.uid"/>">删除 </a>
<a href="user-doDetail.action?user.uid=<s:property value="#user.uid"/>">修改</a></td>
</tr>
</s:iterator>
</s:else>
通过标签来遍历list集合,这里和forech循环比较相似。user.uid,user.uname,user.upass是UserInfo中的属性。可以看到当我点击删除的时候,会跳转到UserInfoAction中的doDelete方法,删除的连接是跳转到doDetail方法。
运行截图:
源码下载