三大框架顾名思义就是非常有名的Struts2 ,Hibernate,Spring,
框架整合的方法很多,现在我写一个非常简单的整合过程,相信大家一看就会!
这里使用的struts-2.2.1.1、hibernate-3.2.0、spring2.5.6
第一步,搭建Struts2环境
1、导入struts2的jar包(直接把struts-blank项目下的依赖库coypy到自己项目中)
2、 配置web.xml,增加struts2提供的过滤器(参考struts-blank项目)
<?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"> <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>
3、建立包:com.qcf.struts.test,并增加普通java类,代码如下:
package com.qcft.struts.test; public class FirstAction { private String msg; public String execute() throws Exception{
System.out.println("FirstAction.test1()");
setMsg("为了让生活美好!");
return "success";
} public String getMsg() {
return msg;
} public void setMsg(String msg) {
this.msg = msg;
}
}
4、在src下增加struts.xml,并增加FirstAction类的配置内容:
<?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>
<package name="default" namespace="/" extends="struts-default">
<action name="first" class="com。qcf.struts.test.FirstAction">
<result name="success">ok.jsp</result>
</action>
</package>
<!-- Add packages here -->
</struts>
5、增加ok.jsp页面,用来显示FirstAction中的属性msg:
<%@ page language="java" import="java.util.*" pageEncoding="gbk"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>测试struts2</title>
</head>
<body>
<s:property value="msg" />
</body>
</html>
测试成功!
第二步:搭建Hibernate环境
1、导入hibernate所需要的基本的jar包
2、添加hibernate.cfg.xml配置文件
打开etc目录,将hibernate.cfg.xml拷贝到项目src下
修改配置文件hibernate.cfg.xml内容,结合etc/hibernate.properties(文件中搜索”mysql”),完成后配置内容如下:
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="show_sql">true</property>
<property name="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</property>
<property name="connection.driver_class">
com.mysql.jdbc.Driver
</property>
<property name="hibernate.connection.url">
jdbc:mysql://localhost:3306/testhib
</property>
<property name="connection.username">
root
</property>
<property name="connection.password">
123456
</property>
</session-factory>
</hibernate-configuration>
3、新建pojo类(Plain Old Java Objects 简单的java对象,实际上就是我们讲的普通javabean对象):User
package com.qcf.hib.bean; import java.util.Date; public class User {
private int id;
private String name;
private Date birthday; public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public User(int id, String name, Date birthday) {
super();
this.id = id;
this.name = name;
this.birthday = birthday;
}
public User(String name, Date birthday) {
super();
this.name = name;
this.birthday = birthday;
} public User() {
}
}
4、增加映射文件User.hbm.xml(写法可以参考:eg/User.hbm.xml)
映射文件hbm.xml说明了pojo类和表的对应关系,以及pojo类中属性和表中字段的对应关系。
注:本映射文件增加到跟pojo同一个包中
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping
package="com.qcf.hib.bean">
<class name="User" table="_user" lazy="true">
<id name="id">
<generator class="native"/>
</id>
<property name="name" type="java.lang.String"
not-null="true"
length="15"
column="_name"/>
<property name="birthday" type="java.util.Date" column="birthday"/>
</class>
</hibernate-mapping>
5、在hibernate.cfg.xml中增加User.hbm.xml文件的配置,让hibernate知道本映射关系。在<session-factory>元素下增加:
<mapping resource="com/qcf/hib/bean/User.hbm.xml"/>
6、修改hibernate.cfg.xml文件,在<session-factory>下增加hbm2ddl.auto的配置:
<property name="hibernate.hbm2ddl.auto">update</property>
create-drop: 运行时,先创建,运行完,在删除。
create:每次运行前都会删除已有的。在创建。 测试时,可以使用create.
update:映射文件和表。不会重新创建表及不会重新执行ddl语句,只会更新表中的记录。
validate:看映射文件和表是不是对应,如果不对应,他也不会更新,会报错。经常用它,保险一些。
7、增加Test.java测试类:
package com.bjsxt.hib.test; import java.util.Date; import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration; import com.bjsxt.hib.bean.User; public class Test {
public static void main(String[] args) {
Configuration conf = new Configuration().configure();
SessionFactory factory = conf.buildSessionFactory();
Session session = factory.openSession();
User user = new User("高淇",new Date());
session.save(user);
session.close();
}
}
8、 上一次执行,我们发现表创建成功但是数据记录并没有插入表中。jdbc是自动提交,autocommit。hibernate缺省是false. 因此,我们必须很明确的开启事务才行。我们将Test.java文件内容修改如下:
package com.qcf.hib.test; import java.util.Date; import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration; import com.bjsxt.hib.bean.User; public class Test {
public static void main(String[] args) {
Configuration conf = new Configuration().configure();
SessionFactory factory = conf.buildSessionFactory();
Session session = factory.openSession();
Transaction transaction = session.beginTransaction(); User user = new User("高淇",new Date());
session.save(user); transaction.commit();//保存之后,关闭session之前,显示提交事务.(必须调用,不然不会提交。即使session关闭,也不回自动提交)
session.close();
}
}
测试成功,数据库中也有相应的数据添加成功!
第三步:搭建Spring环境
1、导入Spring所需要的jar包
spring.jar这一个即可!
2、写一个测试类
package com.qcf.test; public class UserDao {
public void add(String uname,String pwd){
System.out.println("增加一个用户!");
}
}
3、增加配置文件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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <bean id="userDao" class="com.sxt.test.UserDao"></bean> </beans>
通过上面的配置文件,spring框架知道了UserDao类的存在!可以通过反射机制自动将UserDao类的对象new出! 所以注意托管给spring的类必须符合基本的javabean规范:
1. 如果有属性,则必须有相应的get/set方法。
2. 必须要无参的构造器
4、建立Test.java类
package com.qcf.test; import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test {
public static void main(String[] args){
// UserDao userDao = new UserDao();
// userDao.add("a", "123"); ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"beans.xml"});
UserDao userdao = (UserDao) context.getBean("userDao");
userdao.add("d", "222");
}
}
5、上面的代码中,我们可以使用context.getBean("userDao")代替了new UserDao(这样的代码,也就是spring内部有个工厂类,替我们完成了new对象的操作!而这个工厂类是通过读取beans.xml文件知道了字符串”userDao”和com.sxt.test.UserDao类之间的关系!
直接运行Test.java类即可。
第四步:已经将三个框架各自搭建完毕。现在先整合Struts和spring
整合struts2和spring非常简单只需两步:
1、在web.xml下面添加一个spring的过滤器(添加到最上面,在struts2的配置文件上面)
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application.xml</param-value>
</context-param>
2、添加一个struts-spring.plus的插件,这个可以再struts官方提供的jar中即可找到
注:action中的class不用填写全名,直接写spring中注册的id即可,如:testAction
<package name="default" namespace="/" extends="struts-default">
<action name="testAction" class="testAction">
<result name="success">index.jsp</result>
</action>
</package>
第五步:整合spring和hibernate
1、首先将web.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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
">
2、配置sessionFactory(配置了C3P0数据库连接池)
<!-- 导入jdbc.properties -->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置sessionfactory -->
<bean name="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<!-- 配置application.xml路径 -->
<property name="configLocation" value="classpath:hibernate.cfg.xml"></property>
<!-- 配置c3p0连接池 -->
<property name="dataSource">
<bean class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- 数据库连接信息 -->
<property name="driverClass" value="${driverClass}"></property>
<property name="jdbcUrl" value="${jdbcUrl}"></property>
<property name="user" value="${user}"></property>
<property name="password" value="${password}"></property>
<!-- 其它配置 -->
<!-- 数据库初始化时获取三个链接,取值应该在min -->
<property name="initialPoolSize" value="3"></property>
<!-- 连接池中保留的最小连接数 -->
<property name="minPoolSize" value="3"></property>
<!-- 连接池中保留的最大连接数 -->
<property name="maxPoolSize" value="10"></property>
<!-- 当连接池中连接耗尽时c3p0一次获取的连接数 -->
<property name="acquireIncrement" value="3"></property>
<!-- 配置数据源内加载的preparestatement数量 -->
<property name="maxStatements" value="3"></property>
<!-- 单个连接所拥有的最大statments缓存数 -->
<property name="maxStatementsPerConnection" value="3"></property>
<!-- 设置最大空闲时间不使用自动丢弃,如果为0永远不丢弃 -->
<property name="maxIdleTime" value="1800"></property>
</bean>
</property>
</bean>
3、配置事务管理(XML)
<!-- 配置声明事务管理 -->
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!-- AOP核心配置 -->
<aop:config>
<!-- 定义aop切面 -->
<aop:pointcut expression="execution(public * com.qcf.test.*.*(..))" id="testa"/>
<aop:advisor advice-ref="txadvice" pointcut-ref="testa"/>
</aop:config>
<!-- 定义切割方法 -->
<tx:advice id="txadvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- -->
配置事务(annotation)
<!-- 自动扫面
-->
<context:component-scan base-package="com.qcf"></context:component-scan> <!-- 配置事务 -->
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean> <!-- annotation配置事务 -->
<tx:annotation-driven transaction-manager="txManager"/>
测试方法:
package com.qcf.dao.impl; import javax.annotation.Resource; import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import com.qcf.dao.UserDao;
import com.qcf.po.User; @Service
public class UserDaoImpl implements UserDao { @Resource
private SessionFactory sessionFactory;
/**
* 增加用户
*/
@Transactional
public void addUser() {
// TODO Auto-generated method stub
Session session=sessionFactory.getCurrentSession();
session.save(new User(, "zhangsan", ));
session.save(new User(, "zhan", )); }
public SessionFactory getSessionFactory() {
return sessionFactory;
}
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
} }
4、配置HibernateTemplate
HibernateTemplate类可让我们将Hibernate的使用模板化,使我们对hibernate的调用更加简单!使用他,我们只需要在配置文件中增加:
<!-- 配置hibernatetempter -->
<bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
hibernateTemplate常用方法:
◦ void delete(Object entity):删除指定持久化实例
◦ deleteAll(Collection entities):删除集合内全部持久化类实例
◦ find(String queryString):根据HQL查询字符串来返回实例集合
◦ get(Class entityClass, Serializable id):根据主键加载特定持久化类的实例
◦ save(Object entity):保存新的实例
◦ saveOrUpdate(Object entity):根据实例状态,选择保存或者更新
◦ update(Object entity):更新实例的状态,要求entity是持久状态
◦ setMaxResults(int maxResults):设置分页的大小(无setFirstResult方法)
HibernateTemplate的典型用法:
- 需要直接获得session对象的处理方式(比如:分页处理)
hibernateTemplate.execute(new HibernateCallback() { public Object doInHibernate(Session session) throws HibernateException,
SQLException {
// TODO Auto-generated method stub
session.save(new User(0, "lisi", 19));
return null;
}
});
2. 不需要直接获得session对象的情况
public List<User> getAllUser(){
List<User> list=hibernateTemplate.find("from User");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i).getId()); } List list2 = hibernateTemplate.find("from User where name=? and id=?", new Object[]{"zhangsan",1});
for (int i = 0; i < list2.size(); i++) {
System.out.println(list2.get(i).toString());
} return null;
}
5、HibernateDaoSupport
封装了HibernateTemplate!常见用法如下:
public class UserDaoImpl3 extends HibernateDaoSupport {
public void add(User u) {
this.getHibernateTemplate().save(u);
}
}
public class UserDaoImpl3 extends HibernateDaoSupport { public void add(User u) {
Session s = this.getSession();
s.save(u);
releaseSession(s); //手动释放session资源
}
}
6、JDBCTemplate的使用
dbcTemplate类是spring为了让我们更加容易使用jdbc而提供的封装。JdbcTemplate对jdbc操作做了简单的封装。内部也使用了类似HibernateTemplate中使用的模板方法模式。JdbcTemplate在工作中用的不多。本节为自学内容,目的是让大家开阔眼界。
要使用JdbcTemplate,我们必须要在spring中增加配置:
<!--配置一个JdbcTemplate实例-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
测试类如下:
package com.qcf.dao.impl; import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map; import javax.annotation.Resource; import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Component; import com.sxt.po.User; /**
* 用来测试jdbcTemplate的用法
* @author Administrator
*
*/
@Component("userDao")
public class UserDaoImpl {
@Resource
private JdbcTemplate jdbcTemplate; public void testInsertUser(User user){
jdbcTemplate.update("insert into user (uname,pwd) values (?,?)",new Object[]{user.getUname(),user.getPwd()});
} public void testUpdateUser(String pwd,int id){
jdbcTemplate.update("update user set pwd=? where id=?",new Object[]{pwd,id});
}
public void deleteUser(int id){
jdbcTemplate.update("delete from user where id=?",new Object[]{id});
} public void testGetUserById(int id){
User user = (User) jdbcTemplate.queryForObject("select id,uname,pwd from user where id=?", new Object[]{id}, new RowMapper(){
@Override
public Object mapRow(ResultSet rs, int no) throws SQLException {
User u = new User();
u.setId(rs.getInt("id"));
u.setUname(rs.getString("uname"));
u.setPwd(rs.getString("pwd"));
return u;
}}); System.out.println(user.getUname());
} public void testGetUsers(){
List<Map> list = jdbcTemplate.queryForList("select * from user where id>?",new Object[]{1});
for (Map map : list) {
System.out.println(map.get("uname"));
}
}
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"applicationContext.xml"});
UserDaoImpl ud = (UserDaoImpl) context.getBean("userDao");
User u = new User();
u.setUname("sss");
u.setPwd("123");
// ud.testInsertUser(u);
// ud.testDeleteUser(2);
ud.testGetUsers();
} }
7、OpenSessionInView管理session
OpenSessionInViewFilter是Spring提供的一个针对Hibernate的一个支持类,其主要意思是在发起一个页面请求时打开 Hibernate的Session,一直保持这个Session,直到这个请求结束,具体是通过一个Filter来实现的。
由于Hibernate引入了Lazy Load特性,使得脱离Hibernate的Session周期的对象如果再想通过getter方法取到其关联对象的值,Hibernate会抛出一个 LazyLoad的Exception。所以为了解决这个问题,Spring引入了这个Filter,使得Hibernate的Session的生命周期变长。
OpenSessionInViewFilter:org.springframework.orm.hibernate3.support.OpenSessionInViewFilter]是 Spring提供的一个针对Hibernate的一个支持类,其主要意思是在发起一个页面请求时打开Hibernate的Session,一直保持这个 Session,直到这个请求结束,具体是通过一个Filter来实现的。
由于Hibernate引入了Lazy Load特性,使得脱离Hibernate的Session周期的对象如果再想通过getter方法取到其关联对象的值,Hibernate会抛出一个 LazyLoad的Exception。所以为了解决这个问题,Spring引入了这个Filter,使得Hibernate的Session的生命周期 变长。
有两种方式可以配置实现OpenSessionInView,分别是 OpenSessionInViewInterceptor和OpenSessionInViewFilter,功能完全相同,只不过一个在 web.xml配置,另一个在application.xml配置而已。
我们可以在web.xml中配置opensessioninview,代码如下:
<!-- 配置Spring自动管理Session. 要配置到struts过滤器之前!-->
<filter>
<filter-name>hibernateSessionFilter</filter-name>
<filter-class>
org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>hibernateSessionFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
8、ThreadLocal模式管理session
我们知道Session是由SessionFactory负责创建的,而SessionFactory的实现是线程安全的,多个并发的线程可以同时访问一 个SessionFactory并从中获取Session实例,那么Session是否是线程安全的呢?很遗憾,答案是否定的。
早在Java1.2推出之时,Java平台中就引入了一个新的支持:java.lang.ThreadLocal,给我们在编写多线程程序时提供了 一种新的选择。ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是thread local variable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。线程局部变量(ThreadLocal)其实的功用非常简单, 就是为每一个使用某变量的线程都提供一个该变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像 每一个线程都完全拥有一个该变量。
ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单,在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本。
比如下面的示例实现(为了简单,没有考虑集合的泛型):
public class ThreadLocal {
private Map values = Collections.synchronizedMap(new HashMap());
public Object get() {
Thread currentThread = Thread.currentThread();
Object result = values.get(currentThread);
if(result == null&&!values.containsKey(currentThread)) {
result = initialValue();
values.put(currentThread, result);
}
return result;
}
public void set(Object newValue) {
values.put(Thread.currentThread(), newValue);
}
public Object initialValue() {
return null;
}
}
那麽具体如何利用ThreadLocal来管理Session呢?Hibernate官方文档手册的示例之中,提供了一个通过ThreadLocal维护Session的好榜样:
public class HibernateUtil {
public static final SessionFactory sessionFactory;
static {
try {
sessionFactory = new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
}
public static final ThreadLocal<Session>session=new ThreadLocal<Session>();
public static Session currentSession() throws HibernateException {
Session s = session.get();
if(s == null) {
s = sessionFactory.openSession();
session.set(s);
}
return s;
}
public static void closeSession() throws HibernateException {
Session s = session.get();
if(s != null) {
s.close();
}
session.set(null);
}
}
只要借助上面的工具类获取Session实例,我们就可以实现线程范围内的Session共享,从而避免了线程中频繁的创建和销毁Session实例。当 然,不要忘记在用完后关闭Session。写到这里,想再多说一些,也许大多数时候我们的DAO并不会涉及到多线程的情形,比如我们不会将DAO的代码写 在Servlet之中,那样不是良好的设计,我自己通常会在service层的代码里访问DAO的方法。但是我还是建议采用以上的工具类来管理 Session,毕竟我们不能仅仅考虑今天为自己做什么,还应该考虑明天为自己做什么!