声明式:针对编程人员,声明spring容器遇到哪些目标方法时需要开启事务,哪些不用开启事务。
事务处理:把事务处理交给spring容器来完成。
spring声明式事务处理的目标:
让程序员从事务处理中脱离开来,交给spring完成,并声明哪些目标方法需要开启事务,哪些不需要。
spring声明式事务处理的步骤:
构建开发环境
导入所需jar包
把dataSource放到spring容器中,并测试是否成功。如果没有成功获取到dataSource说明配置文件有问题
代码实现如下:
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc\:mysql\://localhost\:3306/hibernate1
jdbc.username=root
jdbc.password=root
上面的是放在classpath下的jdbc.properties
下面是spring配置文件的配置:
<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:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<!--
细心的人可以发现,这里多了几行数据,是关于tx的,在下面会讲解到
-->
<!-- ****************读取配置文件信息的固定写法:开始线 **************** -->
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<value>classpath:jdbc.properties</value>
</property>
</bean>
<bean id="dataSource" destroy-method="close"
class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<!-- *****读取配置文件信息的固定写法终结线********************** -->
</beans>
/**
* 测试类
*/
public class test {
//由于所有的测试都在这个类完成,所以把applicationContext设置成静态
public static ApplicationContext applicationContext;
static{
applicationContext=new ClassPathXmlApplicationContext("cn/ansel/config/applicationContext.xml");
}
/**
* 看看是否成功把dataSource放到spring容器中。如果没有成功的话,会报错
*/
@Test
public void testDataSource(){
DataSource dataSource=(DataSource) applicationContext.getBean("dataSource");
System.out.println(dataSource);
}
}
运行结果:
完成dao,service,bean层,并放到spring容器(bean层不用)。
代码实现如下:
/**
* 创建bean层的目的是为了等下需要操作数据库,所以要有一个bean
*/
public class Person implements Serializable {
private Long pid;
private String pname;
private String pdescription;
//省略getter&setter
}
/**dao层
* 这个类需要有2个方法,
* 一个是保存用户
* 另外一个是获取所有的用户
*/
public interface PersonDao {
//保存用户
public void savePerson();
//获取所有的用户
public List<Person> getAllPerson();
}
/**
* dao层的实现。
* 前面我们已经说到,要与jdbc结合的3种方法,在这里我们需要通过jdbc来操作数据库
* 我们就用继承jdbcDaoSupport类来实现
*/
public class PersonDaoImpl extends JdbcDaoSupport implements PersonDao {
/**
* 实现获取所有用户的方法
*/
@Override
public List<Person> getAllPerson() {
//获取到jdbcTemplate类,调用查询方法,直接返回
//这里还要用到rowMapper匿名内部类实现对于person的赋值
return this.getJdbcTemplate().query("select * from person", new RowMapper() {
@Override
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
Person person=new Person();
person.setPdescription(rs.getString("pdescription"));
person.setPid(rs.getLong("pid"));
person.setPname(rs.getString("pname"));
return person;
}
});
}
/**
* 实现保存用户的方法
*/
@Override
public void savePerson() {
//获取到jdbcTemplate类,调用update方法保存
this.getJdbcTemplate().update("insert into person(pname,pdescription) values('ansel','nice')");
}
}
/**
* service层
*这里的方法还是跟dao的方法一致
*/
public interface PersonService {
public void savePerson();
public List<Person> getAllPerson();
}
/**
* service层的实现类
* 这里还需要把personDao引用进来
*/
public class PersonServiceImpl implements PersonService {
//在这里调用了personDao,就还需要利用Di把personDao注入进来,所以要
//设置它的getter&setter方法
private PersonDao personDao;
/**
* 调用dao层来获取所有用户
*/
@Override
public List<Person> getAllPerson() {
return personDao.getAllPerson();
}
/**
* 调用dao层保存用户
*/
@Override
public void savePerson() {
personDao.savePerson();
}
//personDao的getter&setter方法
public PersonDao getPersonDao() {
return personDao;
}
public void setPersonDao(PersonDao personDao) {
this.personDao = personDao;
}
}
把dao和service层配置到spring容器中,配置文件的书写如下:
<!-- *****把dao,service放到spring容器的开始线********************** -->
<!--
因为personDaoImpl继承了jdbcDaoSuppory类,在这个抽象类中
有dataSource的引用,所以在这里需要把dataSource引入
-->
<bean id="personDao" class="cn.ansel.dao.impl.PersonDaoImpl">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
</bean>
<!--
因为在service层引用了personDao,所以在这里要引进来
-->
<bean id="personService" class="cn.ansel.service.impl.PersonServiceImpl">
<property name="personDao">
<ref bean="personDao"/>
</property>
</bean>
<!-- *****把dao,service放到spring容器的终结线********************** -->
测试dao,service层是否成功放到spring容器:
/**
* 测试dao层是否成功放到Spring容器中
* 如果没有成功,我们在获取的时候就会报错
* service层也是一样
*/
@Test
public void testDao(){
applicationContext.getBean("personDao");
}
dao层的运行结果:
//service层的测试:
@Test
public void testService(){
applicationContext.getBean("personService");
}
运行结果:
现在Dao,service层都准备好了,我们就进行aop的配置了。
aop配置(声明式事务处理)
- 我们前面说到了,spring的声明式事务处理 就是让程序员从事务中脱离出来,
- 所以我们在配置config:aop的时候,不用我们自己弄事务,所以此时就不用在中配置事务了,
- 在spring中,有一个最顶级的接口,是处理事务的。叫PlatformTransactionManager,下面是这个接口的视图
-
这个接口有一个继承类:abstractPlatformTransactionManager, 其里面的方法:
在这里省略了方法主体,从这个抽象类的方法我们可以看到,只有getTransaction的方法是抽象的,谁继承这个类,就由谁来实现获取事务的方法。在这里我们使用abstractPlatformTransactionManager的其中一个子类:DataSourceTransactionManager。下面是它的视图
在这里,也引用了dataSource,并且这里实现了getTransaction方法。
配置文件如下:
为了验证我们的配置成功,我们在PersonServiceImpl中设置一个异常
/**
* 调用dao层保存用户
*/
@Override
public void savePerson() {
personDao.savePerson();
//在这里设置一个异常。如果处在同一事务的话,就没有保存成功
//如果保存成功了 就配置错误了。
int a=1/0;
}
/**
* 测试aop配置是否成功
*/
@Test
public void testTransaction(){
PersonService personService=(PersonService) applicationContext.getBean("personService");
personService.savePerson();
}
运行前数据库:
运行结果:
数据库结果:
当我们把异常去掉时,运行结果: