Spring transaction isolation level

Introduction

Transaction isolation level is a concept that is not exclusive to the Spring framework. It is applied to transactions in general and is directly related with the ACID transaction properties. Isolation level defines how the changes made to some data repository by one transaction affect other simultaneous concurrent transactions, and also how and when that changed data becomes available to other transactions. When we define a transaction using the Spring framework we are also able to configure in which isolation level that same transaction will be executed.

Usage example

Using the @Transactional annotation we can define the isolation level of a Spring managed bean transactional method. This means that the transaction in which this method is executed will run with that isolation level:

Isolation level in a transactional method
@Autowired
private TestDAO testDAO;

@Transactional(isolation=Isolation.READ_COMMITTED)
public void someTransactionalMethod(User user) {

  // Interact with testDAO

}

We are defining this method to be executed in a transaction which isolation level is READ_COMMITTED. We will see each isolation level in detail in the next sections.

READ_UNCOMMITTED

READ_UNCOMMITTED isolation level states that a transaction may read data that is still uncommitted by other transactions. This constraint is very relaxed in what matters to transactional concurrency but it may lead to some issues like dirty reads. Let’s see the following image:

Dirty read
Transaction isolation level dirty read

In this example Transaction A writes a record. Meanwhile Transaction B reads that same record before Transaction A commits. Later Transaction A decides to rollback and now we have changes in Transaction B that are inconsistent. This is a dirty read.Transaction B was running in READ_UNCOMMITTED isolation level so it was able to read Transaction A changes before a commit occurred.

Note: READ_UNCOMMITTED is also vulnerable to non-repeatable reads and phantom reads. We will also see these cases in detail in the next sections.

READ_COMMITTED

READ_COMMITTED isolation level states that a transaction can’t read data that is not yet committed by other transactions. This means that the dirty read is no longer an issue, but even this way other issues may occur. Let’s see the following image:

Non-repeatable read
Transaction isolation level repeatable read

In this example Transaction A reads some record. Then Transaction B writes that same record and commits. Later Transaction Areads that same record again and may get different values because Transaction B made changes to that record and committed. This is a non-repeatable read.

Note: READ_COMMITTED is also vulnerable to phantom reads. We will also see this case in detail in the next section.

REPEATABLE_READ

REPEATABLE_READ isolation level states that if a transaction reads one record from the database multiple times the result of all those reading operations must always be the same. This eliminates both the dirty read and the non-repeatable read issues, but even this way other issues may occur. Let’s see the following image:

Phantom read
Transaction isolation level phantom read

In this example Transaction A reads a range of records. Meanwhile Transaction B inserts a new record in the same range thatTransaction A initially fetched and commits. Later Transaction A reads the same range again and will also get the record thatTransaction B just inserted. This is a phantom read: a transaction fetched a range of records multiple times from the database and obtained different result sets (containing phantom records).

SERIALIZABLE

SERIALIZABLE isolation level is the most restrictive of all isolation levels. Transactions are executed with locking at all levels (read, range and write locking) so they appear as if they were executed in a serialized way. This leads to a scenario where noneof the issues mentioned above may occur, but in the other way we don’t allow transaction concurrency and consequently introduce a performance penalty.

DEFAULT

DEFAULT isolation level, as the name states, uses the default isolation level of the datastore we are actually connecting from our application.

Summary

To summarize, the existing relationship between isolation level and read phenomena may be expressed in the following table:

dirty reads non-repeatable reads phantom reads
READ_UNCOMMITTED yes yes yes
READ_COMMITTED no yes yes
REPEATABLE_READ no no yes
SERIALIZABLE no no no

JPA

If you are using Spring with JPA you may come across the following exception when you use an isolation level that is different the default:

InvalidIsolationLevelException: Standard JPA does not support custom isolation levels – use a special JpaDialect for your JPA implementation
at org.springframework.orm.jpa.DefaultJpaDialect.beginTransaction(DefaultJpaDialect.java:67)
at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:378)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:372)
at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:417)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:255)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)

To solve this problem you must implement a custom JPA dialect which is explained in detail in the following article: Spring – Change transaction isolation level example.

FROM HERE

Another example in DB

Time  Transaction 1                    Transaction2
 1    Begin Tx 1                        
 2                                     Begin Tx 2
 4    Select count(*) from my_tab;        
 5                                     Select count(*) from my_tab;
 6    Insert into ... my_tab;
 7    Commit;
 8                                     Select count(*) from my_tab;
 9                                     Insert into ... my_tab;
 10                                     Select count(*) from my_tab;
 11                                    Commit;
 12   Begin Tx3
 13   Select count(*) from my_tab;

If my_tab has 10 rows then the result of the count will be:

  • Time 4 : 10 Rows
  • Time 5 : 10 Rows
  • Time 8 : 10 Rows because table is in repeateble_read mode if the transaction mode is read_commited also will be 10 rows. But if the Tx is set en read_uncommited the number of rows will be 11.
  • Time 10: Since it is in reapeateble read mode ther the count will be 11 rows (ten originals plus one of the insert in the current transaction). If the tx mode is read_commited the number of rows will be 12 (ten originals plus one insert of the tx1 and one insert of the current tx).
  • Time 13: Here the number of rows will be 12 for all transaction modes.

Here is a oracle blog explaining Locking and Concurrency in Java Persistence 2.0

好文:  Spring五个隔离级别 和 七个事务传播行为

HibernateException: Could not obtain transaction-synchronized Session for current thread

After i update to spring4/hibernate4. i got above exception.

Turns out that i need to add

<tx:annotation-driven transaction-manager=”transactionManager”/>

and

@Transactional

to my rest controller so that when the service/dao calls getCurrentSession(), spring could start the transaction and opens session from its own transaction management thru AOP.

 

If you encountered this when running unit test, it is possible that you forget to add @Transaction annotation to the Junit test class or method.

Transaction configuration with JPA and Spring 3.1

FROM HERE

This is the fifth of a series of articles about Persistence with Spring. This article will focus on the configuration of transactions with Spring 3.1 and JPA. For a step by step introduction about setting up the Spring context using Java based configuration and the basic Maven pom for the project, see this article.

The Persistence with Spring series:

Spring and Transactions

Spring provides a consistent and comprehensive transaction abstraction over many supported environments. There are two distinct ways of configuring and using transactions – annotations and AOP – each with their own advantages. The reference should provide enough material on both the decision to use the Spring transaction programming model, as well as a in depthdiscussion of its architecture.

Configuring transactions with annotations

After the introduction of Java based configuration in Spring 3.0, configuring transactions was one of the last things to require XML. Spring 3.1 introduces the new @Enable annotations – @EnableTransactionManagement – which makes it possible to fully use Java for the configuration:

@Configuration
@EnableTransactionManagement
public class PersistenceJPAConfig{
   
   @Bean
   public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(){
      ...
   }
   
   @Bean
   public PlatformTransactionManager transactionManager(){
      JpaTransactionManager transactionManager = new JpaTransactionManager();
      transactionManager.setEntityManagerFactory(
       entityManagerFactoryBean().getObject() );
      return transactionManager;
   }
   
}

However, if Java is not an option, here is the XML equivalent of this configuration:

&lt;bean id=&quot;txManager&quot; class=&quot;org.springframework.orm.jpa.JpaTransactionManager&quot;&gt;
   &lt;property name=&quot;entityManagerFactory&quot; ref=&quot;myEmf&quot; /&gt;
&lt;/bean&gt;
&lt;tx:annotation-driven transaction-manager=&quot;txManager&quot; /&gt;

For a full discussion and code samples on configuring Spring with JPA, check out a previous article of this series.

The @Transactional configuration

By default, @Transactional will set the propagation to REQUIRED, the readOnly flag to false, and the rollback only for unchecked exceptions. Also note that the isolation level is set to the database default; when using JPA, the isolation level is that of the underlying persistence provider. In the case of Hibernate, the isolation level of all transactions should beREPEATABLE_READ.

For the purpose of this discussion, the relevant application layers will be DAO, Service and Controller. These layers can of course vary from application to application, without changing the underlying principles discussed here.

The @Transactional semantics of the Service and DAO layers should both be configured with REQUIRED propagation and thereadOnly flag set to true for the relevant methods. The Controller layer should contain no transaction logic.

Note that this is also the way the DAO implementation is configured in Spring Data. For a detailed analysis of the persistence layer with Spring data, see the previous article of this series.

Assuming a clean separation of layers, where the Controller layer will only invoke the Service layer, which in turn will only call the DAO, then the DAO layer will never be called in a non-transactional context. As such the DAO will never be the transaction owner, and a more strict transaction configuration should be used for it. In this situation, the transactional semantics should beMANDATORY propagation and no readOnly flag. The MANDATORY propagation will simply ensure that a transaction has already been started when the DAO layer is entered, double checking the stated assumption that the DAO is never the transaction owner. The readOnly flag is also not needed because it will be set by the transaction owner as well.

The API layer transaction strategy

With the previous transaction configuration, the Service layer is the transaction owner – it is responsible with starting the transaction and it contains any potential rollback logic. This does however have one downside: a Service write method can invoke another Service write method; because both may contain rollback logic, the transaction owner may not have full control over the rollback logic in some circumstances. For this reason care must be taken if the transaction owner resubmits the transaction or takes corrective action – in these cases the service logic may need to be refactored to avoid this scenario.

The API layer strategy is meant to address this shortcoming by making the Controller layer the transaction owner and working with the assumption that a public Controller method shouldn’t invoke another in any scenario. With this transaction strategy, the DAO and Service transactional semantics remain the same as before; the Controller layer however will use theREQUIRES_NEW propagation to ensure that it invokes the underlying Service layer in a transactional context.

01.@Transactional( propagation = Propagation.REQUIRES_NEW )
02.public class Controller{
03....
04.}
05.@Transactional( propagation = Propagation.MANDATORY )
06.public class Service{
07....
08.}
09.@Transactional( propagation = Propagation.MANDATORY )
10.public class DAO{
11....
12.}

Spring Testing support for Transactions

The TestContext framework provides transaction support Spring enabled tests via the PlatformTransactionManager bean in the application context and the @Transactional annotation.

Spring also support various test specific annotations which, used in conjunction with the TestContext framework allow for full control over the transaction configuration: @TransactionConfiguration, @Rollback and @BeforeTransaction/@AfterTransaction.

A common usage of the framework and a common requirement of an integration test is to leave the database unchanged after the test method is finished. This can be done by annotating the test class with:

1.@TransactionConfiguration( defaultRollback = true )
2.@Transactional
3.public class TransactionalIntegrationTest{
4....
5.}

Also, note that because Spring uses proxies at runtime to manage transactions, the test class must not be final if it’s annotated with@Transactional.

Potential PitfallsChanging the Isolation level

One of the major pitfalls when configuring Spring to work with JPA is that changing the isolation of the transaction semantics will not work – JPA does not support custom isolation levels. This is a limitation of JPA, not Spring; nevertheless changing the@Transactional isolation property will result in:

org.springframework.transaction.InvalidIsolationLevelException: Standard JPA does not support custom isolation levels – use a special JpaDialect for your JPA implementation

Read Only Transactions

The readOnly flag usually generates confusion, especially when working with JPA; from the javadoc:

This just serves as a hint for the actual transaction subsystem; it will not necessarily cause failure of write access attempts. A transaction manager which cannot interpret the read-only hint will not throw an exception when asked for a read-only transaction.

The fact is that it cannot be guaranteed that an insert or update will not occur when the readOnly flag is set – its behavior isvendor dependent whereas JPA is vendor agnostic.

It is also important to understand that the readOnly flag is only relevant inside a transaction; if an operation occurs outside of a transactional context, the flag is simply ignored. A simple example of that would calling a method annotated with:

@Transactional( propagation = Propagation.SUPPORTS,readOnly = true )

from a non-transactional context – a transaction will not be created and the readOnly flag will be ignored.

HERE is another good article about spring transactional TEST

HERE

could not initialize proxy – no Session

All java web frameworks have one or more servlets that handle the requests. The servlet handles each request (HttpRequest) by creating a new thread that will finally produce the response (HttpResponse). The method that processes each request is executed inside this thread.

At the beginning of the request processing your application should allocate the resources that it needs for processing (Transaction, Hibernate session etc). At the end of the processing cycle these resources are released (Transaction is committed, hibernate session is closed, JDBC connections are released etc). Lifecycle of these resources could be managed by your framework, or could be done by your code.

In order to support application state in a stateless protocol as HTTP, we have the HttpSession object. We (or the frameworks) put on HttpSession the information that remains relevant between different request cycles of the same client.

During the processing of the first request hibernate reads (lazily) an entity from the database. Due to lazy initialization some parts of this object’s structure are hibernate proxy objects. These objects are associated with the hibernate session that created them.

When you try to process the second request, then the framework finds the entity from the previous request in the HttpSession object. Then it is trying to access a property from a child entity that was lazily initialized and now is a hibernate proxy object. The hibernate proxy object is an imitation of the real object that will ask its hibernate session to fill it with information from the database when someone tries to access one of its properties. This what your hibernate proxy is trying to do. But its session was closed at the end of the previous request processing, so now it doesn’t have a hibernate session to use in order to be hydrated (filled with real info).

Note that it is possible that you have already opened a hibernate session at the beginning of the second request, but it isn’t aware of the entity that contains the proxy object because this entity was read by a different hibernate sesion. You should re-attach the entity to the new hibernate session.

There is a lot of discussion about how to re-attach a detached entity, but the simplest approach right now is session.update(entity).

I solved the problem by annotating the method which indirectly uses Hibernate to retrieve data from the database with @Transactional.

 

Good reference on session and transaction

another post

spring Transactional annotation

A step by step explanation for Transactional Annotation.

Spring creates proxies for classes that declare @Transactional on the class itself or on members. The proxy is mostly invisible at runtime. It provides a way for Spring to inject behaviors before, after, or around method calls into the object being proxied. Transaction management is just one example of the behaviors that can be hooked in. Security checks are another. And you can provide your own, too, for things like logging. So when you annotate a method with @Transactional, Spring dynamically creates a proxy that implements the same interface(s) as the class you’re annotating. And when clients make calls into your object, the calls are intercepted and the behaviors injected via the proxy mechanism.

When Spring loads your bean definitions, and has been configured to look for @Transactional annotations, it will create these proxy objects around your actual bean. These proxy objects are instances of classes that are auto-generated at runtime. The default behaviour of these proxy objects when a method is invoked is just to invoke the same method on the “target” bean (i.e. your bean).

However, the proxies can also be supplied with interceptors, and when present these interceptors will be invoked by the proxy before it invokes your target bean’s method. For target beans annotated with @Transactional, Spring will create a TransactionInterceptor, and pass it to the generated proxy object. So when you call the method from client code, you’re calling the method on the proxy object, which first invokes the TransactionInterceptor (which begins a transaction), which in turn invokes the method on your target bean. When the invocation finishes, the TransactionInterceptor commits/rolls back the transaction. It’s transparent to the client code.

only external method calls will be under Transaction and not the self-invocation methods. 

if your bean invokes one of its own methods, then it will not be doing so via the proxy. Remember, Spring wraps your bean in the proxy, your bean has no knowledge of it. Only calls from “outside” your bean go through the proxy.

Difference between hibernate/db/spring transaction

Hibernate deals with database specific transactions, whereas spring provides a general transaction management service. @Transactional is a nice way of configuring transaction management behaviour.

The long story:

Transactions

Transactions are basically units of work (ie changes to something) that are managed as a single operation that can be either committed or rolled back. There are lots of different types of transactions in the java world – database, messaging systems like JMS, inter application transactions (for those who are not faint of heart) or anything else that may need to be included in a transaction. In the Java standard transactions are managed using the Java Transaction API which sets the rules for how to participate in a transaction.

Database

For a db transaction, it is bind to the connection get.

try{
  con.setAutoCommit(false);
  ....
  con.commit();
}catch(ex e){
  con.rollback();
}

Hibernate

Hibernate is an ORM for abstracting database components to Java objects, so its transactions are specifically related to changes made within a database. A transaction may be made up of one or many writes to various database tables that are all committed once the operation is completed. Rolling back the transaction, eg f there are any errors during the operation, allows all the changes to be undone.

Spring

At its lowest level Spring is a application framework for managing configuration and dependencies between objects. In addition it also provides an interface for managing higher level services that are used in modern applications such as databases, messaging services, MVC frameworks and transactions.

Spring is designed to be used as an all-encompassing master of objects and services within your application, so its concept of a transaction is at a higher level than the database specific transactions that hibernate concerns itself with. Spring Transactions are designed to give you fine grained control of all your transactional resources while abstracting away the often messy coding required to co-ordinate the transactions.

@Transactional

Spring provides a few different methods for using transactions – among others there xml based aspects, coding to the API and annotation based declarative transactions. The annotation based transactions are handy because you dont need to add the transaction management boilerplate code to your app (even using PlatformTransactionManager via the API has quite a bit of coding overhead).

So basically what happens with @Transactional is that at runtime spring scans your code base for @Transactional classes and methods and wraps them up in the transaction specific management code, based on what you have configured via the annotation. So a method like this:

@Transactional(propagation = REQUIRES_NEW, rollbackFor = {Exception.class})
public void saveAndSendMessage(Foo foo) throws Exception {
    dbManager.save(foo);
    Bar bar = transform(foo);
    jmsSystem.send(bar);
}  

can have spring set up a new transaction for the database and jms system, and co-ordinate them without needing to add all the specific tx management code automagically.

spring transaction 事务

事务(Transaction)

(1):事务(Transaction)是并发控制的单位,是用户定义的一个操作序列。这些操作要么都做,要么都不做,是一个不可分割的工作单位。通过事务,SQL Server能将逻辑相关的一组操作绑定在一起,以便服务器保持数据的完整性。
(2):事务通常是以BEGIN TRANSACTION开始,以COMMIT或ROLLBACK结束。
COMMIT表示提交,即提交事务的所有操作。具体地说就是将事务中所有对数据库的更新写回到磁盘上的物理数据库中去,事务正常结束。
ROLLBACK表示回滚,即在事务运行的过程中发生了某种故障,事务不能继续进行,系统将事务中对数据库的所有以完成的操作全部撤消,滚回到事务开始的状态。

(4):事务的特性(ACID特性)
A:原子性(Atomicity)
事务是数据库的逻辑工作单位,事务中包括的诸操作要么全做,要么全不做。
B:一致性(Consistency)
事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
C:隔离性(Isolation)
一个事务的执行不能被其他事务干扰。
D:持续性/永久性(Durability)
一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。
注:事务是恢复和并发控制的基本单位。

Spring 事务

除了基于XML文件的声明式事务配置外,你也可以采用基于注解式的事务配置方法。直接在Java源代码中声明事务语义的做法让事务声明和将受其影响的代码距离更近了,而且一般来说不会有不恰当的耦合的风险,因为,使用事务性的代码几乎总是被部署在事务环境中。

下面的例子很好地演示了 @Transactional 注解的易用性,随后解释其中的细节。先看看其中的类定义:

&lt;!-- the service class that we want to make transactional --&gt;
@Transactional
public class DefaultFooService implements FooService {

    Foo getFoo(String fooName);

    Foo getFoo(String fooName, String barName);

    void insertFoo(Foo foo);

    void updateFoo(Foo foo);
}

当上述的POJO定义在Spring IoC容器里时,上述bean实例仅仅通过 行xml配置就可以使它具有事务性的。如下:

&lt;!-- from the file 'ApplicationContext.xml' --&gt;

&lt;!--?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?--&gt;

  &lt;!-- this is the service object that we want to make transactional --&gt;

  &lt;!-- enable the configuration of transactional behavior based on annotations --&gt;

  &lt;!-- a PlatformTransactionManager is still required --&gt;

    &lt;!-- (this dependency is defined somewhere else) --&gt;

  &lt;!-- other &lt;bean/&gt; definitions here --&gt;

方法的可见度和 @Transactional

@Transactional 注解应该只被应用到 public 可见度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错, 但是这个被注解的方法将不会展示已配置的事务设置。

@Transactional 注解可以被应用于接口定义和接口方法、类定义和类的 public 方法上。然而,请注意仅仅@Transactional 注解的出现不足于开启事务行为,它仅仅 是一种元数据,能够被可以识别 @Transactional 注解和上述的配置适当的具有事务行为的beans所使用。上面的例子中,其实正是 <tx:annotation-driven/>元素的出现 开启 了事务行为。

Spring团队的建议是你在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。你当然可以在接口上使用 @Transactional 注解,但是这将只能当你设置了基于接口的代理时它才生效。因为注解是 不能继承 的,这就意味着如果你正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,而且对象也将不会被事务代理所包装(将被确认为严重的)。因此,请接受Spring团队的建议并且在具体的类上使用 @Transactional 注解。

在多数情形下,方法的事务设置将被优先执行。在下列情况下,例如: DefaultFooService 类被注解为只读事务,但是,这个类中的 updateFoo(Foo) 方法的 @Transactional 注解的事务设置将优先于类级别注解的事务设置。

@Transactional(readOnly = true)
public class DefaultFooService implements FooService {

    public Foo getFoo(String fooName) {
        // do something
    }

    // these settings have precedence for this method
    @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
    public void updateFoo(Foo foo) {
        // do something

    }
}

还可以加上:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true),这样就做成一个只读事务,可以提高效率。

各种属性的意义:

REQUIRED:业务方法需要在一个容器里运行。如果方法运行时,已经处在一个事务中,那么加入到这个事务,否则自己新建一个新的事务。

NOT_SUPPORTED:声明方法不需要事务。如果方法没有关联到一个事务,容器不会为他开启事务,如果方法在一个事务中被调用,该事务会被挂起,调用结束后,原先的事务会恢复执行。

REQUIRESNEW:不管是否存在事务,该方法总汇为自己发起一个新的事务。如果方法已经运行在一个事务中,则原有事务挂起,新的事务被创建。

MANDATORY:该方法只能在一个已经存在的事务中执行,业务方法不能发起自己的事务。如果在没有事务的环境下被调用,容器抛出例外。

SUPPORTS:该方法在某个事务范围内被调用,则方法成为该事务的一部分。如果方法在该事务范围外被调用,该方法就在没有事务的环境下执行。

NEVER:该方法绝对不能在事务范围内执行。如果在就抛例外。只有该方法没有关联到任何事务,才正常执行。

NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务,则按REQUIRED属性执行。它使用了一个单独的事务,这个事务 拥有多个可以回滚的保存点。内部事务的回滚不

A very good article about Transaction