说说在 Spring 中,如何基于注解来配置事务

Spring 提供了基于注解的事务配置,即对须要事务加强的 Bean 接口 、 实现类或者方法进行标注@Transactional,而后在容器中配置基于注解的事务加强驱动,便可使用基于注解的声明式事务 。spring

1 配置事务示例

咱们使用 @Transactional 来为业务类配置事务:bash

@Service
@Transactional
public class UserService {

    @Autowired
    private UserDao userDao;


    /**
     * 新增
     *
     * @param user
     */
    public int addUser(final User user) {
        return userDao.save(user);
    }

    /**
     * 依据 Id,获取帐号
     *
     * @param userId
     * @return
     */
    public User getUser(Long userId) {
        return userDao.get(userId);
    }

    /**
     * 更新帐号所对应的密码
     *  @param userId
     * @param pwd
     */
    public int update(Long userId, String pwd) {
        return userDao.update(userId, pwd);
    }
}
复制代码

接着在 Spring 配置文件中, 告知 Spring 容器对标注了 @Transactional 注解的 Bean,织入事务管理切面:单元测试

<!-- 扫描带 @Transactional 注解的 Bean,织入事务管理切面-->
<tx:annotation-driven transaction-manager="transactionManager"/>
复制代码

在默认状况下, <tx:annotation-driven> 会自动使用名为 transactionManager 的事务管理器, 因此,若是咱们的事务管理器就叫作 transactionManager ,那么就能够进一步简化为测试

<tx:annotation-driven/>
复制代码

<tx:annotation-driven> 拥有如下属性:ui

属性 默认值 说明
transaction-manager transactionManager 事务管理器 Bean ID
proxy-target-class false true 表示将经过建立子类来代理业务类(须要在类路径中添加 CGlib.jar 类库); false 表示使用基于接口来代理 。
order - 若是业务类除了须要事务切面以外,还须要织入其余切面,那么能够经过该属性,来控制事务切面在目标链接点中的织入顺序。

单元测试:spa

public class UserServiceTest {

    ApplicationContext context;

    @BeforeMethod
    public void setUp() throws Exception {
        context = new ClassPathXmlApplicationContext("spring_anno.xml");
    }

    @Test
    public void testAddUser() throws Exception {
        UserService userService = (UserService) context.getBean("userService");
        final User user = new User("deniro");
        userService.addUser(user);
    }
}
复制代码

运行日志:3d

从日志中能够看出,Spring 容器为这个类的全部方法,都织入了事务管理功能。代理

2 @Transactional 属性

@Transactional 拥有如下这些属性:日志

属性 默认值 说明
propagation PROPAGATION_REQUIRED 事务传播行为。可经过org.springframework.transaction.annotation.Propagation枚举类,来提供合法值,例:@Transactional(propagation=Propagation.SUPPORTS)
isolation ISOLATION_DEFAULT 事务隔离级别。可经过 org.springframework.transaction.annotation.Isolation 枚举类,来提供合法值,例:@Transactional(isolation=Isolation.READ_UNCOMMITTED)
readOnly false 是否可读写事务。例:@Transactional(readOnly=true)
timeout 使用底层事务系统的默认值 超时时间,单位为秒。例: @Transactional(timeout=3)
rollbackFor 回滚全部运行期异常。 须要回滚的一组异常类,类型为 Class[], 多个异常类使用逗号分隔。例:@Transactional(rollbackFor={SQLException,class})
rollbackForClassName {} 须要回滚的一组异常类,类型为 String[]。例:@Transactional(rollbackForClassName={“xxxException”})
noRollbackFor {} 不须要回滚的一组异常类,类型为 Class<? extends Throwable>[]
noRolbackForClassName {} 不须要回滚的一组异常类,类型为 String[]。

3 标注位置

@Transactional 注解能够被标注于接口定义、接口方法 、 类定义和类的 Public 方法上 。code

但若是 @Transactional 注解被标注在业务接口上,那么若是启用了子类代理:

<tx:annotation-driven  proxy-target="true"/>
复制代码

那么被代理的业务类并不会织入事务加强,仍然工做在非事务环境下。这显然不是咱们想看到的。

建议在具体业务类上使用 @Transactional 注解,这样无论是否开启子类代理模式,业务类都会织入事务加强。

也能够在直接在方法上定义注解。

方法上定义的注解会覆盖类定义的注解,好比有些方法须要使用到特殊的事务属性,那么就能够直接在方法上定义注解。

在如下示例中,咱们在 getUser() 方法上设置了只读事务属性:

@Service
@Transactional
public class UserService {

    @Autowired
    private UserDao userDao;

    /**
     * 依据 Id,获取帐号
     *
     * @param userId
     * @return
     */
    @Transactional(readOnly = true)
    public User getUser(Long userId) {
        return userDao.get(userId);
    }
    ...
}
复制代码

单元测试:

@Test
    public void testGetUser() throws Exception {
        UserService userService = (UserService) context.getBean("userService");
        User user = userService.getUser(1l);
        logger.info("user={}", user);
    }
复制代码

控制台输出结果:

image.png

从输出结果中咱们能够看出,在调用该方法时,事务加入了只读属性。

相关文章
相关标签/搜索