标签:
最近在看马士兵老师的关于Spring方面的视频,讲解的挺好的,到了Spring、Hibernate、struts2整合这里,由于是以例子的形式来对Spring+Hibernate+struts2这3大框架进行整合,因此,自己还跟着写代码的过程中,发现还是遇到了很多问题,因此,就记录下。
特此说明:本篇博文完全参考于马士兵老师的《Spring视频教程》。
本篇博文均以如下这个例子一步一步的将Hibernate、struts、spring整合进来。
现在我们要实现这样一个功能:
有一个用户注册的页面,当用户进行在注册页面进行信息的填写之后,会出现如下的两种情况:
1、如果信息符合要求,则显示登录成功的页面并将用户信息写入到数据库中
2、如果信息不符合要求,则显示登录失败的页面。
本篇博文为Spring、Hibernate、struts2的整合经历的如下的阶段
1、两层架构的实现
2、两层架构+Entity+Service
3、三层架构:两层架构+Entity+Service+Hibernate(引入Hibernate)
4、N层+DAO层+抽象层
5、N层架构+Hibernate+struts(引入struts)
6、N层架构+Hibernate+struts+spring
下面就围绕上面的例子来慢慢将Spring、Hibernate和Struts进行整合进来。
我们首先采用两层架构来实现上面所描述的功能。
个jsp+数据库的两层架构
具体设计如下:
主要涉及到如下的4个jsp文件:
1、register.jsp
2、registerDeal.jsp
3、registerSuccess.jsp
4、registerFail.jsp
关于以上4个jsp文件的代码如下
1、register.jsp
这个文件主要就是一个表单form。具体内容如下:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>登陆界面</title>
</head>
<body>
<form action="registerDeal.jsp" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name= "password"><br>
确认密码:<input type="password" name="password2"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
2、registerDeal.jsp
这个文件包括主要的业务逻辑,主要用于接收register.jsp中用户输入的数据,以及和数据库打交道,进行数据的存储。下面的代码是我们比较熟悉的代码。
<%@page import="java.sql.Connection"%>
<%@ page language="java" import="java.util.*,java.sql.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
String username=request.getParameter("username");
String password=request.getParameter("password");
String password2=request.getParameter("password2");
Class.forName("com.mysql.jdbc.Driver");
Connection conn=DriverManager.getConnection("jdbc:mysql://localhost/spring", "root", "123456");
String sqlQuery="select count(*) from user where username=?";
PreparedStatement psQuery=conn.prepareStatement(sqlQuery);
psQuery.setString(1, username);
ResultSet rs=psQuery.executeQuery();
rs.next();
int count=rs.getInt(1);
//如果count>0 则跳转到registerFail.jsp页面 ,否则跳转到 registerSuccess.jsp页面并将用户的信息插入到数据库中
if(count>0){
response.sendRedirect("registerFail.jsp");
psQuery.close();
conn.close();
return;
}
String sqlInsert="insert into user values(null,?,?)";
PreparedStatement ps=conn.prepareStatement(sqlInsert);
ps.setString(1, username);
ps.setString(2,password);
ps.executeUpdate();
response.sendRedirect("registerSuccess.jsp");
psQuery.close();
ps.close();
conn.close();
%>
3、registerSuccess.jsp registerFail.jsp
这两个jsp文件比较简单,均只在页面中,显示Success / Fail信息。
例如:registerSuccess.jsp中body下的代码为:
<body>
Success. <br>
</body>
要注意的几点是:
1、在MySQL数据库中建立名为spring的database和名为user的table
2、需要在项目中导入数据库驱动包。如下:
在浏览器中输入:http://localhost:8080/Spring_Register/register.jsp
会看到如下的登陆界面:
当我们在登陆界面数据信息后点击提交就会进行相应的跳转,以及将信息写入数据库中。
以上就通过jsp+数据库两层架构实现了这样一个简单的功能。
上面所完成的所有功能均在jsp中实现,根本就没有体现面向对象编程的思维逻辑,因此,我们在上面的两层架构中引入Entity+Service来完成上面描述的功能。
2.1:定义一个User类
内容如下:
package com.register.model;
public class User {
private String username;
private String password;
//getter setter方法
}
2.2:定义一个UserManager类来管理User
内容如下:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import com.register.model.User;
public class UserManager {
/*
* 判断用户是否已在数据库中存在
* */
public boolean exists(User user) throws Exception{
Class.forName("com.mysql.jdbc.Driver");
Connection conn=DriverManager.getConnection("jdbc:mysql://localhost/spring", "root", "123456");
String sqlQuery="select count(*) from user where username=?";
PreparedStatement psQuery=conn.prepareStatement(sqlQuery);
psQuery.setString(1, user.getUsername());
ResultSet rs=psQuery.executeQuery();
rs.next();
int count=rs.getInt(1);
if(count>0){
psQuery.close();
conn.close();
return true;
}
return false;
}
/*
* 函数功能:将用户的信息保存到数据库中
* */
public void save(User user) throws Exception{
Class.forName("com.mysql.jdbc.Driver");
Connection conn =DriverManager.getConnection("jdbc:mysql://localhost/spring",
"root", "123456");
String sql="insert into user values(null,?,?)";
PreparedStatement ps=conn.prepareStatement(sql);
ps.setString(1, user.getUsername());
ps.setString(2, user.getPassword());
ps.executeUpdate();
ps.close();
conn.close();
}
}
此文件中的代码基本上就是对两层架构中registerDeal.jsp文件中的内容进行了一个提取和封装。
因此,在registerDeal.jsp文件中的内容如下
registerDeal.jsp中主要完成的是根据用户信息是否已经在数据库中存在完成页面的跳转。
<%@page import="java.sql.Connection"%>
<%@ page language="java" import="java.util.*,java.sql.*" pageEncoding="UTF-8"%>
<%@ page import="com.register.model.*" %>
<%@ page import="com.register.service.*" %>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
String username=request.getParameter("username");
String password=request.getParameter("password");
String password2=request.getParameter("password2");
User user=new User();
user.setUsername(username);
user.setPassword(password);
UserManager um=new UserManager();
boolean exists=um.exists(user);
if(exists){
response.sendRedirect("registerFail.jsp");
return;
}
um.save(user);
response.sendRedirect("registerSuccess.jsp");
%>
其它文件与两层架构中文件内容一致,这样就利用两层架构+Entity+Service,即三层架构来完成了此功能。这样的三层架构模型如下:
在两层架构中,jsp需要和数据库交互,在三层架构中,jsp就不再需要和数据库交互了。
这样我们离Spring/Hibernate/struts2整合又进了一步。
在上面的三层架构中,我们是在UserManager中直接利用如下的语句完成数据库的交互
public void save(User user) throws Exception{
Class.forName("com.mysql.jdbc.Driver");
Connection conn =DriverManager.getConnection("jdbc:mysql://localhost/spring",
"root", "123456");
String sql="insert into user values(null,?,?)";
PreparedStatement ps=conn.prepareStatement(sql);
ps.setString(1, user.getUsername());
ps.setString(2, user.getPassword());
ps.executeUpdate();
ps.close();
conn.close();
}
想上面这样的语句不是面向对象的,因此,这里我们就顺其自然的引入了Hibernate,利用Hibernate来和数据库进行交互。架构图如下
具体实现如下:
1、在工程WebRoot/WEB-INF/lib引入Hibernate的一些jar包
2、添加配置文件hibernate.cfg.xml
3、利用Annotation来注解实体类User。
4、其它文件中的代码与三层架构的代码一致,只需要更改UserManager类中的代码,代码如下:
import java.util.List;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import com.register.model.User;
public class UserManager {
private SessionFactory sessionFactory=new Configuration().configure().buildSessionFactory();
/*
* 判断用户是否已在数据库中存在
* */
public boolean exists(User user) throws Exception{
Session session=sessionFactory.getCurrentSession();
session.beginTransaction();
//利用hql来查询数据库中是否已经存在此名字的用户
String hql="from User as u where u.username=?";
Query query=session.createQuery(hql);
query.setString(0, user.getUsername());
List list=query.list();
if(!list.isEmpty()){
return true;
}
session.getTransaction().commit();
return false;
}
/*
* 函数功能:将用户的信息保存到数据库中
* */
public void save(User user) throws Exception{
Session session=sessionFactory.getCurrentSession();
session.beginTransaction();
session.save(user);
session.getTransaction().commit();
}
}
以上就是借用了Hibernate来完成和数据库的交互。
上面是利用了Hibernate,在UserManager中调用Hibernate来完成数据库的交互。为了跨数据库平台等要求,需要引入DAO层,用DAO层来完成数据库的CRUD操作。
具体结构如下:
而关于DAO层,我们可能会有多种具体实现方式,对于Service层,我们也可能有多种具体的实现,因此,我们也就引入了面向接口编程。
引入DAO层和Service层结构以及他们之间的交互如下:
根据上面的结构图,我们只需要在第三节中介绍的:三层架构+Hibernate中的代码进行提取即可。
例如
1、UserDao接口中的代码如下:
public interface UserDao {
public void save(User user);
public boolean exists(User user);
}
2、UserDAO的实现UserDaoImpl类的代码如下:
public class UserDaoImpl implements UserDao{
@Override
public void save(User user) {
SessionFactory sessionFactory=HibernateUtil.getSessionFactory();
Session session=sessionFactory.getCurrentSession();
session.beginTransaction();
session.save(user);
session.getTransaction().commit();
}
@Override
public boolean exists(User user) {
SessionFactory sessionFactory=HibernateUtil.getSessionFactory();
Session session=sessionFactory.getCurrentSession();
session.beginTransaction();
//利用hql来查询数据库中是否已经存在此名字的用户
String hql="from User as u where u.username=?";
Query query=session.createQuery(hql);
query.setString(0, user.getUsername());
List list=query.list();
if(!list.isEmpty()){
return true;
}
session.getTransaction().commit();
return false;
}
}
3、UserManager接口的代码如下
public interface UserManager {
public void save(User user);
public boolean exists(User user);
}
4、UserManager的实现UserManagerImpl类的代码如下:
public class UserManagerImpl implements UserManager{
private UserDao userDao=new UserDaoImpl();
public UserDao getUserDao() {
return userDao;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public boolean exists(User user) {
return userDao.exists(user);
}
public void save(User user) {
userDao.save(user);
}
}
从以上的4个文件可以看出,就是将原来的三层架构+Hibernate的UserManager类中的代码进行了抽取,并添加了DAO层来完成和数据库的交互。
整个工程的代码可以在这里提取:https://github.com/wojiushimogui/Spring_register_dao_impl
到此为止,我们又距离Spring 、Hibernate 、struts2融合又近了一点。
下一步将引入struts2框架
在第4种情况下引入Struts框架,需要引入的东西如下:
1、Struts2相关的jar包
2、web.xml文件(放入WEB-INF目录下)
3、配置文件struts.xml(放在/src目录下)
4、Action类
其中,
web.xml文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
<display-name>Spring_Register_Hibernate_Struts</display-name>
<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>
Action类的内容如下:
属性username/password/password2用来接收登录界面闯进来的参数。
public class LoginAction extends ActionSupport{
private String username;
private String password;
private String password2;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPassword2() {
return password2;
}
public void setPassword2(String password2) {
this.password2 = password2;
}
//判断user在数据库中是否存在来返回不同的字符串
@Override
public String execute() throws Exception {
UserManager um=new UserManagerImpl();
User user=new User();
user.setUsername(username);
user.setPassword(password);
boolean exists=um.exists(user);
if(exists){
System.out.println("registerFail");
return "fail";
}
um.save(user);
System.out.println("registerSuccess");
return "success";
}
}
struts.xml文件中的内容如下:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.1//EN"
"http://struts.apache.org/dtds/struts-2.1.dtd">
<struts>
<package name="default" namespace="/" extends="struts-default">
<action name="registerAction" class="com.register.action.LoginAction">
<result name="success">/registerSuccess.jsp</result>
<result name="fail">/registerFail.jsp</result>
</action>
</package>
</struts>
这样,就引入了Struts,将registerDeal的逻辑放入到了Action类中的execute方法中。并根据execute返回的字符串显示不同的view。
在完成struts整合的过程中,遇到了很多问题,有的比较好解决,有的也纠结了很长时间,例如:相同但不同版本的jar包问题(避免冲突,保留最新的jar包)
在删除重复的jar包的过程中,也遇到了一些问题,如下:
在MyEclipse中直接删除时,是删不掉的,不知道为什么,但是有的包又可以直接删除,因为这个问题,自己折腾了半天才OK。
到这里为止,我们就整合了Hibernate 、struts,下面我们就将Spring整合进来。
这节就开始引入Spring来对一些单例对象及事务进行管理。
主要细节可以参考这篇博文:http://blog.csdn.net/qq7342272/article/details/7928814
需要进行以下步骤:
1、引入Spring 相关的jar包
2、配置文件:beans.xml文件
3、用@Resource @Component来对一些类和对象进行注解,供Spring来进行管理
下面主要贴下beans.xml文件中的代码,这个beans.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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<context:annotation-config />
<!-- 使用@Resource进行自动注入,需要增加如下的配置 -->
<bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor" />
<!-- 检测那些类可以作为组件 -->
<context:component-scan base-package="com.register"/>
<aop:config></aop:config>
<!-- 配置一个SessionFactory -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost/spring"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
<!-- 数据库连接池保持的最小和最大的连接数 -->
<property name="minIdle" value="5"/>
<property name="maxIdle" value="30"/>
<!--
当数据库连接因为某种原因断掉之后,再重新从连接池中拿另外一个连接时实际上这个连接可能
已经无效,所以为了确保所拿到的连接全都有效需要在获取连接,返回连接以及连接空闲时进行
有效性验证 下面3个设置为ture时进行验证,默认为false
-->
<!-- 取得连接时是否进行有效性验证 -->
<property name="testOnBorrow" value="true" />
<!-- 返回连接时是否进行有效性验证 -->
<property name="testOnReturn" value="true" />
<!-- 连接空闲时是否进行有效性验证 -->
<property name="testWhileIdle" value="true" />
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!-- 注意:我用的是Annotation的方式配置的Hibernate,
这里的property的name是annotatedClasses
<property name="annotatedClasses">
<list>
<value>com.register.model.User</value>
<value>com.register.service.impl.UserManagerImpl</value>
</list>
</property>
-->
<property name="packagesToScan">
<list>
<value>com.register.model</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<!-- 设置Hibernate方言 -->
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<!-- 是否打印sql -->
<prop key="hibernate.show_sql">true</prop>
<!-- 格式化sql -->
<prop key="hibernate.format_sql">true</prop>
<!-- 是否自动更新表 -->
<!--
<prop key="hibernate.hbm2ddl.auto">update</prop>
<prop key="hibernate.current_session_context_class">thread</prop>
最大抓取深度,如果为0,则关闭默认的外连接抓取。建议值为0-3
<prop key="hibernate.max_fetch_depth">1</prop>
用于生成有助于调试的注释信息,默认为关闭
<prop key="hibernate.use_sql_comments">true</prop>
-->
</props>
</property>
</bean>
<bean id="hibernateTemplate" class="org.springframework.orm.hibernate5.HibernateTemplate">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager" >
<property name="dataSource" ref="dataSource" />
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<aop:config>
<aop:pointcut id="txMethod" expression="execution(* com.register.dao..*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="txMethod"/>
</aop:config>
<!-- AOP切面声明事务管理 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED" /> <!-- 支持当前事务,如果执行到save开头的任何方法时没有事务则开启一个事务 这是最常见的方式-->
<tx:method name="update*" propagation="REQUIRED" /><!-- 支持当前事务,如果执行到update开头的任何方法时没有事务则开启一个事务 这是最常见的方式-->
<tx:method name="add*" propagation="REQUIRED" /><!-- 支持当前事务,如果执行到add开头的任何方法时没有事务则开启一个事务 这是最常见的方式-->
<tx:method name="delete*" propagation="REQUIRED" /><!-- 支持当前事务,如果执行到delete开头的任何方法时没有事务则开启一个事务 这是最常见的方式-->
<tx:method name="find*" propagation="SUPPORTS" read-only="true"/> <!-- 支持当前事务,如果当前没有事务,就以非事务方式执行。只读 -->
<tx:method name="get*" propagation="SUPPORTS" read-only="true"/><!-- 支持当前事务,如果当前没有事务,就以非事务方式执行。只读 -->
<tx:method name="*" />
</tx:attributes>
</tx:advice>
<!-- <bean name="userManager" class="com.register.service.impl.UserManagerImpl" /> -->
</beans>
UserDaoImpl类的代码更改为如下:
@Component("userDao")
public class UserDaoImpl implements UserDao{
private HibernateTemplate hibernateTemplate;
public HibernateTemplate getHibernateTemplate() {
return hibernateTemplate;
}
@Resource
public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
this.hibernateTemplate = hibernateTemplate;
}
@Override
public void save(User user) {
this.hibernateTemplate.save(user);
}
@Override
public boolean exists(User user) {
List res=hibernateTemplate.find("from User as u where u.username=?", user.getUsername());
if(res!=null&&res.size()>0){
return true;
}
return false;
}
}
测试代码如下:
public class UserManagerTest {
@Test
public void testExists() {
ApplicationContext context= new ClassPathXmlApplicationContext("/beans.xml");
UserManager um =(UserManager) context.getBean("userManager");
// UserManager um=new UserManagerImpl();
User user=new User();
user.setUsername("haowu");
boolean exists=um.exists(user);
assertEquals(false, exists);
}
@Test
public void testSave() {
ApplicationContext context= new ClassPathXmlApplicationContext("/beans.xml");
UserManager um =(UserManager) context.getBean("userManager");
// UserManager um=new UserManagerImpl();
User user=new User();
user.setUsername("haowuwu3");
user.setPassword("123");
boolean exists=um.exists(user);
if(!exists){
um.save(user);
//如果我们不想从数据库中查看是否保存成功,则可以利用以下的代码来完成
exists=um.exists(user);
assertEquals(true, exists);
}
else{
fail("save fail!!!");
}
}
}
将对象交给Spring来进行管理,在代码中我们比较常见的类似于UserDao userDao=new UserDaoImpl()这样的代码就不见了。
完整代码可以在这里提取:https://github.com/wojiushimogui/Spring_Register_Hibernate_Struts_0000_Final
在引入spring的过程中,遇到了很多的问题,如下
1、java.lang.ClassNotFoundException: org.apache.commons.dbcp.BasicDataSource
2、 java.lang.NoClassDefFoundError: org/aspectj/weaver/reflect/ReflectionWorld$ReflectionWorldException
3、’hibernateTemplate’ must be of type [org.springframework.orm.hibernate5.HibernateTemplate], but was actually of type [org.springframework.orm.hibernate3.HibernateTemplate]
4、java.lang.NoClassDefFoundError: org/hibernate/engine/SessionFactoryImplementor
5、org.springframework.transaction.TransactionSystemException: Could not commit Hibernate transaction; nested exception is org.hibernate.TransactionException: Transaction not successfully started
这些问题的解决方案均分别以博客的形式给出。
虽然跟着视频完成了Spring、Hibernate、struts的整合,但是,还是有很多地方不太理解,特别是关于最后的Spring框架的整合。关于Spring还需要继续的学习才行。
《Spring学习笔记》:Spring、Hibernate、struts2的整合(以例子来慢慢讲解,篇幅较长)
标签:
原文地址:http://blog.csdn.net/u010412719/article/details/51508266