1.Spring整合JDBC(基于数据库连接池)
1)Spring框架对数据库访问技术提供了以下几点支持 --提供了编写DAO的工具类DaoSupport,Template --提供了一致的异常处理层次DataAccessException --提供了声明式事务管理方法(基于AOP方式配置控制事务)
2)Spring整合JDBC的步骤(示例见SpringTest02)
--引入Spring开发包和配置
引入JDBC开发包
--根据COST表编写实体类
--定义CostDao接口,编写实现类并继承JdbcDaoSupport,在实现类的增删改查方法中,利用super.getJdbcTemplate()工具完成相应操作
@Repository ( "jdbcCostDAO" ) public class JdbcCostDAO extends JdbcDaoSupport implements ICostDao { @Resource public void setMyDataSource(DataSource ds) { super .setDataSource(ds); } @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>?" ; 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{ 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 > < 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" ); 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 name = "com.test.pojo.Cost" table = "cost" dynamic-update = "true" > < id name = "id" type = "integer" column = "id" > < generator class = "sequence" > < param name = "sequence" > cost_seq </ param > </ generator > </ id > < 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" ) public class HibernateCostDAO extends HibernateDaoSupport implements ICostDao { @Resource public void setMySessionFactory(SessionFactory sf) { 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(); 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 > < 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 > < bean id = "sessionFactory" class = "org.springframework.orm.hibernate3.LocalSessionFactoryBean" > < property name = "dataSource" ref = "myDataSource" > </ property > < 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" ); 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" ; } }
public class HelloAction1 { private String msg; private MessageDao dao; public void setJdbcMessageDao(MessageDao dao) { this .dao = dao; } public String execute() { msg = dao.getMessage(); return "success" ; } }
public interface MessageDao { public String getMessage(); }
@Repository public class JdbcMessageDao implements MessageDao { public String getMessage() { System.out.println("访问数据库,获取出信息" ); return "新年快乐" ; } }
struts.xml:
< struts > <!-- 记忆口诀: URL虽然长,namespace站中央; action name排队尾,action后缀不要忘; extends包继承,action class做封装; action method找方法,返回值让result忙 --> < package name = "demo1" namespace = "/day01" extends = "struts-default" > < action name = "hello" class = "helloAction" method = "execute" > < result > /hello.jsp </ result > </ 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 > < a href = "day01/hello1.action" > Struts+Spring整合案例2 </ a > < 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 > < context-param > < param-name > contextConfigLocation </ param-name > < param-value > classpath:applicationContext.xml </ param-value > </ context-param > < 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" /> < constant name = "struts.objectFactory" value = "spring" /> ... </ struts >
而在StrutsSpringObjectFactory中提供一个获取Action对象的方法,该方法主要逻辑如下:
public class StrutsSpringObjectFactory{ public Object buildAction(){ try { ApplicationContext ac = ...; Object action = ac.getBean(class 属性值) return obj; }catch (Exception ex){ Class c = Class.forName(class 属性值); Object obj = c.newInstance(); } } }
综上,由于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)给客户端响应输出