您的位置:首页 > 编程语言 > Java开发

Spring JDBC-使用注解配置声明式事务

2017-09-24 08:51 417 查看
系列

概述

使用Transactional注解
txannotation-driven其他属性

关于Transaction的属性

在何处标注Transactional注解

在方法处使用注解

使用不同的事务管理器

示例

系列

Spring对事务管理的支持概述以及 编程式的事务管理

Spring JDBC-使用XML配置声明式事务

Spring JDBC-使用注解配置声明式事务

概述

除了基于XML的事务配置,Spring还提供了基于注解的事务配置,即通过@Transactional对需要事务增强的Bean接口、实现类或者方法进行标注:在容器中配置基于注解的事务增强驱动,即可以启用基于注解的声明式事务

使用@Transactional注解

我们来对Spring JDBC-使用XML配置声明式事务中的例子使用@Transactional对基于aop/tx命名空间的事务配置进行改造,我们来感受下二者在使用方式上的差异。

@Service
@Transactional // 对业务类进行事务增强的标注
public class TeacherService {

private TeacherDao teacherDao;

public void addTeacher(Teacher teacher) {
teacherDao.addTeacher(teacher);
}

public void updateTeacher(Teacher teacher) {
teacherDao.updateTeacher(teacher);
}

public void getTeacherById(int teacherId) {
teacherDao.getTeacher(teacherId);
}

public void addStudentForTeacher(Teacher teacher, Student student) {
teacher.setStudent(student);
teacherDao.addStudent(student);
}
}


因为注解本身具有一组普适性的默认事务属性,所以往往只需要在需要事务管理的业务类中添加一个@Transactional注解,就完成了业务类事务属性的配置.

当然,注解只是提供元数据,它本身并不能完成事务切面织入的功能,因此,还需要在Spring中配置文件中通过一行小小的配置“通知”Spring容器对标注@Transactional注解的Bean进行加工处理,如下

<!--基于数据源的事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
p:dataSource-ref="dataSource"/>

<!--对标注了@Transactional注解Bean进行加工处理,以织入事务管理切面-->
<tx:annotation-driven transaction-manager="transactionManager"/>


在默认情况下,
<tx:annotation-driven>
会自动使用名为transactionManager事务管理器, 所以,如果我们的事务管理器的id为transactionManager,如上所示,则可以进一步简化为

<tx:annotation-driven/>


<tx:annotation-driven>
其他属性

proxy-target-class: 如果为true ,Spring将通过创建子类来代理业务类,若果为false,则使用基于接口的代理。 如果使用子类代理,,需要在类路径中添加CGlib.jar类库

order:如果业务类除了事务切面外,还要织入其他的切面,则通过该属性可以控制事务切面在目标连接点的织入顺序。

mode: 模式 ,默认为proxy ,可以选择aspectj

关于@Transaction的属性

基于@Transactional注解的配置和基于XML的配置方式一样,它拥有一组普适性很强的默认事务属性,往往可以直接使用这些默认的属性

事务传播行为: PROPAGATION_REQUIRED

事务隔离级别:ISOLATION_DEFAULT

读写事务属性:读/写事务

超时时间:依赖底层的事务属性的默认值

回滚设置:任何运行期异常引发回滚,任何检查型异常不会已发回滚。

因为这些默认设置在大多数情况下是都是适用的,所以一般不需要手工设置事务注解的属性(如下面的表格),当然Spring允许通过手工设定属性值覆盖默认值。

属性名说明
propagation事务传播行为,通过org.springframework.transaction.annotation.Propagation枚举类,提供合法值,比如@Transactional(propagation=Propagation.REQUIRES_NEW)
isolation事务的隔离级别,通过org.springframework.transaction.annotation.Isolation枚举类,提供合法值,比如@Transactional(isolation=Isolation.READ_COMMITED)
readOnly事务读写属性,布尔型,比如 @Transactional(readOnly=true)
timeout超时时间,int型,单位为秒,例如 @Transactional(timeout=10)
rollbackFor一组异常类,遇到时进行回滚,类型为 Class[],比如@Transactional(rollbackFor={SQLException,class})。 多个异常之间使用逗号分隔
rollbackForClassName组异常类,遇到时进行回滚,类型为Strin[],默认值为{},比如@Transactional(rollbackForClassName={“Exception”})
noRollbackFor一组异常类,遇到时不回滚,类型为 Class[],默认值为{}
noRolbackForClassName一组异常类,遇到时不回滚,类型为 String[],默认值为{}

在何处标注@Transactional注解

@Transactional注解可以被应用于接口定义和接口方法、类定义和类的Public方法上。

但是Spring建议在业务的实现类上使用@Transactional注解,当然也可以在业务接口上使用@Transactional注解,但是这样会遗留下一些容易被忽视的隐患, 因为注解不能被继承,所以在业务接口中标注的@Transactional注解不会被业务实现类继承。 如果通过以下配置启用了代理类

<tx:annotation-driven transaction-manager="transactionManager"  proxy-target="true"/>


那么业务类不会添加事务增强,照样工作在非事务环境下。 举个例子,如果使用子类代理,假设用户为 XXX接口标注了@Transaction注解,那么其实现类XXXImpl依旧不会启用事务机制。

因此,Spring建议在具体业务类上使用@Transactional注解,这样不管tx:annotation-driven将proxy-target设置为true还是false,业务类都会启用事务机制

@Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果我们在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。

在方法处使用注解

方法处的注解会覆盖类定义的注解,如果有些方法需要使用特殊的事务属性,则可以在类注解的基础上提供方法注解,比如

@Repository
@Transactional // (1)类级注解,适用于类中所有的public方法
public class TeacherDaoImpl extends BaseDao implements TeacherDao {
private Logger logger = Logger.getLogger(TeacherDaoImpl.class);

private static final String addTeacherSQL = "insert into teacher(id,name,age,sex) values(teacher_id_seq.nextval,?,?,?)";
private static final String queryTeacherByIdSQL = "select name ,age ,sex from teacher where id = ?";

@Transactional(readOnly=true) // (2)提供额外的注解信息,它将覆盖(1)处的类级注解
@Override
public Teacher getTeacher(int teacherId) {

logger.info("TeacherID:" + teacherId);

final Teacher teacher = new Teacher();
jdbcTemplate.query(queryTeacherByIdSQL, new Object[] { teacherId },
new RowCallbackHandler() {
@Override
public void processRow(ResultSet rs) throws SQLException {
teacher.setAge(rs.getInt("age"));
teacher.setName(rs.getString("name"));
teacher.setSex(rs.getString("sex"));
}
});
return teacher;
}
}


(2)处的方法注解提供了readOnly属性,它将覆盖类级注解中默认的readOnly=false设置

使用不同的事务管理器

一般情况下,一个应用仅需要使用一个事务管理器, 如果希望在不同的地方使用不同的事务管理器,则可以通过如下方式实现

配置文件:

<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> 
<!-- 扫描类包,将标注Spring注解的类自动转化Bean,同时完成Bean的注入 -->
<context:component-scan base-package="com.xgj.dao.transaction.multiTxManager" />

<!-- 使用context命名空间,引入数据库的properties文件 -->
<context:property-placeholder location="classpath:spring/jdbc.properties" />

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close"
p:driverClassName="${jdbc.driverClassName}"
p:url="${jdbc.url}"
p:username="${jdbc.username}"
p:password="${jdbc.password}" />

<!-- 配置Jdbc模板 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
p:dataSource-ref="dataSource" />

<!--基于数据源的事务管理器,通过属性引用数据源  可以使用dataSource-ref引用不同的数据源,我们这里只展示同一个-->
<bean id="forumTxManger" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
p:dataSource-ref="dataSource">
<!-- 为事务管理器指定一个名字 -->
<qualifier value="forum"/>
</bean>

<bean id="topicTxManger" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
p:dataSource-ref="dataSource">
<!-- 为事务管理器指定一个名字 -->
<qualifier value="topic"/>
</bean>

<!-- 通知Spring处理注解Bean -->
<tx:annotation-driven/>

</beans>


使用

package com.xgj.dao.transaction.multiTxManager;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class MulitTxServiceWitSpecificName {

// (1)使用名为forum的事务管理器
@Transactional("forum")
public void addForum() {

}

// 使用名为topic的事务管理器
@Transactional("topic")
public void addTopic() {

}
}


在(1)处我们为事务管理器指定了一个数据源,每个事务管理器都可以绑定一个独立的数据源。

在spring配置文件中

<!-- 为事务管理器指定一个名字 -->
<qualifier value="forum"/>


指定了一个可以被@Transactional注解引用的事务管理器的标识。

我们发现在代码中使用 @Transactional(“forum”) 来引用特定的事务管理器,如果很多地方都需要使用,则显得很麻烦,我们可以通过自定义注解进行标识

package com.xgj.dao.transaction.multiTxManager;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.transaction.annotation.Transactional;

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
// 绑定到forum的事务管理器中
@Transactional("forum")
public @interface ForumTransactional {

}


引用的话,调整下代码,如下

package com.xgj.dao.transaction.multiTxManager;

import org.springframework.stereotype.Service;

@Service
public class MulitTxServiceWithSelfDefineAnno {

// 使用名为forum的事务管理器
@ForumTransactional
public void addForum() {

}

// 使用名为topic的事务管理器
@TopicTransactional
public void addTopic() {

}
}


示例

代码已托管到Github—> https://github.com/yangshangwei/SpringMaster
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐