1.Spring整合JDBC(基于数据库连接池)
1)Spring框架对数据库访问技术提供了以下几点支持
--提供了编写DAO的工具类DaoSupport,Template
--提供了一致的异常处理层次DataAccessException
--提供了声明式事务管理方法(基于AOP方式配置控制事务)
2)Spring整合JDBC的步骤(示例见SpringTest02)

--引入Spring开发包和配置
引入JDBC开发包
--根据COST表编写实体类
--定义CostDao接口,编写实现类并继承JdbcDaoSupport,在实现类的增删改查方法中,利用super.getJdbcTemplate()工具完成相应操作
-
@Repository("jdbcCostDAO")
-
// @Scope("singleton")// 默认情况是单例
-
public class JdbcCostDAO extends JdbcDaoSupport implements ICostDao {
-
-
@Resource
-
// 将容器中的dataSource给DAOSupport注入
-
public void setMyDataSource(DataSource ds) {
-
super.setDataSource(ds);// DaoSupport利用ds实例化template对象,使得getJdbcTemplate()可以获取连接
-
}
-
-
@Override
-
public List<Cost> findAll() throws DataAccessException {
-
String sql = "select * from COST";
-
CostMapper mapper = new CostMapper();
-
List<Cost> list = super.getJdbcTemplate().query(sql, mapper);
-
return list;
-
}
-
-
@Override
-
public List<Cost> findByPage(int page, int pageSize)
-
throws DataAccessException {
-
String sql = "select ID,NAME,BASE_DURATION,BASE_COST,UNIT_COST,STATUS,DESCR,CREATIME,STARTIME,COST_TYPE from(select ID,NAME,BASE_DURATION,BASE_COST,UNIT_COST,STATUS,DESCR,CREATIME,STARTIME,COST_TYPE,rownum n from COST where rownum <? order by ID)where n>?";// 分页
-
// 算当前页最大行:当前页数2*每页显示5=当前最大行10
-
// 小于下一页的最小行
-
int nextMin = page * pageSize + 1;
-
// 大于上一页的最大行
-
int lastMax = (page - 1) * pageSize;
-
Object[] params = { nextMin, lastMax };
-
CostMapper mapper = new CostMapper();
-
List<Cost> list = super.getJdbcTemplate().query(sql, params, mapper);
-
return list;
-
}
-
-
@Override
-
public int findTotalPage(int pageSize) throws DataAccessException {
-
String sql = "select count(*) from COST";
-
int size = super.getJdbcTemplate().queryForInt(sql);
-
if (size % pageSize == 0) {
-
return size / pageSize;
-
} else {
-
return size / pageSize + 1;
-
}
-
}
-
-
@Override
-
public void deleteById(Integer id) throws DataAccessException {
-
String sql = "delete from COST where ID=?";
-
Object[] params = { id };
-
super.getJdbcTemplate().update(sql, params);
-
}
-
-
@Override
-
public Cost findByName(String feeName) throws DataAccessException {
-
String sql = "select * from COST where NAME=?";
-
Object[] params = { feeName };
-
CostMapper mapper = new CostMapper();
-
Cost cost = (Cost) super.getJdbcTemplate().queryForObject(sql, params,
-
mapper);
-
return cost;
-
}
-
-
@Override
-
public Cost findById(Integer id) throws DataAccessException {
-
String sql = "select * from COST where ID=?";
-
Object[] params = { id };
-
CostMapper mapper = new CostMapper();
-
Cost cost = (Cost) super.getJdbcTemplate().queryForObject(sql, params,
-
mapper);
-
return cost;
-
}
-
-
@Override
-
public void updateCost(Cost cost) throws DataAccessException {
-
String sql = "update cost set name=?,base_duration=?,"
-
+ "base_cost=?,unit_cost=?,cost_type=?,descr=? " + "where id=?";
-
Object[] params = { cost.getName(), cost.getBaseDuration(),
-
cost.getBaseCost(), cost.getUnitCost(), cost.getCostType(),
-
cost.getDescr(), cost.getId() };
-
super.getJdbcTemplate().update(sql, params);
-
}
-
-
}
CostMapper.java:
-
public class CostMapper implements RowMapper{
-
//将rs中当前游标指定的记录转换成实体对象
-
public Object mapRow(ResultSet rs, int index) throws SQLException {
-
Cost c = new Cost();
-
c.setId(rs.getInt("id"));
-
c.setName(rs.getString("name"));
-
c.setBaseDuration(rs.getInt("base_duration"));
-
c.setBaseCost(rs.getDouble("base_cost"));
-
c.setUnitCost(rs.getDouble("unit_cost"));
-
c.setStatus(rs.getString("status"));
-
c.setDescr(rs.getString("descr"));
-
c.setCreateTime(rs.getDate("creatime"));
-
c.setStartTime(rs.getDate("startime"));
-
c.setCostType(rs.getString("cost_type"));
-
return c;
-
}
-
}
--将CostDao实现组件扫描到Spring容器
--在Spring容器定义一个连接池对象(需引入连接池开发包[dbcp+数据库驱动],在Spring配置中添加dataSource组件定义),然后将连接池对象给CostDao注入,目是为JdbcTemplate设置Connection连接资源.(方法:在Dao中定义一个setXXX方法,接收注入的DataSource对象,然后给DaoSupport传入)
applicationContext.xml:
-
<span style="font-size:14px;"><?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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
-
xsi:schemaLocation="http://www.springframework.org/schema/beans
-
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
-
http://www.springframework.org/schema/aop
-
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
-
http://www.springframework.org/schema/context
-
http://www.springframework.org/schema/context/spring-context-3.0.xsd
-
http://www.springframework.org/schema/tx
-
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
-
">
-
-
<!-- 开启组件扫描 -->
-
<context:component-scan base-package="com.test"></context:component-scan>
-
<!-- 定义一个连接池对象dataSource -->
-
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource">
-
<property name="username" value="system"></property>
-
<property name="password" value="123456"></property>
-
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"></property>
-
<property name="url" value="jdbc:oracle:thin:@localhost:1521:test"></property>
-
<property name="maxActive" value="20"></property><!-- 最大连接数 -->
-
<property name="initialSize" value="1"></property><!-- 初始连接数 -->
-
</bean>
-
</beans></span>
--测试Dao方法
-
@Test
-
public void test1() {
-
String conf = "/applicationContext.xml";
-
ApplicationContext ac = new ClassPathXmlApplicationContext(conf);
-
-
ICostDao costDao = (ICostDao) ac.getBean("jdbcCostDAO");
-
//costDao.deleteById(122);
-
List<Cost> list = costDao.findAll();
-
for (Cost c : list) {
-
System.out.println(c.getId() + " " + c.getName());
-
}
-
}
3)JdbcTemplate API的使用
--update() 用于执行增删改SQL语句
--queryForObject() 用于执行查询一行结果的SQL
--query() 用于执行查询多行结果的SQL
--queryForInt() 用于查询返回一个数值的SQL
PS:连接池概念和优点
连接池是用于管理数据库Connection对象的工具,它可以将Connection对象数量控制在一个安全范围内。连接池中的Connection对象始终与数据库保持联通,避免频繁的创建和释放连接。
2.Spring整合Hibenrate
(示例见SpringTest03)

a.引入开发包
Spring,Hibernate,数据库驱动
b.根据COST表编写实体类和hbm.xml
hbm.xml:
-
<?xml version="1.0" encoding="utf-8"?>
-
<!DOCTYPE hibernate-mapping PUBLIC
-
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
-
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
-
<hibernate-mapping>
-
<!-- 通过class指定实体类和表的关系 -->
-
<class name="com.test.pojo.Cost" table="cost" dynamic-update="true">
-
<!-- 通过id元素指定主键属性和字段的关系: name:指定主键属性 column:指定主键字段 -->
-
<id name="id" type="integer" column="id">
-
<!-- 用来指明主键的生成方式 -->
-
<generator class="sequence">
-
<param name="sequence">cost_seq</param>
-
</generator>
-
</id>
-
<!-- 通过property元素指定属性和字段的关系: name:指定的是实体类中的属性 column:指定都是表中的字段 type:指定的是属性与字段转换时的类型 -->
-
<property name="name" type="string" column="name" />
-
<property name="baseDuration" type="integer" column="base_duration" />
-
<property name="baseCost" type="double" column="base_cost" />
-
<property name="unitCost" type="double" column="unit_cost" />
-
<property name="status" type="string" column="status" />
-
<property name="descr" type="string" column="descr" />
-
<property name="createTime" type="date" column="creatime" />
-
<property name="startTime" type="date" column="startime" />
-
<property name="costType" type="string" column="cost_type" />
-
-
</class>
-
</hibernate-mapping>
c.定义DAO接口,根据接口编写实现类(继承HibernateDaoSupport,使用HibernateTemplate)
--HibernateTemplate提供了增删改查处理方法。save()、delete()、updata()、load()、get()、find()
-
@Repository("hibernateCostDAO")
-
// @Scope("singleton")// 默认情况是单例
-
public class HibernateCostDAO extends HibernateDaoSupport implements ICostDao {
-
-
@Resource
-
public void setMySessionFactory(SessionFactory sf) {
-
// 将注入的sessionFactory,用于实例化template
-
super.setSessionFactory(sf);
-
}
-
-
@Override
-
public List<Cost> findAll() throws DataAccessException {
-
String sql = " from Cost";
-
List<Cost> list = super.getHibernateTemplate().find(sql);
-
return list;
-
}
-
-
@Override
-
public List<Cost> findByPage(int page, int pageSize) {
-
List list = (List) super.getHibernateTemplate().execute(
-
new HibernateCallback() {
-
-
@Override
-
public Object doInHibernate(Session session)
-
throws HibernateException, SQLException {
-
String hql = "from Cost";
-
Query query = session.createQuery(hql);
-
int begin = (page - 1) * pageSize;
-
query.setFirstResult(begin);
-
query.setMaxResults(pageSize);
-
-
return query.list();
-
}
-
});
-
-
return list;
-
}
-
-
@Override
-
public int findTotalPage(int pageSize) throws DataAccessException {
-
String hql = "select count(*) from Cost";
-
Session session = super.getSession();
-
long size = (Long) session.createQuery(hql).uniqueResult();
-
session.close();// 一定记得关闭session,使用getHibernateTemplate()会在调用结束后自动关闭session
-
if (size % pageSize == 0) {
-
return (int) (size / pageSize);
-
} else {
-
return (int) (size / pageSize + 1);
-
}
-
}
-
-
@Override
-
public void deleteById(Integer id) throws DataAccessException {
-
Cost cost = findById(id);
-
super.getHibernateTemplate().delete(cost);
-
}
-
-
@Override
-
public Cost findByName(String feeName) throws DataAccessException {
-
String sql = "from Cost where NAME=?";
-
Object[] params = { feeName };
-
List<Cost> cost = super.getHibernateTemplate().find(sql, params);
-
if (cost.isEmpty()) {
-
return null;
-
}
-
return cost.get(0);
-
}
-
-
@Override
-
public Cost findById(Integer id) throws DataAccessException {
-
Cost cost = (Cost) super.getHibernateTemplate().load(Cost.class, id);
-
return cost;
-
}
-
-
@Override
-
public void updateCost(Cost cost) throws DataAccessException {
-
super.getHibernateTemplate().update(cost);
-
}
-
-
}
d.将DAO实现类扫描到Spring容器
e.在Spring配置中,定义SessionFactory对象,给DAO注入。
-
<!-- 开启组件扫描 -->
-
<context:component-scan base-package="com.test"></context:component-scan>
-
-
<!-- 定义一个连接池对象dataSource -->
-
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource">
-
<property name="username" value="system"></property>
-
<property name="password" value="123456"></property>
-
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"></property>
-
<property name="url" value="jdbc:oracle:thin:@localhost:1521:test"></property>
-
<property name="maxActive" value="20"></property><!-- 最大连接数 -->
-
<property name="initialSize" value="1"></property><!-- 初始连接数 -->
-
</bean>
-
-
<!-- 定义SessionFactory组件,给DAO注入 -->
-
<bean id="sessionFactory"
-
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
-
<!--注入连接池对象 -->
-
<property name="dataSource" ref="myDataSource"></property>
-
<!-- 注入Hibernate 框架参数 -->
-
<property name="hibernateProperties">
-
<props>
-
<prop key="hibernate.dialect">org.hibernate.dialect.OracleDialect</prop>
-
<prop key="hibernate.show_sql">true</prop>
-
<prop key="hibernate.format_sql">true</prop>
-
</props>
-
</property>
-
<!-- 注入映射描述文件 -->
-
<property name="mappingResources">
-
<list>
-
<value>com/test/pojo/Cost.hbm.xml</value>
-
</list>
-
</property>
-
</bean>
f.测试
-
@Test
-
public void test1() {
-
String conf = "/applicationContext.xml";
-
ApplicationContext ac = new ClassPathXmlApplicationContext(conf);
-
-
ICostDao costDao = (ICostDao) ac.getBean("hibernateCostDAO");
-
// costDao.deleteById(122);
-
List<Cost> list = costDao.findAll();
-
for (Cost c : list) {
-
System.out.println(c.getId() + " " + c.getName());
-
}
-
}
PS:如果需要使用Session可以采用下面两种方法:
(1)用HibernateDaoSupport提供的getSession()方法,用完后必须关闭。
(2)使用HibernateTemplate.execute()方法,以回调方式使用session.不需要关闭,因为由HibernateTemplate统一关闭处理(推荐)
3.Spring整合Struts
(示例见SpringTest04)

1)单独使用Struts开发的流程
hello.action-->Struts Filter控制器-->struts.xml-->HelloAction-->hello.jsp
2)引入Spring开发框架
引入jar包和配置
*3)采用组件扫描,将Action交给Spring管理
applicationContext.xml:
-
<!-- 开启组件扫描 -->
-
<context:component-scan base-package="com.test"></context:component-scan>
*4)引入struts2-spring-plugin.jar开发包【原理见下文4】
(struts2利用该插件去spring容器获取Action对象,处理请求)
*5)修改原有<action>配置,将class属性指定成Spring中Action对象的id值
-
@Service
-
@Scope("prototype")
-
public class HelloAction {
-
private String msg;
-
@Resource
-
private MessageDao dao;
-
-
public String execute() {
-
msg = dao.getMessage();
-
System.out.println("执行Action...");
-
return "success";
-
}
-
-
//省略get/setMsg();
-
}
-
//交由StrutsSpringObjectFactory创建
-
//将Spring容器中id名与setxxx一致的Bean对象注入
-
public class HelloAction1 {
-
private String msg;
-
private MessageDao dao;
-
-
// StrutsSpringObject将Spring容器中id名与setxxx一致的Bean对象注入
-
public void setJdbcMessageDao(MessageDao dao) {
-
this.dao = dao;
-
}
-
-
public String execute() {
-
msg = dao.getMessage();
-
return "success";
-
}
-
-
//省略get/setMsg();
-
}
-
public interface MessageDao {
-
public String getMessage();
-
}
-
@Repository//配置注解后默认id=jdbcMessageDao
-
public class JdbcMessageDao implements MessageDao {
-
public String getMessage() {
-
// 访问数据库,获取出信息
-
System.out.println("访问数据库,获取出信息");
-
return "新年快乐";
-
}
-
}
struts.xml:
-
<struts>
-
<!-- http://localshost:8080/struts01a/NAMESPACE/ACTIONNAME.action -->
-
<!-- 记忆口诀: URL虽然长,namespace站中央; action name排队尾,action后缀不要忘; extends包继承,action
-
class做封装; action method找方法,返回值让result忙 -->
-
<package name="demo1" namespace="/day01" extends="struts-default">
-
<!-- struts2-spring-plugin.jar利用helloAction 当做id去Spring容器寻找Bean对象 -->
-
<!-- 走StrutsSpringObjectFactory的try流程,整合方法一(参考4中原理分析图ssh-1.jpg) -->
-
<action name="hello" class="helloAction" method="execute">
-
<result>/hello.jsp</result>
-
</action>
-
<!-- 走StrutsSpringObjectFactory的catch流程,整合方法二(参考4中原理分析图ssh-2.jpg),此时生成的Action在容器外 -->
-
<action name="hello1" class="com.test.action.HelloAction1"
-
method="execute">
-
<result>/hello.jsp</result>
-
</action>
-
</package>
-
</struts>
hello.jsp:
-
<html>
-
<head>
-
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
-
<title>Insert title here</title>
-
</head>
-
<body>
-
信息:
-
<br />${msg }
-
</body>
-
</html>
index.jsp:
-
<html>
-
<head>
-
</head>
-
<body>
-
<a href="day01/hello.action">Struts+Spring整合案例1</a><!--走try逻辑,见下文4原理分析 -->
-
<a href="day01/hello1.action">Struts+Spring整合案例2</a><!-- 走catch逻辑,见下文4原理分析 -->
-
<br>
-
</body>
-
</html>
*6)在web.xml中定义ContextLoaderListener,用于在服务器启动时,实例化Spring容器,在web.xml中采用<content-param>指定Spring。
web.xml:
-
<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>
-
-
<!-- 指定Spring配置位置 -->
-
<context-param>
-
<param-name>contextConfigLocation</param-name>
-
<param-value>classpath:applicationContext.xml</param-value>
-
</context-param>
-
-
<!-- 封装了实例化Spring容器功能 -->
-
<listener>
-
<listener-class>
-
org.springframework.web.context.ContextLoaderListener
-
</listener-class>
-
</listener>
-
-
<welcome-file-list>
-
<welcome-file>index.jsp</welcome-file>
-
</welcome-file-list></span>
7)测试

点击任何链接都可看到相同效果:

PS:整合后的流程如下
hello.action-->Struts Filter控制器-->struts.xml-->struts2-spring-plugin.jar-->Spring容器获取Action对象(可以注入dao)-->hello.jsp
4.struts2-spring-plugin.jar原理
在Struts2底层有一个StrutsObjectFactory组件,用于实例化Struts2中的Action等组件对象。当引入了struts2-spring-plugin之后,该插件提供了一个StrutsSpringObjectFactory组件,它也属于ObjectFactory组件。在struts2-spring-plugin配置(struts-plugin.xml)中将原来Struts2的ObjectFactory指定成了StrutsSpringObjectFactory。因此Struts再接收请求后,会利用StrutsSpringObjectFactory获取Action对象。
struts-plugin.xml核心代码:
-
<struts>
-
<bean type="com.opensymphony.xwork2.ObjectFactory" name="spring" class="org.apache.struts2.spring.StrutsSpringObjectFactory"/>
-
-
<!-- Make the Spring object factory the automatic default -->
-
<constant name="struts.objectFactory" value="spring" />
-
...
-
-
</struts>
而在StrutsSpringObjectFactory中提供一个获取Action对象的方法,该方法主要逻辑如下:
-
public class StrutsSpringObjectFactory{
-
//用于获取Action对象
-
public Object buildAction(){
-
try{
-
//ssh-1.jpg整合流程1
-
//获取web.xml中listener创建的Spring容器对象
-
ApplicationContext ac = ...;
-
//调用ac.getBean()获取Spring容器的Bean对象。用<action>配置中的class属性值去寻找
-
Object action = ac.getBean(class属性值)
-
//将返回的action对象交给Struts框架处理请求
-
return obj;
-
}catch(Exception ex){
-
//ssh-2.jpg整合流程2
-
//如果找不到class名字的Bean对象,会首先利用反射技术创建一个Action对象(在Spring容器外)
-
Class c = Class.forName(class属性值);
-
Object obj = c.newInstance();
-
//访问Spring容器,将Spring容器中id名和Action对象属性名一致的Bean对象,给Action对象注入
-
//返回Action对象给Struts框架处理请求
-
}
-
}
-
}
综上,由于struts2-spring-plugin插件中StrutsSpringObjectFactory的buildAction的try...catch...流程,导致Struts和Spring有两种整合方
法和流程。参考下图ssh1.jpg和ssh2.jpg
ssh1.jpg:

ssh2.jpg:

PS:选择哪种整合方法?
一般如果有明确的Action、Service、DAO组件分类,建议使用ssh2.jpg整合流程;
如果没有封装Service组件,采用Action直接调用DAO的方式,则建议使用ssh1.jsp,将Action放入容器内;
若需采用AOP切入事务管理,因为AOP只能将容器内的Bean作为目标对象,故不能将Action分离到容器外;
5.SSH处理流程
1)启动服务器时,加载web.xml,将Spring容器和Struts控制器实例化
2)客户端发出URL请求,请求进入Struts控制处理器
3)Struts控制器如果发现是/login.action或/login格式请求,会进入Action组件的处理流程。Struts控制器根据struts.xml中<action>配置寻找Action对象。
4)Struts控制器调用struts-spring-plugin.jar开发包提供的ObjectFactory组件获取Action对象。有以下两种获取方法:
a.会利用<action>配置的class属性当id去Spring容器中寻找。
b.会根据<action>配置的class属性利用反射机制生成一个Action对象。然后可以将Spring容器中Bean对象给Action对象属性注入(容器中Bean对象的id=Action对象,可以使用@Resource注解按类型注入)
5)Struts控制器执行Action对象execute()方法处理请求。(DAO等组件以注入的方式使用)
6)Struts控制器根据execute()方法返回值调用Result组件,生成响应信息
7)给客户端响应输出