在没有学习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>