在没有学习Spring框架之前,通过学习JDBC编程,已经了解到JDBC编程中使用的工具:

数据库连接池有 dbcp:Database connection pool数据库连接池 和 c3p0,两种数据库连接池使用优势各有千秋。

DAO工具有dbUtils,利用dbUtils工具可以简化对数据库的一系列操作。

那么在Spring对JDBC的支持中,使用数据库连接池工具 和 Spring的JdbcTemplate类

spring开发小组推荐使用的数据库连接池工具是dbcp,本文这里是使用的c3p0作为例子。

1、数据库连接池的配置

创建db.properties文件,写入数据库连接池需要的信息

jdbc.user=root
jdbc.password=root
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/test

jdbc.initialPoolSize=20
jdbc.maxPoolSize=40

配置Spring框架的配置文件applicationContext-jdbc.xml

在里边添加 引入外部文件的标签,引入db.propeties

配置连接池的bean,并且使用${}获取外部文件中的信息,配置给数据库连接池

<!-- 引入外部配置文件 -->
	<context:property-placeholder location="classpath:db.properties"/>
	<!-- 配置c3p0数据源 -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="user" value="${jdbc.user}"></property>
		<property name="password" value="${jdbc.password}"></property>
		<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
		<property name="driverClass" value="${jdbc.driverClass}"></property>
		
		
		<property name="initialPoolSize" value="${jdbc.initialPoolSize}"></property>
		<property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
	</bean>

这个时候可以用测试用例,获取IOC中的dataSource实例,看是否已经连接到数据库

@Test
	public void testSpringJdbc(){
		DataSource dataSource = (DataSource) applicationContext.getBean(DataSource.class);
		Connection conn = null;
		try {
			conn = dataSource.getConnection();
			System.out.println(conn);
			
		} catch (SQLException e) {
			e.printStackTrace();
		} finally{
			if(conn != null){
				try {
					conn.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			}
		}
	}


2、JdbcTemplate的使用

JdbcTemplate类是Spring框架中的,其目的是对JDBC的操作进行一层封装,就是DAO思想

使用JdbcTemplate时,需在spring配置文件中,配置该类的bean,并且要设置属性dataSource

<!-- 配置spring的JdbcTemplate -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource"></property>
	</bean>

这样才可以从IOC中获取到JdbcTemplate的实例,并使用。

可以实现对数据库的各种操作:

更新(增删改):使用JdbcTemplate对象的update(String sql, Object... args)方法

批量更新:使用JdbcTemplate对象的batchUpdate(String sql, List<Object[]> batchArgs)方法

查询一条记录并返回一个对象:使用JdbcTemplate对象的queryForObject(String sql, RowMapper<Employee> rowMapper, Object... args)

查询实体类的集合(多条记录返回实体对象的集合):query(String sql, RowMapper<Employee> rowMapper, Object... args)  ,返回List<Employee> 对象

获取单个列的值或者统计查询:queryForObject(String sql, Class<Long> requiredType)

public class JDBCTest {

	private ApplicationContext applicationContext = null;
	private JdbcTemplate jdbcTemplate = null;
	{
		applicationContext = new ClassPathXmlApplicationContext("applicationContext-jdbc.xml");
		jdbcTemplate = (JdbcTemplate) applicationContext.getBean("jdbcTemplate");
	}

	/*
	 * 获取单个列的值,或者统计查询
	 * 使用 JdbcTemplate.queryForObject(String sql, Class<T> requiredType)方法
	 */
	@Test
	public void testQueryForObject2(){
		String sql= "SELECT count(id) FROM employees";
		Long count = jdbcTemplate.queryForObject(sql, Long.class);
		System.out.println(count);
	}
	/*
	 * 查询实体类的集合
	 * 使用JdbcTemplate.query(String sql, RowMapper<Employee> rowMapper, Object... args) 方法
	 * 注意调用的不是queryForList()方法
	 */
	@Test
	public void testQueryForList(){
		String sql="SELECT id, name, email FROM employees WHERE id>?";
		RowMapper<Employee> rowMapper = new BeanPropertyRowMapper<>(Employee.class);
		List<Employee> list = jdbcTemplate.query(sql,rowMapper,4);
		System.out.println(list);
	}
	
	/*
	 * 从数据库读取一条数据,返回对应的对象
	 * 注意不是使用JdbcTemplate.queryForObject(String sql, Class<Employee> requiredType, Object... args)方法
	 * 需要调用JdbcTemplate.queryForObject(String sql, RowMapper<Employee> rowMapper, Object... args)方法
	 * 其中1.RowMapper指定如何去映射结果集的行,常用的实现类是BeanPropertyRowMapper
	 * 2.使用sql的别名完成列名与类的属性名之间的匹配
	 * 3.不支持级联属性,JdbcTemplate到底是JDBC的一个小工具,而不是ORM框架
	 */
	@Test
	public void testQueryForObject(){
		String sql="SELECT id, name, email FROM employees WHERE id=?";
//		Employee employee = jdbcTemplate.queryForObject(sql, Employee.class,5);
		RowMapper<Employee> rowMapper = new BeanPropertyRowMapper<>(Employee.class);
		Employee employee = jdbcTemplate.queryForObject(sql, rowMapper,3);
		System.out.println(employee);
				
	}
	
	//批量更新,使用JdbcTemplate类对象的batchUpdate(String sql, List<Object[]> batchArgs)方法
	@Test
	public void testBatchUpdate(){
		String sql = "INSERT INTO employees(name, email, dept_id) VALUES(?,?,?)";
		List<Object[]> batchArgs= new ArrayList<>();
		batchArgs.add(new Object[]{"jack2","jack2@163.com",5});
		batchArgs.add(new Object[]{"jack3","jack3@163.com",5});
		batchArgs.add(new Object[]{"jack4","jack4@163.com",5});
		jdbcTemplate.batchUpdate(sql, batchArgs);
	}
	//INSERT , DELETE, UPDATE 使用JdbcTemplate类对象的update()方法
	@Test
	public void testUpdate(){
		String sql="INSERT employees VALUES(4,'yuchen','yuchen@163.com',3)";
		jdbcTemplate.update(sql);
		
		sql = "UPDATE employees SET name=? WHERE id=?";
		jdbcTemplate.update(sql, "jack",1);
		
		sql="DELETE FROM employees WHERE id=?";
		jdbcTemplate.update(sql, 1);
	}
}

可以进一步对JdbcTemplate类的方法调用与实际开发中的对象结合:

创建Employee的数据库访问对象EmployeeDAO,通过id获取Employee对象的信息

//使用注解时,记得配置context:component-scan标签
@Repository
public class EmployeeDAO {

	@Autowired
	private JdbcTemplate jdbcTemplate;
	
	public Employee  getEmployeeById(int id){
		String sql = "SELECT id, name, email FROM employees WHERE id=?";
		RowMapper<Employee> rowMapper = new BeanPropertyRowMapper<>(Employee.class);
		if(jdbcTemplate==null){
			System.out.println("null");
		}
		Employee employee = jdbcTemplate.queryForObject(sql, rowMapper,id);
		return employee;
	}
	
	@Test
	public void testGetEmployeeById(){
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext-jdbc.xml");
		//这里的EmployeeDAO对象必须是从IOC容器中获取的,使用new创建的对象,不能得到自动装配的jdbcTemplate
		//使用new创建的对象,jdbcTemplate为空指针
		EmployeeDAO employeeDAO = (EmployeeDAO) applicationContext.getBean("employeeDAO");
		Employee employee = employeeDAO.getEmployeeById(3);
		System.out.println(employee);
	}
}

3、JdbcDaoSupport的使用

JdbcDaoSupport类同JdbcTemplate类一样,都是spring框架对Jdbc编程的支持,不过使用方法不同,JdbcDaoSupport其实是调用的JdbcTemplate类。

使用JdbcDaoSupport对数据库的操作比直接使用JdbcTemplate要繁琐,所以一般使用JdbcTemplate类对象作为属性进数据库操作。

使用JdbcDaoSupport的方法:

创建一个类,继承JdbcDaoSupport父类,(通过注解方式在IOC中生成bean)

新建一个方法,对DataSource属性进行配置,否则出现异常。(通过注解的方法将IOC中的dataSource自动装配到参数)

调用JdbcDaoSupport的getJdbcTemplate方法得到JdbcTemplate对象,调用方法完成操作。

//不推荐使用JdbcDaoSupport,而推荐使用JdbcTemplate作为类的成员变量
//继承JdbcDaoSupport父类,必须配置dataSource属性
@Repository
public class DepartmentDAO extends JdbcDaoSupport{

	//因为父类JdbcDaoSupport的setDataSource()方法是finally,不能覆盖,故曲线配置
	//如果不配置,则会出现异常
	@Autowired//参数自动装配
	public void setDataSource2(DataSource dataSource){
		setDataSource(dataSource);
	}
	
	public Department get(Integer id){
		String sql= "SELECT id,dept_name name FROM department WHERE id = ?";
		RowMapper<Department> rowMapper = new BeanPropertyRowMapper<>(Department.class);
		//可见JdbcDaoSupprot类也是通过JdbcTemplate实现对数据库的操作的
		Department department = getJdbcTemplate().queryForObject(sql, rowMapper,id);
		return department;
	}
	
	@Test
	public void testGet(){
		ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext-jdbc.xml");
		DepartmentDAO departmentDAO = (DepartmentDAO) ctx.getBean("departmentDAO");
		Department department = departmentDAO.get(1);
		System.out.println(department);
	}
	
}

使用注解方式配置bean时,记得在spring配置文件中添加标签:context:component-scan base-package=""

<context:component-scan base-package="com.cqupt.spring.jdbc"></context:component-scan>

4、NamedParameterJdbcTemplate的使用

NamedParameterJdbcTemplate同JdbcTemplate一样,是一个对数据库操作的类,与JdbcTemplate稍有不同。

String sql="INSERT INTO employees VALUES(?,?,?)";当占位符,参数过多时,需要特别注意参数对应的位置,不方便,故引入具名参数的JdbcTemplate。

使用NamedParameterJdbcTemplate的好处是:不需要再对应参数的位置,直接对应参数名。

/*
	 * 使用NamedParameterJdbcTemplate,
	 * 好处:不用再去对应位置,直接对应参数名,便于维护
	 * 缺点:稍微麻烦
	 */
	@Test
	public void testNamedParameterJdbcTemplate(){
//		String sql="INSERT INTO employees VALUES(?,?,?)";
		String sql="INSERT INTO employees(name,email,dept_id) VALUES(:name,:email,:deptId)";
		NamedParameterJdbcTemplate njt = (NamedParameterJdbcTemplate) applicationContext.getBean("namedParameterJdbcTemplate");
		
		//通过Map容器完成操作
		Map<String, Object> paramMap = new HashMap<>();
		paramMap.put("name", "zhangM");
		paramMap.put("email", "zhangM@163.com");
		paramMap.put("deptId", 4);
		njt.update(sql, paramMap);
		
		//通过对象完成操作
		//创建对象,属性赋值,创建SqlParameterSource对象
		//update(String sql, SqlParameterSource paramSource) 
		Employee employee = new Employee();
		employee.setName("wangX");
		employee.setEmail("wangX@163.com");
		employee.setDeptId(5);
		SqlParameterSource paramSource = new BeanPropertySqlParameterSource(employee);
		njt.update(sql, paramSource);
	}

使用对象完成操作的时候,具名参数的名字要和对象的属性名保持一致。

4、事务管理

Spring框架对于事务处理也有应对机制,

spring既支持编程式事务管理,也支持声明式事务管理,声明式事务可以非常简单的完成对事务的操作。

编程式事务管理 ,就是将事务管理的代码嵌入到业务方法中,控制事务的提交和回滚。

声明式事务管理,就是将事务管理的代码从业务中分离出来,以声明的方式控制事务管理。

Connection conn = null;
		
		try{//前置通知:获取连接和禁止自动提交
			conn = JDBCTools.getConnection();
			//禁止自动提交,设置自动提交为false
			conn.setAutoCommit(false);
			
			String sql= "UPDATE users SET balance = balance+500 WHERE username = 'yuchen' ";
			update(conn, sql);
			int a =10/0;
			
			sql= "UPDATE users SET balance = balance-500 WHERE username = 'jack'";
			update(conn,sql);
			
			//提交,相当于返回通知
			conn.commit();
		} catch (Exception e){
			e.printStackTrace();
			try {
				//回滚操作,相当于异常通知
				conn.rollback();
			} catch (SQLException e1) {
				e1.printStackTrace();
			}
			
		} finally{
			JDBCTools.release(null,null, conn);
		}

从事务处理的编程式中,可以看出,事务管理与AOP有很大的相似性,所以,利用spring的AOP机制,支持声明式事务管理。

事务处理不单是针对于JDBC来说的,对于JTA(Java Transaction API),Hibernate都有各自的事务处理。

Spring从不同的事务管理API中,抽象了一整套的事务机制,开发人员不必了解这些事务的底层API,就可以利用这些事务机制。有了这些事务机制,事务管理代码就能独立于特定的事务技术了。

Spring的核心事务管理抽象是:Interface PlatformTransactionManager

对于Jdbc,有DataSourceTransactionManager,在应用程序中,只需处理一个数据源,而且通过JDBC存取

对于JTA,有JtaTransactionManager,在JavaEE应用服务器上用JTA进行事务管理(JTA需详细了解)

对于Hibernate,有HibernateTransactionManager,用Hibernate框架存取数据库(Hibernate需详细了解)

事务管理器以普通Bean形式声明在IOC容器中。

通过一个例子完成对事务处理的详述。

事务准备

一个书店,有图书销售,三个要素:客户的账户、图书的信息、图书的库存。

账户:账户名、余额;

图书:书号,书名,价格;

库存:书号,库存。

首先,建立BookShopDao接口,完成对数据库的各种操作。

public interface BookShopDao {
	
	//根据书号获取书的单价
	public double findBookPriceByIsbn(String isbn);
	
	//更新书的库存,使书号对应的库存-1
	public void updateBookStock(String isbn);
	
	//更新账户的余额,使username的 balance-price
	public void updateUserAccount(String isbn, double price);
}

实现类

@Repository("bookShopDao")
public class BookShopDaoImpl implements BookShopDao {

	@Autowired
	private JdbcTemplate jdbcTemplate;
	
	@Override
	public double findBookPriceByIsbn(String isbn) {
		String sql = "SELECT price FROM book WHERE isbn=?";
		double price = jdbcTemplate.queryForObject(sql, double.class,isbn);
		return price;
	}

	@Override
	public void updateBookStock(String isbn) {
		//检查书的库存是否足够,若不足,抛出异常(手动提供必要的检查,因为mysq不支持检查约束)
		String sqlCheck = "SELECT stock FROM book_stock WHERE isbn = ?";
		int stock = jdbcTemplate.queryForObject(sqlCheck, int.class,isbn);
		if(stock == 0){
			throw new BookStockException("库存不足!");
		}
		
		String sql = "UPDATE book_stock SET stock = stock-1 WHERE isbn=?";
		jdbcTemplate.update(sql, isbn);
	}

	@Override
	public void updateUserAccount(String username, double price) {
		//检查账户的余额是否足够,若不足,抛出异常
		String sqlCheck = "SELECT balance FROM account WHERE username = ?";
		double balance = jdbcTemplate.queryForObject(sqlCheck, double.class,username);
		if(balance -price < 0){
			throw new BookStockException("余额不足!");
		}
		String sql = "UPDATE account SET balance = balance-? WHERE username=?";
		jdbcTemplate.update(sql,price,username);
	}

}

可以通过Junit测试单元,对BookShopDao的各种方法进行验证。

其次,建立对业务的抽象类BookShopService

public interface BookShopService {

	//客户购买图书,完成两个操作:库存减一、账户余额减书的价格
	public void purchase(String username,String isbn);
}

实现类,通过调用BookShopDao的方法

@Repository("bookShopService")
public class BookShopServiceImpl implements BookShopService{

	@Autowired
	private BookShopDao bookShopDao;
	
	
	@Override
	public void purchase(String username, String isbn) {
		//获取书的单价
		double price = bookShopDao.findBookPriceByIsbn(isbn);
		//更新库存
		bookShopDao.updateBookStock(isbn);
		//更新账户余额
		bookShopDao.updateUserAccount(username, price);
	}

}

purchase方法有可能在余额不足时,更新库存,然后发现余额不足的异常,导致数据库中的数据不具一致性。

此时,如何使purchase()方法,成为一个事务,就需要利用spring提供的事务处理了。

声明式事务

使用方法:

1.在spring配置文件中,配置事务管理器

spring中事务管理器是DataSourceTransactionManager类,配置时,必须通过指定构造参数,指明管理的是哪一个数据源

<!-- 配置事务管理器 ,指定管理的数据源-->
	<bean id="transactionManager" 
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<constructor-arg ref="dataSource"></constructor-arg>
	</bean>

2.启用事务注解

引入命名空间tx,通过标签指明事务管理器

<!-- 启用事务注解 -->
	<tx:annotation-driven transaction-manager="transactionManager"/>

3.通过注解@Transactional,将一套操作声明为事务(一个方法)

//添加事务注解
	@Transactional
	@Override
	public void purchase(String username, String isbn) {
		//获取书的单价
		double price = bookShopDao.findBookPriceByIsbn(isbn);
		//更新库存
		bookShopDao.updateBookStock(isbn);
		//更新账户余额
		bookShopDao.updateUserAccount(username, price);
	}

这样就完成了spring的事务管理

5、事务的属性

事务的传播行为

什么是事务的传播行为?方法A是一个事务,方法B也是一个事务,方法A中调用方法B,那就形成了事务的传播行为。

那么此时,方法B是继续使用方法A的事务,还是方法B新开一个自己的事务,把方法A的事务暂时挂起?

这就是事务的传播行为的设置。

设置方法:

在方法B的事务注解中,注明事务的传播行为,有两种:

@Transactional(propagation=Propagation.REQUIRED)方法B在被其他事务方法调用时,无需创建自己的事务,只需使用调用方法的事务即可,这是默认的传播行为。

@Transactional(propagation=Propagation.REQUIRES_NEW)方法B在被其他事务方法调用时,创建自己新的事务,将调用方法的事务暂时挂起。

注解在接口的方法或者实现类的方法上都可以。

用例:

创建Cashier接口,一次购买多本图书,并作检查。实现类CashierImpl会调用BookShopService的方法

public interface Cashier {

	//一次性买多本书
	public void checkout(String username, List<String> isbns);
}

实现类

@Repository("cashier")
public class CashierImpl implements Cashier {

	@Autowired
	private BookShopService bookShopService;
	

	@Transactional
	@Override
	public void checkout(String username, List<String> isbns) {
		for(String isbn : isbns){
			//purchase()也是一个声明式事务
			bookShopService.purchase(username,isbn);
		}
	}

}

其中,checkout方法是一个事务,调用的purchase方法也是一个事务,那么在purchase方法的事务注解中,注明事务的传播行为

//添加事务注解
	//使用propagation指定事务的传播行为,默认的取值为REQUIRED,即使用调用方法的事务
	//另一种常见的传播行为是REQUIRES_NEW,表示注解的方法必须启动一个新事务,并在自己的事务内运行,
	//如果调用方法有事务运行,先挂起
	@Transactional(propagation=Propagation.REQUIRES_NEW)
	@Override
	public void purchase(String username, String isbn) {
		//获取书的单价
		double price = bookShopDao.findBookPriceByIsbn(isbn);
		//更新库存
		bookShopDao.updateBookStock(isbn);
		//更新账户余额
		bookShopDao.updateUserAccount(username, price);
	}

此时,传播行为是REQUIRES_NEW,表示purchase方法会创建自己的事务,当purchase方法异常时,回滚操作在自己的事务内完成,不会影响到checkout的事务,状态回到当前方法purchase被调用之前。

如果,传播行为是REQUIRED,表示purchase方法使用checkout的事务,一单方法purchase出现异常,回滚操作在checkout的事务内完成,状态回到checkout被调用的状态之前。

事务的隔离级别

事务的隔离级别在JDBC的学习中已经详述,可参见。最常用的是”读已提交“READ_COMMITTED

在spring的设置中,像传播属性一样,直接添加:isolation=Isolation.READ_COMMITTED

指定不回滚的异常

事务的回滚就是针对于,在事务中发生异常时,状态回滚到事务之前的状态。

指定不回滚的异常,就是当这些异常发生时,不执行回滚操作,其他未指定的异常发生时,一样需要回滚操作。

直接添加:noRollbackFor={xxxException.class}

只读属性

添加只读属性,是为了更高效的操作数据库。同时读取与更新的事务,底层需要为数据加锁,以达到隔离级别。而声明为只读属性,就不需要这些操作,使得数据读取更加高效。

直接添加:readOnly=true/false

超时属性

为了使该事务不能长期占据资源,当事务一旦超过某个时间限,就必须强制执行回滚操作。

timeout=3 单位是秒

@Transactional(propagation=Propagation.REQUIRES_NEW,//传播行为
			isolation=Isolation.READ_COMMITTED,//隔离级别
			noRollbackFor={AccountBalanceException.class},//指定不回滚的异常
			readOnly=false,//只读属性
			timeout=3//超时属性
			)
	@Override
	public void purchase(String username, String isbn) {
		//获取书的单价
		double price = bookShopDao.findBookPriceByIsbn(isbn);
		//更新库存
		bookShopDao.updateBookStock(isbn);
		//更新账户余额
		bookShopDao.updateUserAccount(username, price);
	}

6、使用XML文件的形式配置事务

在配置DataSource和JdbcTemplate的bean 的前提下,配置自定义类的bean,然后进行配置事务:

1.配置事务管理器的bean

2.配置事务的属性,使用标签tx:advice,并指明属性--关联的事务管理器

3.配置事务切入点,以及把事务切入点aop:pointcut和事务的属性关联起来aop:advisor,并指明关联的事务属性bean

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd">
	
	
	<!-- 引入外部配置文件 -->
	<context:property-placeholder location="classpath:db.properties"/>
	<!-- 配置c3p0数据源 -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="user" value="${jdbc.user}"></property>
		<property name="password" value="${jdbc.password}"></property>
		<property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
		<property name="driverClass" value="${jdbc.driverClass}"></property>
		
		
		<property name="initialPoolSize" value="${jdbc.initialPoolSize}"></property>
		<property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
	</bean>
	
	<!-- 配置spring的JdbcTemplate -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 配置自定义类的bean -->
	<bean id="bookShopDao" class="com.cqupt.spring.tx.xml.BookShopDaoImpl">
		<property name="jdbcTemplate" ref="jdbcTemplate"></property>
	</bean>
	<bean id="bookShopService" class="com.cqupt.spring.tx.xml.service.impl.BookShopServiceImpl">
		<property name="bookShopDao" ref="bookShopDao"></property>
	</bean>
	<bean id="cashier" class="com.cqupt.spring.tx.xml.service.impl.CashierImpl">
		<property name="bookShopService" ref="bookShopService"></property>
	</bean>

	
	<!--1. 配置事务管理器 ,指定管理的数据源-->
	<bean id="transactionManager" 
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<constructor-arg ref="dataSource"></constructor-arg>
	</bean>
	<!-- 2.配置事务属性 -->
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<!-- 必须有tx:method子节点,通过指定方法名,为特定方法的事务配置属性,可以通过*通配符指定多个方法 -->
			<tx:method name="purchase" propagation="REQUIRES_NEW"/>
			<tx:method name="get*" read-only="true"/>
			<tx:method name="find*" read-only="true"/>
			<!-- 如果上边一一指定了需要指定为事务的方法及属性,那么可以不需要name="*",
			这是为了其他需要指定为事务的方法,统一指定为事务,并设置为默认属性  -->
			<tx:method name="*"/>
		</tx:attributes>
	</tx:advice>
	
	<!-- 3.配置事务切入点,以及把事务切入点和事务属性关联起来 -->
	<aop:config>
		<aop:pointcut expression="execution(* com.cqupt.spring.tx.xml.service.impl.*.*(..))" 
				id="txPintcut"/>
		<aop:advisor advice-ref="txAdvice" pointcut-ref="txPintcut"/>
	</aop:config>
	
</beans>