Top

JAVA SPRINGMYBATIS02 UNIT06

  1. Spring声明式事务-注解应用
  2. Spring声明式事务-XML配置
  3. Spring声明式事务-回滚机制
  4. RESTful应用案例

1 Spring声明式事务-注解应用

1.1 问题

使用Spring注解实现声明式事务,管理Controller中各业务方法的事务。

1.2 方案

Spring事务注解使用步骤:

1.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:测试未管理事务的情况

复制项目SpringUnit05,创建新项目SpringUnit06。

在EmpController中,增加批量添加员工的方法,并模拟实现,代码如下:

package com.tarena.controller;

import javax.annotation.Resource;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import com.tarena.dao.EmpDao;
import com.tarena.entity.Emp;

@Controller
@RequestMapping("/emp")
public class EmpController {
	
#cold_bold	@Resource
#cold_bold	private EmpDao empDao;

	//其他方法略

#cold_bold	/**
#cold_bold	 * 模拟批量添加员工
#cold_bold	 */
#cold_bold	@RequestMapping("/addEmps.do")
#cold_bold	public String addBatch() {
#cold_bold		// 插入第一个员工
#cold_bold		Emp e1 = new Emp();
#cold_bold		e1.setEname("刘备");
#cold_bold		e1.setJob("皇叔");
#cold_bold		e1.setSal(1000.0);
#cold_bold		e1.setEmpno(10);
#cold_bold		empDao.save(e1);
#cold_bold		
#cold_bold		// 模拟异常
#cold_bold		Integer.valueOf("abc"); //ClassCastException
#cold_bold		
#cold_bold		// 插入第二个员工
#cold_bold		Emp e2 = new Emp();
#cold_bold		e2.setEname("关羽");
#cold_bold		e2.setJob("候");
#cold_bold		e2.setSal(1000.0);
#cold_bold		e2.setEmpno(10);
#cold_bold		empDao.save(e2);
#cold_bold		
#cold_bold		return "redirect:findEmp.do";
#cold_bold	}

}

在TestEmpController中,增加测试方法,测试批量添加员工,代码如下:

package com.tarena.test;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.tarena.controller.EmpController;

public class TestEmpController {
	
//其他方法略

#cold_bold	/**
#cold_bold	 * 测试批量添加员工
#cold_bold	 */
#cold_bold	@Test
#cold_bold	public void test2() throws ClassNotFoundException {
#cold_bold		ApplicationContext ctx = new ClassPathXmlApplicationContext(
#cold_bold				"applicationContext.xml");
#cold_bold		EmpController ctl = ctx.getBean(EmpController.class);
#cold_bold		ctl.addBatch();
#cold_bold	}
	
}

没有执行该测试方法前,员工表中的数据如下图:

图-1

执行该测试方法后,员工表中的数据如下图:

图-2

可见,员工刘备添加成功。但EmpController.addBatch()方法是业务方法,体现了完整的业务逻辑,发生异常时应该整个业务失败,而不是一部分成功一部分失败,因此添加刘备成功是不符合要求的,应该引入事务管理机制来解决此问题。

步骤二:声明事务组件

在applicationContext.xml中声明事务组件,关键代码如下:

#cold_bold	<!-- 声明事务管理组件 -->
#cold_bold	<bean id="txManager" 
#cold_bold		class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
#cold_bold		<property name="dataSource" ref="ds"/> 
#cold_bold	</bean>

步骤三:开启事务注解扫描

在applicationContext.xml中,开启事务注解扫描,关键代码如下:

	<!-- 声明事务管理组件 -->
	<bean id="txManager" 
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
		<property name="dataSource" ref="ds"/> 
	</bean>
	
#cold_bold	<!-- 开启事务注解扫描 -->
#cold_bold	<tx:annotation-driven 
#cold_bold		transaction-manager="txManager" proxy-target-class="true"/>

步骤四:使用注解标识类/方法

在EmpController中,使用注解对该方法启用事务管理,代码如下:

package com.tarena.controller;

import javax.annotation.Resource;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import com.tarena.dao.EmpDao;
import com.tarena.entity.Emp;

@Controller
@RequestMapping("/emp")
#cold_bold@Transactional
public class EmpController {
	
	@Resource
	private EmpDao empDao;

//其他方法略

	/**
	 * 模拟批量添加员工
	 */
	@RequestMapping("/addEmps.do")
	public String addBatch() {
		// 插入第一个员工
		Emp e1 = new Emp();
		e1.setEname("刘备");
		e1.setJob("皇叔");
		e1.setSal(1000.0);
		e1.setEmpno(10);
		empDao.save(e1);
		
		// 模拟异常
		Integer.valueOf("abc"); //ClassCastException
				
		// 插入第二个员工
		Emp e2 = new Emp();
		e2.setEname("关羽");
		e2.setJob("候");
		e2.setSal(1000.0);
		e2.setEmpno(10);
		empDao.save(e2);
		
		return "redirect:findEmp.do";
	}

}

步骤五:测试

手动删除员工表中的刘备,执行测试方法TestEmpController.test2(),然后查询员工表数据如下图:

图-3

可见,在引入了事务管理机制后,业务方法发生异常时,由于事务回滚,所以没有成功插入员工刘备,从而保证了业务的完整性。

1.4 完整代码

本案例的完整代码如下所示:

EmpController完整代码如下:

package com.tarena.controller;

import javax.annotation.Resource;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import com.tarena.dao.EmpDao;
import com.tarena.entity.Emp;

@Controller
@RequestMapping("/emp")
@Transactional
public class EmpController {
	
	@Resource
	private EmpDao empDao;

	/**
	 * 查询员工
	 */
	@RequestMapping("/findEmp.do")
	@Transactional(readOnly=true)
	public String find() {
		// 模拟查询员工数据
		System.out.println("查询员工数据,发送至列表页面.");
		// 制造一个异常,便于测试异常通知
		Integer.valueOf("abc");
		return "emp/emp_list.jsp";
	}

	/**
	 * 模拟批量添加员工
	 */
	@RequestMapping("/addEmps.do")
	public String addBatch() {
		// 插入第一个员工
		Emp e1 = new Emp();
		e1.setEname("刘备");
		e1.setJob("皇叔");
		e1.setSal(1000.0);
		e1.setEmpno(10);
		empDao.save(e1);
		
		// 模拟异常
		Integer.valueOf("abc"); //ClassCastException
		
		// 插入第二个员工
		Emp e2 = new Emp();
		e2.setEname("关羽");
		e2.setJob("候");
		e2.setSal(1000.0);
		e2.setEmpno(10);
		empDao.save(e2);
		
		return "redirect:findEmp.do";
	}

}

TestEmpController完整代码如下:

package com.tarena.test;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.tarena.controller.EmpController;

public class TestEmpController {
	
	/**
	 * 测试查询员工
	 */
	@Test
	public void test1() {
		ApplicationContext ctx = new ClassPathXmlApplicationContext(
				"applicationContext.xml");
		EmpController ctl = ctx.getBean(EmpController.class);
		ctl.find();
	}

	/**
	 * 测试批量添加员工
	 */
	@Test
	public void test2() throws ClassNotFoundException {
		ApplicationContext ctx = new ClassPathXmlApplicationContext(
				"applicationContext.xml");
		EmpController ctl = ctx.getBean(EmpController.class);
		ctl.addBatch();
	}
	
}

applicationContext.xml完整代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="/www.springframework.org/schema/beans" 
	xmlns:xsi="/www.w3.org/2001/XMLSchema-instance"
	xmlns:context="/www.springframework.org/schema/context" 
	xmlns:jdbc="/www.springframework.org/schema/jdbc"  
	xmlns:jee="/www.springframework.org/schema/jee" 
	xmlns:tx="/www.springframework.org/schema/tx"
	xmlns:aop="/www.springframework.org/schema/aop" 
	xmlns:mvc="/www.springframework.org/schema/mvc"
	xmlns:jpa="/www.springframework.org/schema/data/jpa"
	xsi:schemaLocation="
		/www.springframework.org/schema/beans /www.springframework.org/schema/beans/spring-beans-3.2.xsd
		/www.springframework.org/schema/context /www.springframework.org/schema/context/spring-context-3.2.xsd
		/www.springframework.org/schema/jdbc /www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd
		/www.springframework.org/schema/jee /www.springframework.org/schema/jee/spring-jee-3.2.xsd
		/www.springframework.org/schema/tx /www.springframework.org/schema/tx/spring-tx-3.2.xsd
		/www.springframework.org/schema/data/jpa /www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
		/www.springframework.org/schema/aop /www.springframework.org/schema/aop/spring-aop-3.2.xsd
		/www.springframework.org/schema/mvc /www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
	
	<!-- 配置数据源 -->
	<bean id="ds" 
		class="org.apache.commons.dbcp.BasicDataSource"
		destroy-method="close">
		<property name="url" 
			value="jdbc:oracle:thin:@localhost:1521:xe"/>
		<property name="driverClassName" 
			value="oracle.jdbc.OracleDriver"/>
		<property name="username" value="lhh"/>
		<property name="password" value="123456"/>
	</bean>
	
	<!-- 配置SqlSessionFactory -->
	<bean id="sqlSessionFactory" 
		class="org.mybatis.spring.SqlSessionFactoryBean">  
      <property name="dataSource" ref="ds" />
      <property name="mapperLocations" 
      	value="classpath:com/tarena/entity/*.xml"/>
    </bean>
    
    <!-- 配置MyBatis注解 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
       <property name="basePackage" value="com.tarena.dao" />
       <property name="annotationClass" 
           value="com.tarena.annotation.MyBatisRepository"/>
	</bean>
	
	<!-- 开启注解扫描 -->
	<context:component-scan base-package="com.tarena" />

	<!-- 支持@RequestMapping请求和Controller映射 -->
	<mvc:annotation-driven />

	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/"/>
		<property name="suffix" value=".jsp"/>
	</bean>	
	
	<!-- 声明方面组件 -->
	<!-- <bean id="operateLogger" class="com.tarena.aspect.OperateLogger"/> -->
	
	<!-- 配置AOP -->
	<!-- <aop:config>
		<aop:aspect ref="operateLogger">
			<aop:before method="log1" 
				pointcut="within(com.tarena.controller..*)"/>
		</aop:aspect>
		<aop:aspect ref="operateLogger">
			<aop:around method="log2" 
				pointcut="within(com.tarena.controller..*)"/>
		</aop:aspect>
		<aop:aspect ref="operateLogger">
			<aop:after-throwing method="log3" throwing="e"
				pointcut="within(com.tarena.controller..*)"/>
		</aop:aspect>
	</aop:config> -->
	
	<!-- 开启AOP注解扫描 -->
	<aop:aspectj-autoproxy proxy-target-class="true"/>
	
	<!-- 声明事务管理组件 -->
	<bean id="txManager" 
	class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
		<property name="dataSource" ref="ds"/> 
	</bean>
	
	<!-- 开启事务注解扫描 -->
	<tx:annotation-driven 
		transaction-manager="txManager" proxy-target-class="true"/> 

</beans>

2 Spring声明式事务-XML配置

2.1 问题

使用XML配置代替注解,实现Spring声明式事务。

2.2 方案

XML配置声明式事务,关键代码如下:

	<!-- XML配置声明事务范围及类型 -->
	<tx:advice id="txAdvice" transaction-manager="txManager">
		<tx:attributes>
			<tx:method name="find*" read-only="true" />
			<tx:method name="add*" propagation="REQUIRED" 
				rollback-for="java.lang.Exception"/>
			<tx:method name="update*" propagation="REQUIRED" 
				rollback-for="java.lang.Exception"/>
			<tx:method name="delete*" propagation="REQUIRED" 
				rollback-for="java.lang.Exception"/>
		</tx:attributes>
	</tx:advice>
	<aop:config proxy-target-class="true">
		<aop:advisor advice-ref="txAdvice" 
			pointcut="within(com.tarena.controller..*)" />
	</aop:config>

2.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:取消事务注解

在applicationContext.xml中,去掉开启事务注解扫描的代码,使事务注解失效,关键代码如下:

#cold_bold	<!-- 开启事务注解扫描 -->
#cold_bold	<!-- <tx:annotation-driven 
#cold_bold		transaction-manager="txManager" proxy-target-class="true"/> -->

步骤二:使用XML配置声明式事务

在applicationContext.xml中,使用XML配置声明式事务,关键代码如下:

#cold_bold	<!-- XML配置声明事务范围及类型 -->
#cold_bold	<tx:advice id="txAdvice" transaction-manager="txManager">
#cold_bold		<tx:attributes>
#cold_bold			<tx:method name="find*" read-only="true" />
#cold_bold			<tx:method name="add*" propagation="REQUIRED" />
#cold_bold			<tx:method name="update*" propagation="REQUIRED" />
#cold_bold			<tx:method name="delete*" propagation="REQUIRED" />
#cold_bold		</tx:attributes>
#cold_bold	</tx:advice>
#cold_bold	<aop:config proxy-target-class="true">
#cold_bold		<aop:advisor advice-ref="txAdvice" 
#cold_bold			pointcut="within(com.tarena.controller..*)" />
#cold_bold	</aop:config>

步骤三:测试

执行测试方法TestEmpController.test2(),然后查询员工表数据如下图:

图-4

可见,XML配置的声明式事务与注解方式效果一样。

2.4 完整代码

本案例的完整代码如下所示:

applicationContext.xml完整代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="/www.springframework.org/schema/beans" 
	xmlns:xsi="/www.w3.org/2001/XMLSchema-instance"
	xmlns:context="/www.springframework.org/schema/context" 
	xmlns:jdbc="/www.springframework.org/schema/jdbc"  
	xmlns:jee="/www.springframework.org/schema/jee" 
	xmlns:tx="/www.springframework.org/schema/tx"
	xmlns:aop="/www.springframework.org/schema/aop" 
	xmlns:mvc="/www.springframework.org/schema/mvc"
	xmlns:jpa="/www.springframework.org/schema/data/jpa"
	xsi:schemaLocation="
		/www.springframework.org/schema/beans /www.springframework.org/schema/beans/spring-beans-3.2.xsd
		/www.springframework.org/schema/context /www.springframework.org/schema/context/spring-context-3.2.xsd
		/www.springframework.org/schema/jdbc /www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd
		/www.springframework.org/schema/jee /www.springframework.org/schema/jee/spring-jee-3.2.xsd
		/www.springframework.org/schema/tx /www.springframework.org/schema/tx/spring-tx-3.2.xsd
		/www.springframework.org/schema/data/jpa /www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
		/www.springframework.org/schema/aop /www.springframework.org/schema/aop/spring-aop-3.2.xsd
		/www.springframework.org/schema/mvc /www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
	
	<!-- 配置数据源 -->
	<bean id="ds" 
		class="org.apache.commons.dbcp.BasicDataSource"
		destroy-method="close">
		<property name="url" 
			value="jdbc:oracle:thin:@localhost:1521:xe"/>
		<property name="driverClassName" 
			value="oracle.jdbc.OracleDriver"/>
		<property name="username" value="lhh"/>
		<property name="password" value="123456"/>
	</bean>
	
	<!-- 配置SqlSessionFactory -->
	<bean id="sqlSessionFactory" 
		class="org.mybatis.spring.SqlSessionFactoryBean">  
      <property name="dataSource" ref="ds" />
      <property name="mapperLocations" 
      	value="classpath:com/tarena/entity/*.xml"/>
    </bean>
    
    <!-- 配置MyBatis注解 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
       <property name="basePackage" value="com.tarena.dao" />
       <property name="annotationClass" 
           value="com.tarena.annotation.MyBatisRepository"/>
	</bean>
	
	<!-- 开启注解扫描 -->
	<context:component-scan base-package="com.tarena" />

	<!-- 支持@RequestMapping请求和Controller映射 -->
	<mvc:annotation-driven />

	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/"/>
		<property name="suffix" value=".jsp"/>
	</bean>	
	
	<!-- 声明方面组件 -->
	<!-- <bean id="operateLogger" class="com.tarena.aspect.OperateLogger"/> -->
	
	<!-- 配置AOP -->
	<!-- <aop:config>
		<aop:aspect ref="operateLogger">
			<aop:before method="log1" 
				pointcut="within(com.tarena.controller..*)"/>
		</aop:aspect>
		<aop:aspect ref="operateLogger">
			<aop:around method="log2" 
				pointcut="within(com.tarena.controller..*)"/>
		</aop:aspect>
		<aop:aspect ref="operateLogger">
			<aop:after-throwing method="log3" throwing="e"
				pointcut="within(com.tarena.controller..*)"/>
		</aop:aspect>
	</aop:config> -->
	
	<!-- 开启AOP注解扫描 -->
	<aop:aspectj-autoproxy proxy-target-class="true"/>
	
	<!-- 声明事务管理组件 -->
	<bean id="txManager" 
	class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
		<property name="dataSource" ref="ds"/> 
	</bean>
	
	<!-- 开启事务注解扫描 -->
	<!-- <tx:annotation-driven 
		transaction-manager="txManager" proxy-target-class="true"/> -->
		
	<!-- XML配置声明事务范围及类型 -->
	<tx:advice id="txAdvice" transaction-manager="txManager">
		<tx:attributes>
			<tx:method name="find*" read-only="true" />
			<tx:method name="add*" propagation="REQUIRED" 
				rollback-for="java.lang.Exception"/>
			<tx:method name="update*" propagation="REQUIRED" 
				rollback-for="java.lang.Exception"/>
			<tx:method name="delete*" propagation="REQUIRED" 
				rollback-for="java.lang.Exception"/>
		</tx:attributes>
	</tx:advice>
	<aop:config proxy-target-class="true">
		<aop:advisor advice-ref="txAdvice" 
			pointcut="within(com.tarena.controller..*)" />
	</aop:config>		

</beans>

3 Spring声明式事务-回滚机制

3.1 问题

默认情况下,声明式事务只能捕获RuntimeException,并使事务回滚,请处理非RuntimeException情况下的事务回滚。

3.2 方案

使用注解实现声明式事务时,按如下方式指定异常:

@Transactional(rollbackFor=ClassNotFoundException.class)

使用XML配置实现声明式事务时,按如下方式指定异常:

<tx:method name="update*" propagation="REQUIRED"

rollback-for="java.lang.ClassNotFoundException"/>

本案例中演示第二种方式。

3.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:测试非RuntimeException异常的情况

修改EmpController.addBatch()方法,将模拟的异常换成非RuntimeException,代码如下:

package com.tarena.controller;

import javax.annotation.Resource;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import com.tarena.dao.EmpDao;
import com.tarena.entity.Emp;

@Controller
@RequestMapping("/emp")
@Transactional
public class EmpController {
	
	@Resource
	private EmpDao empDao;

//其他方法略

	/**
	 * 模拟批量添加员工
	 */
	@RequestMapping("/addEmps.do")
	public String addBatch() throws ClassNotFoundException {
		// 插入第一个员工
		Emp e1 = new Emp();
		e1.setEname("刘备");
		e1.setJob("皇叔");
		e1.setSal(1000.0);
		e1.setEmpno(10);
		empDao.save(e1);
		
		// 模拟异常
#cold_bold//		Integer.valueOf("abc"); //ClassCastException
#cold_bold		Class.forName("BadClass"); //ClassNotFoundException
		
		// 插入第二个员工
		Emp e2 = new Emp();
		e2.setEname("关羽");
		e2.setJob("候");
		e2.setSal(1000.0);
		e2.setEmpno(10);
		empDao.save(e2);
		
		return "redirect:findEmp.do";
	}

}

执行测试方法TestEmpController.test2(),然后查询员工表数据如下图:

图-5

可见,对于非RuntimeException异常,默认的声明式事务是处理不了的。

步骤二:注解上指定处理的异常类型

修改applicationContext.xml,在通知配置上指定异常类型,关键代码如下:

	<!-- XML配置声明事务范围及类型 -->
	<tx:advice id="txAdvice" transaction-manager="txManager">
		<tx:attributes>
			<tx:method name="find*" read-only="true" />
#cold_bold			<tx:method name="add*" propagation="REQUIRED" 
#cold_bold				rollback-for="java.lang. ClassNotFoundException"/>
			<tx:method name="update*" propagation="REQUIRED" />
			<tx:method name="delete*" propagation="REQUIRED" />
		</tx:attributes>
	</tx:advice>
	<aop:config proxy-target-class="true">
		<aop:advisor advice-ref="txAdvice" 
			pointcut="within(com.tarena.controller..*)" />
	</aop:config>

步骤三:测试

手动删除员工表中的刘备,执行测试方法TestEmpController.test2(),然后查询员工表数据如下图:

图-6

可见,在注解上指定了要处理的异常类型后,它就可以处理非RuntimeException了。

3.4 完整代码

本案例的完整代码如下所示:

applicationContext.xml完整代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="/www.springframework.org/schema/beans" 
	xmlns:xsi="/www.w3.org/2001/XMLSchema-instance"
	xmlns:context="/www.springframework.org/schema/context" 
	xmlns:jdbc="/www.springframework.org/schema/jdbc"  
	xmlns:jee="/www.springframework.org/schema/jee" 
	xmlns:tx="/www.springframework.org/schema/tx"
	xmlns:aop="/www.springframework.org/schema/aop" 
	xmlns:mvc="/www.springframework.org/schema/mvc"
	xmlns:jpa="/www.springframework.org/schema/data/jpa"
	xsi:schemaLocation="
		/www.springframework.org/schema/beans /www.springframework.org/schema/beans/spring-beans-3.2.xsd
		/www.springframework.org/schema/context /www.springframework.org/schema/context/spring-context-3.2.xsd
		/www.springframework.org/schema/jdbc /www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd
		/www.springframework.org/schema/jee /www.springframework.org/schema/jee/spring-jee-3.2.xsd
		/www.springframework.org/schema/tx /www.springframework.org/schema/tx/spring-tx-3.2.xsd
		/www.springframework.org/schema/data/jpa /www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
		/www.springframework.org/schema/aop /www.springframework.org/schema/aop/spring-aop-3.2.xsd
		/www.springframework.org/schema/mvc /www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
	
	<!-- 配置数据源 -->
	<bean id="ds" 
		class="org.apache.commons.dbcp.BasicDataSource"
		destroy-method="close">
		<property name="url" 
			value="jdbc:oracle:thin:@localhost:1521:xe"/>
		<property name="driverClassName" 
			value="oracle.jdbc.OracleDriver"/>
		<property name="username" value="lhh"/>
		<property name="password" value="123456"/>
	</bean>
	
	<!-- 配置SqlSessionFactory -->
	<bean id="sqlSessionFactory" 
		class="org.mybatis.spring.SqlSessionFactoryBean">  
      <property name="dataSource" ref="ds" />
      <property name="mapperLocations" 
      	value="classpath:com/tarena/entity/*.xml"/>
    </bean>
    
 <!-- 配置MyBatis注解 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
       <property name="basePackage" value="com.tarena.dao" />
       <property name="annotationClass" 
           value="com.tarena.annotation.MyBatisRepository"/>
	</bean>
	
	<!-- 开启注解扫描 -->
	<context:component-scan base-package="com.tarena" />

	<!-- 支持@RequestMapping请求和Controller映射 -->
	<mvc:annotation-driven />

	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/"/>
		<property name="suffix" value=".jsp"/>
	</bean>	
	
	<!-- 声明方面组件 -->
	<!-- <bean id="operateLogger" class="com.tarena.aspect.OperateLogger"/> -->
	
	<!-- 配置AOP -->
	<!-- <aop:config>
		<aop:aspect ref="operateLogger">
			<aop:before method="log1" 
				pointcut="within(com.tarena.controller..*)"/>
		</aop:aspect>
		<aop:aspect ref="operateLogger">
			<aop:around method="log2" 
				pointcut="within(com.tarena.controller..*)"/>
		</aop:aspect>
		<aop:aspect ref="operateLogger">
			<aop:after-throwing method="log3" throwing="e"
				pointcut="within(com.tarena.controller..*)"/>
		</aop:aspect>
	</aop:config> -->
	
	<!-- 开启AOP注解扫描 -->
	<aop:aspectj-autoproxy proxy-target-class="true"/>
	
	<!-- 声明事务管理组件 -->
	<bean id="txManager" 
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
		<property name="dataSource" ref="ds"/> 
	</bean>
	
	<!-- 开启事务注解扫描 -->
	<!-- <tx:annotation-driven 
		transaction-manager="txManager" proxy-target-class="true"/> -->
		
	<!-- XML配置声明事务范围及类型 -->
	<tx:advice id="txAdvice" transaction-manager="txManager">
		<tx:attributes>
			<tx:method name="find*" read-only="true" />
			<tx:method name="add*" propagation="REQUIRED" 
				rollback-for="java.lang.ClassNotFoundException"/>
			<tx:method name="update*" propagation="REQUIRED" />
			<tx:method name="delete*" propagation="REQUIRED" />
		</tx:attributes>
	</tx:advice>
	<aop:config proxy-target-class="true">
		<aop:advisor advice-ref="txAdvice" 
			pointcut="within(com.tarena.controller..*)" />
	</aop:config>

</beans>

4 RESTful应用案例

4.1 问题

使用RESTful实现员工模块的增删改查功能。

4.2 方案

在Spring+MyBatis框架基础上实现员工模块的增删改查功能,然后再使用RESTful来重构请求URL。

4.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:实现员工模块增删改查功能

复制项目SpringUnit01,创建新项目SpringRestful,在新项目中完成员工的增删改查功能。

EmpDao代码如下:

package com.tarena.dao;

import java.util.List;
import com.tarena.annotation.MyBatisRepository;
import com.tarena.entity.Emp;

/**
 * 员工表的DAO组件
 */
@MyBatisRepository
public interface EmpDao {

	List<Emp> findAll();
	
	void save(Emp emp);
	
	Emp findById(int id);
	
	void update(Emp emp);
	
	void delete(int id);

}

EmpMapper.xml代码如下:

<?xml version="1.0" encoding="UTF-8" ?>  
<!DOCTYPE mapper PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"      
 "/ibatis.apache.org/dtd/ibatis-3-mapper.dtd">

<mapper namespace="com.tarena.dao.EmpDao">

	<!-- 查询全部的员工 -->
	<select id="findAll" 
		resultType="com.tarena.entity.Emp">
		select * from t_emp
	</select>
	
	<!-- 保存一条员工数据 -->
	<insert id="save"
		parameterType="com.tarena.entity.Emp">
		insert into t_emp values(
			emp_seq.nextval,
			#{ename,jdbcType=VARCHAR},
			#{job,jdbcType=VARCHAR},
			#{mgr,jdbcType=NUMERIC},
			#{hiredate,jdbcType=DATE},
			#{sal,jdbcType=NUMERIC},
			#{comm,jdbcType=NUMERIC},
			#{deptno,jdbcType=NUMERIC}
		)
	</insert>	
	
	<!-- 根据ID查询员工 -->
	<select id="findById"
		parameterType="java.lang.Integer"
		resultType="com.tarena.entity.Emp">
		select * from t_emp where empno=#{id}
	</select>	
	
	<!-- 修改员工 -->
	<update id="update" parameterType="com.tarena.entity.Emp">
		update t_emp set
			ename=#{ename,jdbcType=VARCHAR},
			job=#{job,jdbcType=VARCHAR},
			sal=#{sal,jdbcType=NUMERIC}
		where empno=#{empno,jdbcType=NUMERIC}
	</update>
	
	<!-- 删除员工 -->
	<delete id="delete" parameterType="java.lang.Integer">
		delete from t_emp where empno=#{id}
	</delete>	
	
</mapper>

EmpController代码如下:

package com.tarena.controller;

import java.util.List;
import javax.annotation.Resource;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import com.tarena.dao.EmpDao;
import com.tarena.entity.Emp;

@Controller
@RequestMapping("/emp")
public class EmpController {

	@Resource
	private EmpDao empDao;

	@RequestMapping("/findEmp.do")
	public String find(Model model) {
		List<Emp> list = empDao.findAll();
		model.addAttribute("emps", list);
		return "emp/emp_list";
	}
	
	/**
	 * 打开新增页面
	 */
	@RequestMapping("/toAddEmp.do")
	public String toAdd() {
		return "emp/add_emp";
	}
	
	/**
	 * 新增保存
	 */
	@RequestMapping("/addEmp.do")
	public String add(Emp emp) {
		empDao.save(emp);
		return "redirect:findEmp.do";
	}
	
	/**
	 * 打开修改页面
	 */
	@RequestMapping("/toUpdateEmp.do")
	public String toUpdate(
			@RequestParam("id") int id,
			Model model) {
		Emp e = empDao.findById(id);
		model.addAttribute("emp", e);
		return "emp/update_emp";
	}
	
	/**
	 * 修改保存
	 */
	@RequestMapping("/updateEmp.do")
	public String update(Emp emp) {
		empDao.update(emp);
		return "redirect:findEmp.do";
	}
	
	/**
	 * 删除
	 */
	@RequestMapping("/deleteEmp.do")
	public String delete(@RequestParam("id") int id) {
		empDao.delete(id);
		return "redirect:findEmp.do";
	}

}

员工列表页面emp_list.jsp代码如下:

<%@page pageEncoding="utf-8"%>
<%@taglib uri="/java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<head>
	<script type="text/javascript" src="../js/jquery-1.11.1.js"></script>
	<script type="text/javascript">
		function delete_emp(id) {
			var r = window.confirm("确定要删除此数据吗?");
			if(r) {
				location.href = "deleteEmp.do?id="+id;
			}
		}
	</script>
</head>
<body>
	<div align="center">
		<input type="button" value="新增" onclick="location.href='toAddEmp.do'"/>
	</div>
	<table width="60%" border="1" cellpadding="2" cellspacing="0" align="center">
		<tr>
			<th>EMPNO</th>
			<th>ENAME</th>
			<th>JOB</th>
			<th>MGR</th>
			<th>HIREDATE</th>
			<th>SAL</th>
			<th>COMM</th>
			<th>DEPTNO</th>
			<th></th>
		</tr>
		<c:forEach items="${emps }" var="emp">
			<tr>
				<td>${emp.empno }</td>
				<td>${emp.ename }</td>
				<td>${emp.job }</td>
				<td>${emp.mgr }</td>
				<td>${emp.hiredate }</td>
				<td>${emp.sal }</td>
				<td>${emp.comm }</td>
				<td>${emp.deptno }</td>
				<td>
					<input type="button" value="修改" onclick="location.href='toUpdateEmp.do?id=${emp.empno }'"/>
					<input type="button" value="删除" onclick="delete_emp(${emp.empno });"/>
				</td>
			</tr>
		</c:forEach>
	</table>
</body>
</html>

员工新增页面add_emp.jsp代码如下:

<%@page pageEncoding="utf-8"%>
<%@taglib uri="/java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<head>
</head>
<body>
	<form action="addEmp.do" method="post">
		<table width="40%" border="1" cellpadding="2" cellspacing="0" align="center">
			<tr>
				<td>姓名:</td>
				<td><input type="text" name="ename"/></td>
			</tr>
			<tr>
				<td>岗位:</td>
				<td><input type="text" name="job"/></td>
			</tr>
			<tr>
				<td>工资:</td>
				<td><input type="text" name="sal"/></td>
			</tr>
			<tr>
				<td colspan="2"><input type="submit" value="保存"/></td>
			</tr>
		</table>
	</form>
</body>
</html>

员工修改页面update_emp.jsp代码如下:

<%@page pageEncoding="utf-8"%>
<%@taglib uri="/java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<head>
</head>
<body>
	<form action="updateEmp.do" method="post">
		<table width="40%" border="1" cellpadding="2" cellspacing="0" align="center">
			<tr>
				<td>EMPNO:</td>
				<td><input type="text" name="empno" value="${emp.empno }"/></td>
			</tr>
			<tr>
				<td>姓名:</td>
				<td><input type="text" name="ename" value="${emp.ename }"/></td>
			</tr>
			<tr>
				<td>岗位:</td>
				<td><input type="text" name="job" value="${emp.job }"/></td>
			</tr>
			<tr>
				<td>工资:</td>
				<td><input type="text" name="sal" value="${emp.sal }"/></td>
			</tr>
			<tr>
				<td colspan="2"><input type="submit" value="保存"/></td>
			</tr>
		</table>
	</form>	
</body>
</html>

部署项目并启动tomcat,测试通过员工的增删改查功能。

步骤二:启用RESTful

修改web.xml,将*.do改为/,代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
	xmlns="/java.sun.com/xml/ns/javaee" 
	xmlns:xsi="/www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="/java.sun.com/xml/ns/javaee 
	/java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
  <display-name></display-name>	
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
  	
 <!-- Spring前端控制器 -->
	<servlet>
		<servlet-name>SpringMVC</servlet-name>
		<servlet-class>
			org.springframework.web.servlet.DispatcherServlet
		</servlet-class>
		<init-param>
			<param-name>
				contextConfigLocation
			</param-name>
			<param-value>
				classpath:applicationContext.xml
			</param-value>
		</init-param>
	</servlet>
	<servlet-mapping>
		<servlet-name>SpringMVC</servlet-name>
#cold_bold		<url-pattern>/</url-pattern>
	</servlet-mapping>
	<!-- 使用Filter解决中文乱码问题 -->
	<filter>
	    <filter-name>encodingFilter</filter-name>
	    <filter-class>
	    	org.springframework.web.filter.CharacterEncodingFilter
	    </filter-class>
	    <init-param>
	        <param-name>encoding</param-name>
	        <param-value>UTF-8</param-value>
	    </init-param>
	</filter>
	<filter-mapping>
	    <filter-name>encodingFilter</filter-name>
#cold_bold	    <url-pattern>/*</url-pattern>
	</filter-mapping>  
  
</web-app>

修改applicationContext.xml,增加支持RESTful访问静态资源的配置,关键代码如下:

#cold_bold	<!-- 支持RESTful访问静态资源 -->
#cold_bold	<mvc:default-servlet-handler />

导入Spring中使用Ajax所需的3个开发包,如下图:

图-7

步骤三:使用RESTful重构Controller

修改EmpController,使用RESTful设计访问URI,代码如下:

package com.tarena.controller;

import java.util.List;
import javax.annotation.Resource;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import com.tarena.dao.EmpDao;
import com.tarena.entity.Emp;

@Controller
@RequestMapping("/emp")
public class EmpController {

	@Resource
	private EmpDao empDao;

#cold_bold	@RequestMapping(value="/find",method=RequestMethod.GET)
	public String find(Model model) {
		List<Emp> list = empDao.findAll();
		model.addAttribute("emps", list);
		return "emp/emp_list";
	}
	
	/**
	 * 打开新增页面
	 */
#cold_bold	@RequestMapping(value="/toAdd",method=RequestMethod.GET)
	public String toAdd() {
		return "emp/add_emp";
	}
	
	/**
	 * 新增保存
	 */
#cold_bold	@RequestMapping(value="/add",method=RequestMethod.POST)
	public String add(Emp emp) {
		empDao.save(emp);
#cold_bold		return "redirect:find";
	}	

	/**
	 * 打开修改页面
	 */
#cold_bold	@RequestMapping(value="/toUpdate/{id}",method=RequestMethod.GET)
	public String toUpdate(
#cold_bold			@PathVariable("id") int id,
			Model model) {
		Emp e = empDao.findById(id);
		model.addAttribute("emp", e);
		return "emp/update_emp";
	}
	
	/**
	 * 修改保存
	 */
#cold_bold	@RequestMapping(value="/update",method=RequestMethod.PUT)
#cold_bold	@ResponseBody
#cold_bold	public boolean update(@RequestBody Emp emp) {
#cold_bold		empDao.update(emp);
#cold_bold		return true;
#cold_bold	}
	
	/**
	 * 删除
	 */
#cold_bold	@RequestMapping(value="/{id}",method=RequestMethod.DELETE)
#cold_bold	@ResponseBody
#cold_bold	public boolean delete(@PathVariable("id") int id) {
#cold_bold		empDao.delete(id);
#cold_bold		return true;
#cold_bold	}

}

步骤四:修改页面发请求的方式

在WebRoot/js文件夹下创建json.js,用于将表单数据转换成json对象,代码如下:

// 将表单数据转换成json对象
$.fn.serializeObject = function() {    
   var o = {};    
   var a = this.serializeArray();
   $.each(a, function() {
       if (o[this.name]) {
           if (!o[this.name].push) {
               o[this.name] = [o[this.name]];
           }
           o[this.name].push(this.value || '');
       } else {
           o[this.name] = this.value || '';
       }    
   });
   return o; 
};

在emp_list.jsp中,对新增、修改按钮的URL进行修改,并将删除按钮改为RESTful的删除提交方式,代码如下:

<%@page pageEncoding="utf-8"%>
<%@taglib uri="/java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<head>
	<script type="text/javascript" src="../js/jquery-1.11.1.js"></script>
	<script type="text/javascript">
		function delete_emp(id) {
			var r = window.confirm("确定要删除此数据吗?");
			if(r) {
#cold_bold				//location.href = "deleteEmp.do?id="+id;
#cold_bold				$.ajax({
#cold_bold            		type:"DELETE",
#cold_bold            		url:"/SpringRestful/emp/"+id,
#cold_bold            		dataType:"json",
#cold_bold            		success:function(data){
#cold_bold            			location.href = "/SpringRestful/emp/find";
#cold_bold            		}
#cold_bold	            });  				
			}
		}
	</script>
</head>
<body>
	<div align="center">
#cold_bold		<input type="button" value="新增" onclick="location.href='toAdd'"/>
	</div>
	<table width="60%" border="1" cellpadding="2" cellspacing="0" align="center">
		<tr>
			<th>EMPNO</th>
			<th>ENAME</th>
			<th>JOB</th>
			<th>MGR</th>
			<th>HIREDATE</th>
			<th>SAL</th>
			<th>COMM</th>
			<th>DEPTNO</th>
			<th></th>
		</tr>
		<c:forEach items="${emps }" var="emp">
			<tr>
				<td>${emp.empno }</td>
				<td>${emp.ename }</td>
				<td>${emp.job }</td>
				<td>${emp.mgr }</td>
				<td>${emp.hiredate }</td>
				<td>${emp.sal }</td>
				<td>${emp.comm }</td>
				<td>${emp.deptno }</td>
				<td>
#cold_bold					<input type="button" value="修改" onclick="location.href='toUpdate/${emp.empno }'"/>
#cold_bold					<input type="button" value="删除" onclick="delete_emp(${emp.empno });"/>
				</td>
			</tr>
		</c:forEach>
	</table>
</body>
</html>

修改add_emp.jsp表单的提交路径,代码如下:

<%@page pageEncoding="utf-8"%>
<%@taglib uri="/java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<head>
</head>
<body>
#cold_bold	<form action="add" method="post">
		<table width="40%" border="1" cellpadding="2" cellspacing="0" align="center">
			<tr>
				<td>姓名:</td>
				<td><input type="text" name="ename"/></td>
			</tr>
			<tr>
				<td>岗位:</td>
				<td><input type="text" name="job"/></td>
			</tr>
			<tr>
				<td>工资:</td>
				<td><input type="text" name="sal"/></td>
			</tr>
			<tr>
				<td colspan="2"><input type="submit" value="保存" /></td>
			</tr>
		</table>
	</form>
</body>
</html>

修改update_emp.jsp中表单提交的方式和路径,代码如下:

<%@page pageEncoding="utf-8"%>
<%@taglib uri="/java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<head>
#cold_bold	<script type="text/javascript" src="../../js/jquery-1.11.1.js"></script>
#cold_bold	<script type="text/javascript" src="../../js/json.js"></script>
#cold_bold	<script type="text/javascript">
#cold_bold		function save() {
#cold_bold           	$.ajax({
#cold_bold           		type:"PUT",
#cold_bold           		url:"/SpringRestful/emp/update",
#cold_bold           		data:JSON.stringify($("#myform").serializeObject()),
#cold_bold           		dataType:"json",
#cold_bold           		contentType:"application/json",
#cold_bold           		success:function(data){
#cold_bold           			location.href = "/SpringRestful/emp/find";
#cold_bold           		}
#cold_bold           	});
#cold_bold           } 		
#cold_bold	</script>
</head>
<body>
	<form action="updateEmp.do" method="post" id="myform">
		<table width="40%" border="1" cellpadding="2" cellspacing="0" align="center">
			<tr>
				<td>EMPNO:</td>
				<td><input type="text" name="empno" value="${emp.empno }"/></td>
			</tr>
			<tr>
				<td>姓名:</td>
				<td><input type="text" name="ename" value="${emp.ename }"/></td>
			</tr>
			<tr>
				<td>岗位:</td>
				<td><input type="text" name="job" value="${emp.job }"/></td>
			</tr>
			<tr>
				<td>工资:</td>
				<td><input type="text" name="sal" value="${emp.sal }"/></td>
			</tr>
			<tr>
#cold_bold				<td colspan="2"><input type="button" value="保存" onclick="save();"/></td>
			</tr>
		</table>
	</form>	
</body>
</html>

步骤五:测试

重新部署并启动tomcat,测试通过员工的增删改查功能。

4.4 完整代码

本案例的完整代码如下所示:

EmpDao完整代码如下:

package com.tarena.dao;

import java.util.List;
import com.tarena.annotation.MyBatisRepository;
import com.tarena.entity.Emp;

/**
 * 员工表的DAO组件
 */
@MyBatisRepository
public interface EmpDao {

	List<Emp> findAll();
	
	void save(Emp emp);
	
	Emp findById(int id);
	
	void update(Emp emp);
	
	void delete(int id);

}

EmpMapper.xml完整代码如下:

<?xml version="1.0" encoding="UTF-8" ?>  
<!DOCTYPE mapper PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"      
 "/ibatis.apache.org/dtd/ibatis-3-mapper.dtd">

<mapper namespace="com.tarena.dao.EmpDao">

	<!-- 查询全部的员工 -->
	<select id="findAll" 
		resultType="com.tarena.entity.Emp">
		select * from t_emp
	</select>
	
	<!-- 保存一条员工数据 -->
	<insert id="save"
		parameterType="com.tarena.entity.Emp">
		insert into t_emp values(
			emp_seq.nextval,
			#{ename,jdbcType=VARCHAR},
			#{job,jdbcType=VARCHAR},
			#{mgr,jdbcType=NUMERIC},
			#{hiredate,jdbcType=DATE},
			#{sal,jdbcType=NUMERIC},
			#{comm,jdbcType=NUMERIC},
			#{deptno,jdbcType=NUMERIC}
		)
	</insert>	
	
	<!-- 根据ID查询员工 -->
	<select id="findById"
		parameterType="java.lang.Integer"
		resultType="com.tarena.entity.Emp">
		select * from t_emp where empno=#{id}
	</select>	
	
	<!-- 修改员工 -->
	<update id="update" parameterType="com.tarena.entity.Emp">
		update t_emp set
			ename=#{ename,jdbcType=VARCHAR},
			job=#{job,jdbcType=VARCHAR},
			sal=#{sal,jdbcType=NUMERIC}
		where empno=#{empno,jdbcType=NUMERIC}
	</update>
	
	<!-- 删除员工 -->
	<delete id="delete" parameterType="java.lang.Integer">
		delete from t_emp where empno=#{id}
	</delete>	
	
</mapper>

web.xml完整代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" 
	xmlns="/java.sun.com/xml/ns/javaee" 
	xmlns:xsi="/www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="/java.sun.com/xml/ns/javaee 
	/java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
  <display-name></display-name>	
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
  	
  	<!-- Spring前端控制器 -->
	<servlet>
		<servlet-name>SpringMVC</servlet-name>
		<servlet-class>
			org.springframework.web.servlet.DispatcherServlet
		</servlet-class>
		<init-param>
			<param-name>
				contextConfigLocation
			</param-name>
			<param-value>
				classpath:applicationContext.xml
			</param-value>
		</init-param>
	</servlet>
	<servlet-mapping>
		<servlet-name>SpringMVC</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
	<!-- 使用Filter解决中文乱码问题 -->
	<filter>
	    <filter-name>encodingFilter</filter-name>
	    <filter-class>
	    	org.springframework.web.filter.CharacterEncodingFilter
	    </filter-class>
	    <init-param>
	        <param-name>encoding</param-name>
	        <param-value>UTF-8</param-value>
	    </init-param>
	</filter>
	<filter-mapping>
	    <filter-name>encodingFilter</filter-name>
	    <url-pattern>/*</url-pattern>
	</filter-mapping>  
  
</web-app>

applicationContext.xml完整代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="/www.springframework.org/schema/beans" 
	xmlns:xsi="/www.w3.org/2001/XMLSchema-instance"
	xmlns:context="/www.springframework.org/schema/context" 
	xmlns:jdbc="/www.springframework.org/schema/jdbc"  
	xmlns:jee="/www.springframework.org/schema/jee" 
	xmlns:tx="/www.springframework.org/schema/tx"
	xmlns:aop="/www.springframework.org/schema/aop" 
	xmlns:mvc="/www.springframework.org/schema/mvc"
	xmlns:jpa="/www.springframework.org/schema/data/jpa"
	xsi:schemaLocation="
		/www.springframework.org/schema/beans /www.springframework.org/schema/beans/spring-beans-3.2.xsd
		/www.springframework.org/schema/context /www.springframework.org/schema/context/spring-context-3.2.xsd
		/www.springframework.org/schema/jdbc /www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd
		/www.springframework.org/schema/jee /www.springframework.org/schema/jee/spring-jee-3.2.xsd
		/www.springframework.org/schema/tx /www.springframework.org/schema/tx/spring-tx-3.2.xsd
		/www.springframework.org/schema/data/jpa /www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
		/www.springframework.org/schema/aop /www.springframework.org/schema/aop/spring-aop-3.2.xsd
		/www.springframework.org/schema/mvc /www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">

	<!-- 配置数据源 -->
	<bean id="ds" class="org.apache.commons.dbcp.BasicDataSource">
		<property name="url" value="jdbc:oracle:thin:@localhost:1521:xe" />
		<property name="driverClassName" value="oracle.jdbc.OracleDriver" />
		<property name="username" value="lhh" />
		<property name="password" value="123456" />
	</bean>
	
	<!-- 配置SqlSessionFactory -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="ds" />
		<property name="mapperLocations" value="classpath:com/tarena/entity/*.xml" />
	</bean>
	
	<!-- 配置MyBatis注解 -->
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<property name="basePackage" value="com.tarena.dao" />
		<property name="annotationClass" value="com.tarena.annotation.MyBatisRepository" />
	</bean>
	
	<!-- 开启注解扫描 -->
	<context:component-scan base-package="com.tarena" />
	
	<!-- 开启RequestMapping注解 -->
	<mvc:annotation-driven />
	
	<!-- 处理请求转发 -->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/"/>
		<property name="suffix" value=".jsp"/>
	</bean>	
	
	<!-- 支持RESTful访问静态资源 -->
	<mvc:default-servlet-handler />

</beans>

EmpController完整代码如下:

package com.tarena.controller;

import java.util.List;
import javax.annotation.Resource;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import com.tarena.dao.EmpDao;
import com.tarena.entity.Emp;

@Controller
@RequestMapping("/emp")
public class EmpController {

	@Resource
	private EmpDao empDao;

	@RequestMapping(value="/find",method=RequestMethod.GET)
	public String find(Model model) {
		List<Emp> list = empDao.findAll();
		model.addAttribute("emps", list);
		return "emp/emp_list";
	}
	
	/**
	 * 打开新增页面
	 */
	@RequestMapping(value="/toAdd",method=RequestMethod.GET)
	public String toAdd() {
		return "emp/add_emp";
	}
	
	/**
	 * 新增保存
	 */
	@RequestMapping(value="/add",method=RequestMethod.POST)
	public String add(Emp emp) {
		empDao.save(emp);
		return "redirect:find";
	}
	
	/**
	 * 打开修改页面
	 */
	@RequestMapping(value="/toUpdate/{id}",method=RequestMethod.GET)
	public String toUpdate(
			@PathVariable("id") int id,
			Model model) {
		Emp e = empDao.findById(id);
		model.addAttribute("emp", e);
		return "emp/update_emp";
	}
	
	/**
	 * 修改保存
	 */
	@RequestMapping(value="/update",method=RequestMethod.PUT)
	@ResponseBody
	public boolean update(@RequestBody Emp emp) {
		empDao.update(emp);
		return true;
	}
	
	/**
	 * 删除
	 */
	@RequestMapping(value="/{id}",method=RequestMethod.DELETE)
	@ResponseBody
	public boolean delete(@PathVariable("id") int id) {
		empDao.delete(id);
		return true;
	}

}

json.js完整代码如下:

// 将表单数据转换成json对象
$.fn.serializeObject = function() {    
   var o = {};    
   var a = this.serializeArray();
   $.each(a, function() {
       if (o[this.name]) {
           if (!o[this.name].push) {
               o[this.name] = [o[this.name]];
           }
           o[this.name].push(this.value || '');
       } else {
           o[this.name] = this.value || '';
       }    
   });
   return o; 
};

emp_list.jsp完整代码如下:

<%@page pageEncoding="utf-8"%>
<%@taglib uri="/java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<head>
	<script type="text/javascript" src="../js/jquery-1.11.1.js"></script>
	<script type="text/javascript">
		function delete_emp(id) {
			var r = window.confirm("确定要删除此数据吗?");
			if(r) {
				//location.href = "deleteEmp.do?id="+id;
				$.ajax({
            		type:"DELETE",
            		url:"/SpringRestful/emp/"+id,
            		dataType:"json",
            		success:function(data){
            			location.href = "/SpringRestful/emp/find";
            		}
	            });  				
			}
		}
	</script>
</head>
<body>
	<div align="center">
		<input type="button" value="新增" onclick="location.href='toAdd'"/>
	</div>
	<table width="60%" border="1" cellpadding="2" cellspacing="0" align="center">
		<tr>
			<th>EMPNO</th>
			<th>ENAME</th>
			<th>JOB</th>
			<th>MGR</th>
			<th>HIREDATE</th>
			<th>SAL</th>
			<th>COMM</th>
			<th>DEPTNO</th>
			<th></th>
		</tr>
		<c:forEach items="${emps }" var="emp">
			<tr>
				<td>${emp.empno }</td>
				<td>${emp.ename }</td>
				<td>${emp.job }</td>
				<td>${emp.mgr }</td>
				<td>${emp.hiredate }</td>
				<td>${emp.sal }</td>
				<td>${emp.comm }</td>
				<td>${emp.deptno }</td>
				<td>
					<input type="button" value="修改" onclick="location.href='toUpdate/${emp.empno }'"/>
					<input type="button" value="删除" onclick="delete_emp(${emp.empno });"/>
				</td>
			</tr>
		</c:forEach>
	</table>
</body>
</html>

add_emp.jsp完整代码如下:

<%@page pageEncoding="utf-8"%>
<%@taglib uri="/java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<head>
</head>
<body>
	<form action="add" method="post">
		<table width="40%" border="1" cellpadding="2" cellspacing="0" align="center">
			<tr>
				<td>姓名:</td>
				<td><input type="text" name="ename"/></td>
			</tr>
			<tr>
				<td>岗位:</td>
				<td><input type="text" name="job"/></td>
			</tr>
			<tr>
				<td>工资:</td>
				<td><input type="text" name="sal"/></td>
			</tr>
			<tr>
				<td colspan="2"><input type="submit" value="保存" /></td>
			</tr>
		</table>
	</form>
</body>
</html>

update_emp.jsp完整代码如下:

<%@page pageEncoding="utf-8"%>
<%@taglib uri="/java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<head>
	<script type="text/javascript" src="../../js/jquery-1.11.1.js"></script>
	<script type="text/javascript" src="../../js/json.js"></script>
	<script type="text/javascript">
		function save() {
           	$.ajax({
           		type:"PUT",
           		url:"/SpringRestful/emp/update",
           		data:JSON.stringify($("#myform").serializeObject()),
           		dataType:"json",
           		contentType:"application/json",
           		success:function(data){
           			location.href = "/SpringRestful/emp/find";
           		}
           	});
           } 		
	</script>
</head>
<body>
	<form action="updateEmp.do" method="post" id="myform">
		<table width="40%" border="1" cellpadding="2" cellspacing="0" align="center">
			<tr>
				<td>EMPNO:</td>
				<td><input type="text" name="empno" value="${emp.empno }"/></td>
			</tr>
			<tr>
				<td>姓名:</td>
				<td><input type="text" name="ename" value="${emp.ename }"/></td>
			</tr>
			<tr>
				<td>岗位:</td>
				<td><input type="text" name="job" value="${emp.job }"/></td>
			</tr>
			<tr>
				<td>工资:</td>
				<td><input type="text" name="sal" value="${emp.sal }"/></td>
			</tr>
			<tr>
				<td colspan="2"><input type="button" value="保存" onclick="save();"/></td>
			</tr>
		</table>
	</form>	
</body>
</html>