spring注解事务

一、基础配置

(1)配置文件:使用spring注解方式的配置方式

db.xml

    <bean id="txManager"
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

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

(2)PO 测试表结构:

School.java

@Entity
@Table(name = "SCHOOL")
public class School {
	@Id
	@GenericGenerator(name = "generator", strategy = "increment")
	@GeneratedValue(generator = "generator")
	@Column(name = "id", unique = true, nullable = false)
	private Integer id;
	@Column(name = "NAME")
	private String name;
	@Column(name = "ADDRESS")
	private String address;

	public School(String name, String address) {
		this.name = name;
		this.address = address;
	}

	..........
}

Student.java

@Entity
@Table(name = "STUDENT")
public class Student {
	@Id
	@GenericGenerator(name = "generator", strategy = "increment")
	@GeneratedValue(generator = "generator")
	@Column(name = "id", unique = true, nullable = false)
	private Integer id;
	@Column(name = "NAME")
	private String name;
	@Column(name = "AGE")
	private Integer age;
	@ManyToOne(targetEntity = School.class)
	@JoinColumn(name = "SCHOOL_ID")
	private School school;
	@Temporal(TemporalType.TIMESTAMP)
	@Column(name = "LOGIN_DATE")
	private Date loginDate;

	public Student(String name, int age, School school) {
		this.name = name;
		this.age = age;
		this.school = school;
	}
	......
}

Student  多<———>一关系

(3)dao:StudentDAO,SchoolDAO

(4)service:StudentSchoolService,向其中注入StudentDAO,SchoolDAO

二、事务传播机制 测试代码

PROPAGATION_REQUIRED支持当前事务(即合并为一个事务),如果当前没有事务,就新建一个事务。这是最常见的选择。
PROPAGATION_SUPPORTS支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER 以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。


(1)在service上生命@Transactional,该类的所有方法将都受到事务控制,默认是:Propagation.REQUIRED

org.springframework.transaction.annotation.Transactional

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {

	/**
	 * A qualifier value for the specified transaction.
	 * <p>May be used to determine the target transaction manager,
	 * matching the qualifier value (or the bean name) of a specific
	 * {@link org.springframework.transaction.PlatformTransactionManager}
	 * bean definition.
	 */
	String value() default "";

	/**
	 * The transaction propagation type.
	 * Defaults to {@link Propagation#REQUIRED}.
	 * @see org.springframework.transaction.interceptor.TransactionAttribute#getPropagationBehavior()
	 */
	Propagation propagation() default Propagation.REQUIRED;

(2)@Transactional(propagation=Propagation.NEVER):以非事务方式执行,如果当前存在事务,则抛出异常。

	@Transactional(propagation=Propagation.NEVER)
	public void methodNever() {
		System.out.println("============ BEGIN methodNever============");
		this.schoolDAO.save(new School("11", "11"));
		System.out.println("============ SPLIT TOW METHOD ============");
		this.schoolDAO.save(new School("22", "22"));
		System.out.println("============ END methodNever============");
	}
输出:

============ BEGIN methodNever============
Hibernate: select max(id) from SCHOOL
============ SPLIT TOW METHOD ============
============ END methodNever============
Hibernate: insert into SCHOOL (ADDRESS, NAME, id) values (?, ?, ?)
Hibernate: insert into SCHOOL (ADDRESS, NAME, id) values (?, ?, ?)
<1>当在单方法上声明时,方法仍被事务控制,如上图输出。
        @Transactional(propagation=Propagation.REQUIRED)
	public void methodNeverInnerREQUIREDSameBean() {
		methodNever();
	}
<2>如果在service内部,在一个REQUIRED方法内包含一个NEVER方法,声明为NEVER的方法会正常在外部事务范围内执行。

<3>如果在serviceA的REQUIRED方法调用serviceB的NEVER方法,NEVER方法会抛出异常。对NEVER事务抛出异常的描述指的是在嵌套的情况下

serviceB:public class SchoolService

@Transactional(propagation = Propagation.NEVER)
    public void methodNever() {
        this.schoolDAO.save(new School("11", "11"));
    }
serviceA:public class StudentSchoolService

@Transactional(propagation=Propagation.REQUIRED)
	public void methodNeverInnerREQUIREDDifBean() {
		schoolService.methodNever();
	}
执行serviceA.methodNeverInnerREQUIREDDifBean()方法,抛出异常:

Exception in thread "main" org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never'
如果在methodNeverInnerREQUIREDDifBean方法中调用NEVER方法之前有数据提交,也将被回滚,如:

	@Transactional(propagation=Propagation.REQUIRED)
	public void methodNeverInnerREQUIREDDifBean() {
		this.schoolDAO.save(new School("11", "11"));	// 会回滚
		schoolService.methodNever();
	}
(2) PROPAGATION_NOT_SUPPORTED以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
在serviceA的REQUIRED方法调用serviceB的NOT_SUPPORTED方法:

当执行到serviceB的NOT_SUPPORTED方法时,serviceA的REQUIRED方法事务将会被挂起,先进行NOT_SUPPORTED方法的执行

serviceB:public class SchoolService

@Transactional(propagation = Propagation.NOT_SUPPORTED)
	public void methodNotSupported() {
		this.schoolDAO.save(new School("11", "11"));
	}
serviceA:public class StudentSchoolService

	@Transactional(propagation=Propagation.REQUIRED)
	public void methodNotSupportedInnerREQUIREDDifBean() {
		System.out.println("============ BEGIN methodNotSupport ============");
		this.schoolDAO.save(new School("11", "11"));
		System.out.println("============ finish required save ============");
		schoolService.methodNotSupported();
		System.out.println("============ finish not supported save ============");
		System.out.println("============ END methodNotSupport ============");
	}
输出:

============ BEGIN methodNotSupport ============
Hibernate: select max(id) from SCHOOL
============ finish required save ============
Hibernate: insert into SCHOOL (ADDRESS, NAME, id) values (?, ?, ?)    ------serviceB的NOT_SUPPORTED方法
============ finish not supported save ============
============ END methodNotSupport ============
Hibernate: insert into SCHOOL (ADDRESS, NAME, id) values (?, ?, ?)    ------serviceA的required方法
(3) PROPAGATION_SUPPORTS支持当前事务,如果当前没有事务,就以非事务方式执行

在serviceA的REQUIRED方法调用serviceB的SUPPORTS方法:supports方法中的操作也会包含在父方法的事务内

serviceB:public class SchoolService

	@Transactional(propagation = Propagation.SUPPORTS)
	public void methodSupports() {
		this.schoolDAO.save(new School("11", "11"));
	}
serviceA:public class StudentSchoolService

	@Transactional(propagation=Propagation.REQUIRED)
	public void methodSupportsInnerREQUIREDDifBean() {
		System.out.println("============ BEGIN methodSupports ============");
		this.schoolDAO.save(new School("11", "11"));
		schoolService.methodSupports();
		System.out.println("============ END methodSupports ============");
	}
输出:

============ BEGIN methodSupports ============
Hibernate: select max(id) from SCHOOL
============ END methodSupports ============
Hibernate: insert into SCHOOL (ADDRESS, NAME, id) values (?, ?, ?)
Hibernate: insert into SCHOOL (ADDRESS, NAME, id) values (?, ?, ?)

如果将serviceA:public class StudentSchoolService中的REQUIRED方法去掉事务的配置

整提操作将都不存在事务,数据库操作按顺序执行,输出:

============ BEGIN methodSupports ============
Hibernate: select max(id) from SCHOOL
Hibernate: insert into SCHOOL (ADDRESS, NAME, id) values (?, ?, ?)
Hibernate: insert into SCHOOL (ADDRESS, NAME, id) values (?, ?, ?)
============ END methodSupports ============
(4) PROPAGATION_MANDATORY支持当前事务,如果当前没有事务,就抛出异常

在serviceA的无事务配置方法调用serviceB的MANDATORY方法:执行到serviceB的MANDATORY方法时将抛出异常,在serviceA中调用serviceB.MANDATORY方法之前的数据库操作会正常提交(因为没在事务控制范围内)。

serviceB:public class SchoolService

	@Transactional(propagation = Propagation.MANDATORY)
	public void methodMandatory() {
		this.schoolDAO.save(new School("11", "11"));
	}
serviceA:public class StudentSchoolService

public void methodMandatoryInnerNoTransactionDifBean() {
		System.out.println("============ BEGIN methodMandatory ============");
		this.schoolDAO.save(new School("11", "11"));	// 正常save
		schoolService.methodMandatory();	// 抛出异常
		System.out.println("============ END methodMandatory ============");
	}
输出:

============ BEGIN methodMandatory ============
Hibernate: select max(id) from SCHOOL
Hibernate: insert into SCHOOL (ADDRESS, NAME, id) values (?, ?, ?)
Exception in thread "main" org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory'

(5)在事务内部新起线程执行的情况

例:

@Transactional(propagation = Propagation.REQUIRED)
	public void methodRequiredNewThread() {
		System.out.println("============ BEGIN methodRequiredNewThread ============");
		this.schoolDAO.save(new School("11", "11"));	// 在方法的事务内
		new Thread(new Runnable() {
			public void run() {
				System.out.println("save in thread");
				schoolDAO.save(new School("22", "22"));	// 不在事务内
			}
		}).start();
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("============ END methodRequiredNewThread ============");
	}
输出:

============ BEGIN methodRequiredNewThread ============
Hibernate: select max(id) from SCHOOL
save in thread
Hibernate: insert into SCHOOL (ADDRESS, NAME, id) values (?, ?, ?)
============ END methodRequiredNewThread ============
Hibernate: insert into SCHOOL (ADDRESS, NAME, id) values (?, ?, ?)
配置了事务的方法内部,新起线程执行的数据库操作,不在外部的方法事务内。如果内部线程中使用配置了配置事务的方法,该方法使用新事务。

spring事务,根据每个线程分配一个session。所在内部起新线程,就另起了一个session,和新事务。

(6)PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW的嵌套

两个PROPAGATION_REQUIRED事务方法嵌套,由于两个方法合并成一个事务,所以不论外部或内部的方法抛出异常,都将回滚。

外部方法:PROPAGATION_REQUIRED,内部方法:PROPAGATION_REQUIRES_NEW

<1>外部方法REQUIRED抛出异常(所有数据库操作方法之后,包括嵌套字方法):内部REQUIRES_NEW事务方法可正常提交

<2>内部方法REQUIRES_NEW抛出异常:所有操作都将回滚


PROPAGATION_REQUIREDPROPAGATION_REQUIRES_NEWPROPAGATION_NESTED的区别

嵌套抛出异常的情况



三、@Transactional(readOnly = true)

public @interface Transactional:

/**
	 * {@code true} if the transaction is read-only.
	 * Defaults to {@code false}.
	 * <p>This just serves as a hint for the actual transaction subsystem;
	 * it will <i>not necessarily</i> cause failure of write access attempts.
	 * A transaction manager which cannot interpret the read-only hint will
	 * <i>not</i> throw an exception when asked for a read-only transaction.
	 * @see org.springframework.transaction.interceptor.TransactionAttribute#isReadOnly()
	 */
	boolean readOnly() default false;

只读型事务

默认为false,该注解只是给事务系统一个提示,并不一定会导致写操作失败。看其他文章描述,声明为只读事务,spring会对性能方面进行优化。

例子:在声明为readOnly事务内执行数据库save操作,抛出异常

	@Transactional(readOnly = true)
	public void methodReadOnly() {
		this.schoolDAO.save(new School("11", "11"));
	}
输出:

Exception in thread "main" org.springframework.dao.InvalidDataAccessApiUsageException: Write operations are not allowed in read-only mode (FlushMode.MANUAL): Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction definition.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值