Spring整合开发示例(分别与JDBC、Hibernate、Struts整合)

本文中Spring整合Hibenrate项目下所需数据库文件相关内容下载:
http://download.csdn.net/detail/daijin888888/9551724
本文代码示例(含详细注释、所有jar文件,可运行)下载:
http://download.csdn.net/detail/daijin888888/9562121

GitHub(给个星杯~):

https://github.com/VingeDai/SpringStrutsDemo

https://github.com/VingeDai/SpringHibernateDemo

https://github.com/VingeDai/SpringJDBCDemo

阅读提示:读者在阅读3中内容时,可以结合4中的原理介绍和5中的SSH三大框架整体工作流程理解。

若对文中提到的IOC、AOP概念模糊,可先移步到:Spring基础、IOC(控制反转)、AOP(面向切面编程)、Log4j、注解配置

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)给客户端响应输出

 

转载请注明出处:

http://blog.csdn.net/daijin888888/article/details/51777488

 

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值