【spring框架】spring使用Annotation进行声明式事务管理

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介:
声明式的事务管理
a)事务加在DAO层还是Service层?

UserService调了UserDao,调了它的save方法,而UserDao它会去访问数据库。
在Dao里面,事务的还是就是save的开始,事务的结束就是save的结束,这是最直观的。也可以用AOP来写,Spring已经给你写好了。
那么事物的边界(beginTransaction()事务开始和事物结束getTransaction().commit())加在哪里比较好呢?
Dao一般都是固定的单一的CRUD,也就是核心的事务操作,像加日志这种事务的准备或者锦上添花的东西我们可以加在Service层。

我们在这里用一个例子说明:
在数据库创建一个日志信息表t_Log,我们准备在save事务之后向数据库添加一个日志信息,来记录User的存储情况。
首先回顾一下UserDao的存储:
User.java:
package cn.edu.hpu.model;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class User {
	private int id;
	private String name;


	@Id
	@GeneratedValue
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}


UserDaoImpl.java:
package cn.edu.hpu.dao.Impl;

import javax.annotation.Resource;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.stereotype.Component;

import cn.edu.hpu.dao.UserDao;
import cn.edu.hpu.model.User;

@Component("u")
public class UserDaoImpl implements UserDao{
	
	private SessionFactory sessionFactory;
	
	public SessionFactory getSessionFactory() {
		return sessionFactory;
	}
	
	@Resource
	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	public void save(User u) {
		Session s=sessionFactory.openSession();
		s.beginTransaction();
		s.save(u);
		s.getTransaction().commit();
		System.out.println("add success!!");		
	}
}

之后我添加了日志:
package cn.edu.hpu.model;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="t_log")
public class Log {
	private int id;
	private String msg;
	
	@Id
	@GeneratedValue
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getMsg() {
		return msg;
	}
	public void setMsg(String msg) {
		this.msg = msg;
	}
	
}

LogDaoImpl.java:
package cn.edu.hpu.dao.Impl;

import javax.annotation.Resource;


import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.stereotype.Component;


import cn.edu.hpu.dao.LogDao;
import cn.edu.hpu.model.Log;


@Component("logDao")
public class LogDaoImpl implements LogDao{
	
	private SessionFactory sessionFactory;
	
	public SessionFactory getSessionFactory() {
		return sessionFactory;
	}
	
	@Resource
	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}


	public void save(Log log) {
		Session s=sessionFactory.openSession();
		s.beginTransaction();
		s.save(log);
		s.getTransaction().commit();
		System.out.println("Log add success!!");		
	}
}

别忘记在配置文件beans.xml中加Log实体类<value>cn.edu.hpu.model.Log</value>:
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    	<property name="annotatedClasses">
      		<list>
        	<value>cn.edu.hpu.model.User</value>
        	<value>cn.edu.hpu.model.Log</value>
     		</list>
    	</property>
    	<property name="hibernateProperties">
			<props>
		   		<prop key="hibernate.dialect">
                    org.hibernate.dialect.MySQLDialect
                </prop>
		   		<prop key="hibernate.show_sql">true</prop>
			</props>
    	</property>
  	</bean>


现在我们需要将日志添加事务放在存储User的后面,是放在Dao里面好还是Service里面好呢?按照前面说的,加载Service里面比较合适:
package cn.edu.hpu.service;

import javax.annotation.Resource;

import org.springframework.stereotype.Component;

import cn.edu.hpu.dao.LogDao;
import cn.edu.hpu.dao.UserDao;
import cn.edu.hpu.model.Log;
import cn.edu.hpu.model.User;

@Component("userService")
public class UserService {


	private UserDao userDao;
	private LogDao logDao;
	
	public UserDao getUserDao() {
		return userDao;
	}
	
	@Resource(name="u")
	public void setUserDao(UserDao userDao) {
		this.userDao = userDao;
	}


	public LogDao getLogDao() {
		return logDao;
	}
	
	@Resource(name="logDao")
	public void setLogDao(LogDao logDao) {
		this.logDao = logDao;
	}


	public void add(User u){
		this.userDao.save(u);
		Log log=new Log();
		log.setMsg("a user saved!");
		logDao.save(log);
	}
}

测试:
package cn.edu.hpu.service;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


import cn.edu.hpu.dao.UserDao;
import cn.edu.hpu.model.User;


public class UserServiceTest {
	
	@Test
	public void testAdd() throws Exception{
		ClassPathXmlApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml");
		
		UserService userService=(UserService)ctx.getBean("userService");
		System.out.println(userService.getClass());
		User u=new User();
		u.setName("jack");
		userService.add(u);
		ctx.destroy();
	}
}

测试结果:
class cn.edu.hpu.service.UserService
Hibernate: insert into User (name) values (?)
add success!!
Hibernate: insert into t_log (msg) values (?)
Log add success!!

Log日志添加成功!

这都是我们是手动添加的事务开始于结束(s.beginTransaction();与s.getTransaction().commit();),那么如何用Spring的机制来自动添加事务呢?下面先说一下Annotation的实现:


b)annotation
i.加入annotation.xsd(关于tx的)
<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: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/context
           http://www.springframework.org/schema/context/spring-context-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">

ii.加入txManager bean
因为你结合的是hibernate,所以事务管理也是使用hibernate的事务管理。
所用类org.springframework.orm.hibernate3.HibernateTransactionManager
HibernateTransactionManager就是在方法之前加点东西,方法之后加点东西,也就是是一个Aspect切面类,应用了AOP。你让它管理事务,必须要告诉他数据库的连接是谁,采取的方式是将sessionFactory注进去:
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory">
</bean>

iii.<tx:annotation-driven
<tx:annotation-driven transaction-manager="txManager"/>


iv.在需要事务的方法上加:@Transactional


例如你需要在add方法上有事务,那就在add方法上加@Transactional
@Transactional
public void add(User u){
	this.userDao.save(u);
	Log log=new Log();
	log.setMsg("a user saved!");
	this.logDao.save(log);
}
它会自动在前后帮你加上关于业务的事务逻辑

这个时候User的save和Log的Save里面的s.beginTransaction();与s.getTransaction().commit();就不用写了
public void save(User u) {
		Session s=sessionFactory.getCurrentSession();
		s.save(u);
		System.out.println("add success!!");		
	}
public void save(Log log) {
		Session s=sessionFactory.openSession();
		s.save(log);
		System.out.println("Log add success!!");		
	}
v.需要注意,使用SessionFactory.getCurrentSession 不要使用OpenSession
测试:
package cn.edu.hpu.service;
import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;


import cn.edu.hpu.dao.UserDao;
import cn.edu.hpu.model.User;


public class UserServiceTest {
	
	@Test
	public void testAdd() throws Exception{
		ClassPathXmlApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml");
		
		UserService userService=(UserService)ctx.getBean("userService");
		User u=new User();
		u.setName("jack");
		userService.add(u);
		ctx.destroy();
	}
}
测试结果:
Hibernate: insert into User (name) values (?)
add success!!
Hibernate: insert into t_log (msg) values (?)
Log add success!!


结果存储成功,说明@Transactional注解起作用了。而且如果@Transactional注解的方法的事务Runtime时出了问题,会自动回滚,测试:
在Log的save后面加一个RuntimeException异常:
public void save(Log log) {
	Session s=sessionFactory.openSession();
	s.save(log);
	throw new RuntimeException("Error");	
}

测试:
@Test
	public void testAdd() throws Exception{
		ClassPathXmlApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml");
		
		UserService userService=(UserService)ctx.getBean("userService");
		User u=new User();
		u.setName("jack");
		userService.add(u);
		ctx.destroy();
	}
结果:
Hibernate: insert into User (name) values (?)
add success!!
Hibernate: insert into t_log (msg) values (?)
没有将Log添加进去,说明事务回滚了。这是spring对于hibernate事务的管理。通常情况下add方法里面都要加try-catch包围的,有了上述管理,就不用try-catch了。

3.@Transactional 有关的设置
@Transactional 注解是用来指定接口、类或方法必须拥有事务语义的元数据。 如:“当一个方法开始调用时就开启一个新的只读事务,并停止掉任何现存的事务”。 默认的 @Transactional 设置如下: 
事务传播设置是 PROPAGATION_REQUIRED
事务隔离级别是 ISOLATION_DEFAULT
事务是 读/写
事务超时默认是依赖于事务系统的,或者事务超时没有被支持。
任何 RuntimeException 将触发事务回滚,但是任何 checked Exception 将不触发事务回滚
这些默认的设置当然也是可以被改变的。 @Transactional 注解的各种属性设置总结如下: 

表 9.3. @Transactional 注解的属性

属性 类型 描述 
propagation  枚举型:Propagation  可选的传播性设置 

isolation 枚举型:Isolation  可选的隔离性级别(默认值:ISOLATION_DEFAULT) 

readOnly 布尔型 读写型事务 vs. 只读型事务 

timeout int型(以秒为单位) 事务超时 

rollbackFor 一组 Class 类的实例,必须是Throwable的子类一组异常类,遇到时必须进行回滚。默认情况下checked exceptions不进行回滚,仅unchecked exceptions(即RuntimeException的子类)才进行事务回滚。 

rollbackForClassname 一组Class类的名字,必须是Throwable的子类 一组异常类名,遇到时 必须进行回滚 

noRollbackFor 一组 Class 类的实例,必须是Throwable的子类一组异常类,遇到时必须不回滚。 

noRollbackForClassname 一组Class类的名字,必须是Throwable 的子类 一组异常类,遇到时 必须不回滚 

在写代码的时候,不可能对事务的名字有个很清晰的认识,这里的名字是指会在事务监视器(比如WebLogic的事务管理器)或者日志输出中显示的名字, 对于声明式的事务设置,事务名字总是全限定名+"."+事务通知的类的方法名。比如BusinessService类的handlePayment(..)方法启动了一个事务,事务的名称是:
com.foo.BusinessService.handlePayment

最常用的是@Transactional(propagation=Propagation.REQUIRED) 
是propagation一堆属性中唯一一个需要记住的。

意思是在当前的这个环境中,别人调我这个方法,比如说已经有了一个Transaction了,就用原来那个,把自己作为Transaction的一部分加进去就行了,如果没有就创建一个新的。

转载请注明出处:http://blog.csdn.net/acmman/article/details/44651921

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
1月前
|
前端开发 Java 数据库连接
Spring框架初识
Spring 是一个分层的轻量级开源框架,核心功能包括控制反转(IOC)和面向切面编程(AOP)。主要模块有核心容器、Spring 上下文、AOP、DAO、ORM、Web 模块和 MVC 框架。它通过 IOC 将配置与代码分离,简化开发;AOP 提供了声明性事务管理等增强功能。
83 21
Spring框架初识
|
9天前
|
前端开发 Java 数据库连接
Spring MVC 扩展和SSM框架整合
通过以上步骤,我们可以将Spring MVC扩展并整合到SSM框架中。这个过程包括配置Spring MVC和Spring的核心配置文件,创建控制器、服务层和MyBatis的Mapper接口及映射文件。在实际开发中,可以根据具体业务需求进行进一步的扩展和优化,以构建更加灵活和高效的企业级应用程序。
22 5
|
22天前
|
存储 人工智能 开发框架
Spring AI Alibaba 应用框架挑战赛圆满落幕,恭喜获奖选手
第二届开放原子大赛 Spring AI Alibaba 应用框架挑战赛决赛于 2 月 23 日在北京圆满落幕。
|
1月前
|
XML Java 开发者
通过springboot框架创建对象(一)
在Spring Boot中,对象创建依赖于Spring框架的核心特性——控制反转(IoC)和依赖注入(DI)。IoC将对象的创建和管理交由Spring应用上下文负责,开发者只需定义依赖关系。DI通过构造函数、setter方法或字段注入实现依赖对象的传递。Spring Boot的自动配置机制基于类路径和配置文件,自动为应用程序配置Spring容器,简化开发过程。Bean的生命周期包括定义扫描、实例化、依赖注入、初始化和销毁回调,均由Spring容器管理。这些特性提高了开发效率并简化了代码维护。
|
14天前
|
存储 监控 数据可视化
SaaS云计算技术的智慧工地源码,基于Java+Spring Cloud框架开发
智慧工地源码基于微服务+Java+Spring Cloud +UniApp +MySql架构,利用传感器、监控摄像头、AI、大数据等技术,实现施工现场的实时监测、数据分析与智能决策。平台涵盖人员、车辆、视频监控、施工质量、设备、环境和能耗管理七大维度,提供可视化管理、智能化报警、移动智能办公及分布计算存储等功能,全面提升工地的安全性、效率和质量。
|
存储 SQL Java
Spring事务管理的底层逻辑—源码解析
首先进入CglibAopProxy.class的intercept方法打上一个Debug断点调试,或者在JdkDynamicAopProxy.class的invoke方法(如果目标方法是继承接口方式实现),根据不同实现方法类型选择不同的动态代理类
152 0
Spring事务管理的底层逻辑—源码解析
|
XML Java 数据格式
【spring源码学习】spring的事务管理的源码解析
【一】spring事务管理(1)spring的事务管理,是基于aop动态代理实现的。对目标对象生成代理对象,加入事务管理的核心拦截器==>org.springframework.transaction.interceptor.TransactionInterceptor。
2111 0
|
5天前
|
缓存 NoSQL Java
基于SpringBoot的Redis开发实战教程
Redis在Spring Boot中的应用非常广泛,其高性能和灵活性使其成为构建高效分布式系统的理想选择。通过深入理解本文的内容,您可以更好地利用Redis的特性,为应用程序提供高效的缓存和消息处理能力。
110 79
|
27天前
|
Java 数据库 开发者
详细介绍SpringBoot启动流程及配置类解析原理
通过对 Spring Boot 启动流程及配置类解析原理的深入分析,我们可以看到 Spring Boot 在启动时的灵活性和可扩展性。理解这些机制不仅有助于开发者更好地使用 Spring Boot 进行应用开发,还能够在面对问题时,迅速定位和解决问题。希望本文能为您在 Spring Boot 开发过程中提供有效的指导和帮助。
77 12
|
1月前
|
Java 应用服务中间件 Maven
SpringBoot项目打包成war包
通过上述步骤,我们成功地将一个Spring Boot应用打包成WAR文件,并部署到外部的Tomcat服务器中。这种方式适用于需要与传统Servlet容器集成的场景。
45 8