前言
如果开发者正开发或维护基于Servlet的Web应用,则Servlet规范建议最好能够看看。因为它含有的内容对于Web应用开发者理解Servlet容器的工作机理很有帮助。
规范中最有趣的一部分是给出了Servlet容器是如何处理客户请求的。Servlet容器将会根据web.xml配置文件中定义的各个Servet而创建相应的单例(Singleton)。因此,客户请求可能同时访问这些单例,即多个线程同时运行你的代码。在Web应用中保证线程安全是很重要的。开发者应该对这个问题保持警惕,而且必须确保各自的代码必须以线程安全的方式运行。
温故线程安全
让我们花费一点时间来回忆什么是线程安全以及如何实现线程安全。有关线程安全,现有的资料有很多,我很喜欢Wikipedia[译者注:http://en.wikipedia.org/wiki/Thread-safe ]这篇文章。开发者基本上可以认为,如果代码是可重入的(reentrant),或者通过某种形式的互斥而实现对并发访问的保护,则代码是线程安全的。
大部分Java开发者都应该听过synchronized关键字。在不采用任何第三方库的前提下,Java本身对线程提供了原生支持,而且 synchronized关键字往往是Java应用中实现线程安全最重要的因素。Java中的同步提供了互斥支持。通过同步一块代码或整个方法能够保证同时最多只有单个线程执行它,从而实现了线程安全。但是同步的一个副作用,即阻塞。比如,大公司或律师办公室的前台小姐同时需要处理电话、邮件、受访客户等等。这使得她的工作很繁忙,而且导致一些事情不能够及时处理。
在Web应用中需要警惕阻塞。受同步保护的代码块使得其同时处理客户请求的吞吐量降低,而且很多客户处于阻塞状态,除非某客户处理完成。而且互斥不仅会带来阻塞,还会带来死锁。通常,死锁是不可恢复的。如下条件将触发死锁的发生:线程A锁住了线程B等待的资源,而且线程B锁住了线程A等待的资源,即线程B一直在等待线程A释放锁,线程A也是如此。因此,对于多线程的应用而言,死锁的预防和处理通常都是很头疼的。
另外,synchronized关键字还使得大量的同步对象到处使用,从而引入了死锁的可能性。比如,java.util.Hashtable和 java.util.Vector中提供的方法都是受互斥保护的,因此除非确实需要使用它们,否则尽量不用。开发者只需要使用 java.util.HashMap和java.util.ArrayList即可。当然,java.util.Collections中的同步方法也使用了synchronized关键字。
尽管可重入更易于管理,但它引入了其他问题。可重入代码避免了线程间数据的共享。考虑如下代码(姑且认为Java中的方法是线程安全的):
public Double pi() {
int a = 22;
int b = 7;
return new Double(a / b);
}
不管同时进入该方法的线程有多少,它总是线程安全的。各个线程都维护了属于各个线程的栈,并不同其他线程共享。其中,各个线程在当前方法(包括静态方法)中创建的方法变量仅属于当前线程,即存储在当前线程的栈中。因此,当线程A和B同时进入上述方法时,它们都将创建a和b。由于上述方法不存在数据共享,因此上述方法是线程安全的。请注意:22/7值同PI值较接近,但它们不相等。
接下来,看看如何优化上述代码吧。
private Double pi = null;
public Double pi() {
if (pi == null) {
pi = new Double(22 / 7);
}
return pi;
}
尽管改进后的方法能够提高性能,但并不是线程安全的。比如:如果pi为null,而且线程A和B同时进入第4行。因此,线程A和B会同时测试pi是否为空,它们都将返回true。接下来,如果线程A继续执行(线程B由于某种原因被暂挂),然后返回对内存地址的引用。其中,该内存地址含有22/7的结果,即pi值。最后,线程A退出方法。当线程B再次进入第5行时,新的内存地址将覆盖原先的内存地址(线程A提供的)。这太危险了,而且这种问题往往难于调试。
如果使用ThreadLocal,则不仅能够保证pi()方法是线程安全,而且能够提供性能的改善。
private static ThreadLocalpi = new ThreadLocal();
public Double pi() {
if (pi.get() == null) {
pi.set(new Double(22 / 7));
}
return (Double)pi.get();
}
ThreadLocal类能够包裹任何对象,而且能够将对象绑定到当前线程,使得它仅仅供当前线程使用。当线程初次执行pi()方法时,由于没有对象绑定到ThreadLocal实例pi上,因此get()方法返回null。借助于set()方法能够将对象绑定到当前线程,而且不供其它线程使用。因此,如果不同线程需要经常访问pi()方法,则借助于ThreadLocal不仅能够保证线程安全,而且能够提高性能。
目前,存在很多关于如何使用ThreadLocal的资源。在Java 1.4之前,ThreadLocal的性能确实很差,但是现已解决了这个问题。另外,由于对ThreadLocal的错误理解,使得很多开发者对它的误用。注意,上述实例使用ThreadLocal的方式是绝对没问题的。在引入ThreadLocal后,上述方法的行为并未发生改变,但是方法已经是线程安全的了。
通过可重入的方式开发线程安全的代码要求开发者谨慎使用实例变量或静态变量,尤其对于修改那些其他线程需要使用的对象而言。某些场合,使用同步可能更为合适。然而,为识别由于同步而引起的应用性能瓶颈往往只能借助于专业的性能评测工具或负载测试完成。
Web应用中的线程安全
好了,在温故线程安全的知识后,来研究Web应用中是如何线程安全的吧!开发者通过创建Web页面来操作数据库。比如,在Web层和业务逻辑层都能够操作RDBMS。本文使用Hibernate将业务模型持久化到数据库中。在Web层,开发者可以使用Tapestry、Wicket、Struts、 WebWork、JSF、Spring MVC,或者其他运行在Web容器中的Web框架。
至于Web层的具体实现并不是本文的重点。本文将关注如何管理数据库连接, 这也是Web应用中处理线程安全问题是经常要考虑的资源。数据库连接对象,比如连接、结果集、Statement、Hibernate Session,是有状态对象。当然,它们不是线程安全的,因此不能够同时供多个线程访问。在本文前面已经提到,开发者应尽量避免使用同步。无论是 synchronized关键字,还是那些同步类(Hashtable或Vector),应尽量避免使用。因此,如果使用可重入,则不用处理阻塞或死锁。
当然,通过可重入实现线程安全以访问数据库并不是件简单的工作。比如,有些开发者可能会在Servlet容器配置中添加过滤器。因此,在客户请求到来时,过滤器将创建JDBC连接或Hibernate Session,并借助于ThreadLocal类将它们绑定到当前线程中,从而供业务逻辑使用。如果直接使用J2EE API,则开发者除了需要做很多同业务逻辑无关的操作外,还需要管理事务、DB错误等等开发内容。请注意,这些同业务逻辑无关的操作的维护工作往往很费时间。
Spring的闯入
一些Java开发者可能听说过Spring提供的DAO抽象。当然,一些开发者也有可能使用过它。借助于Spring提供的模板,开发者能够使用 DAO代码的重用。借助于Spring AOP,开发者还能够使用声明式事务。因此,本文来研究Spring是如何实现以线程安全方式访问RDBMS的。比如,Spring允许以JDBC、 Hibernate、JDO、iBATIS、TopLink等方式访问数据库。如下给出的实例是企业应用中很常见的情景。
首先,定义数据源和用于Hibernate SessionFactory。
<bean
id="propertyConfigurer"class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>WEB-INF/jdbc.properties</value>
</list>
</property>
</bean>
<bean id="dataSource"class="org.apache.commons.dbcp.BasicDataSource"destroy-method="close">
<property name="driverClassName"><value>${jdbc.driverClassName}</value></property>
<propertyname="url"><value>${jdbc.url}</value></property>
<propertyname="username"><value>${jdbc.username}</value></property>
<propertyname="password"><value>${jdbc.password}</value></property>
</bean>
<bean id="sessionFactory"class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
<property name="mappingDirectoryLocations">
<list>
<value>classpath:</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<propkey="hibernate.dialect">net.sf.hibernate.dialect.HSQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
这是使用Hibernate的典型配置,即通过定义的数据源连接到数据库、通过本地SessionFactory创建Hibernate SessionFactory。接下来,需要定义业务对象(实现对DB的访问)和事务管理器(通过HibernateSession管理本地事务)。其中,业务对象暴露的方法能够在数据库中添加新的纪录,而事务管理器能够将方法包裹在事务中。它们的定义如下。
public interface CustomerDAO {
public void createCustomer(Customer customer);
}
public class HibernateCustomerDAO implements CustomerDAO {
private HibernateTemplate hibernateTemplate = null;
public void setSessionFactory(SessionFactory sessionFactory) {
this.hibernateTemplate = new HibernateTemplate(sessionFactory, false);
}
public void createCustomer(Customer customer) {
this.hibernateTemplate.save(customer);
}
}
开发者应该已经看到,上述类使用了Spring提供的HibernateTemplate。注意,模板的开发遵循了业界最佳实践,并且将一些同业务不相关,但J2EE API规定要处理的那些代码处理掉了。与此同时,它通过DAO抽象将受查异常转换为非受查异常。当然,Spring不只是为使用Hibernate提供模板,它还为JDBC、iBATIS、SqlMap、JDO、TopLink提供类似模板。由于这些模板类及其实例变量实现了可重入,即都是线程安全的,因此允许并发线程同时使用模板。使用这些模板不仅能够实现代码的重用,还提供了最佳实践。除了以线程安全方式访问DB外,模板还提供了其他很多有意义的内容。好了,来看看如何定义业务对象和事务管理器吧!
<bean id="customerDAOTarget"class="test.usecase.HibernateCustomerDAO">
<property name="sessionFactory"><refbean="sessionFactory"/></property>
</bean>
<bean id="transactionManager"class="org.springframework.orm.hibernate.HibernateTransactionManager">
<property name="sessionFactory"><refbean="sessionFactory"/></property>
</bean>
<bean id="customerDAO"class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager"><refbean="transactionManager"/></property>
<property name="target"><refbean="customerDAOTarget"/></property>
<property name="transactionAttributes">
<props>
<prop key="create*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
如果开发者对Spring中事务管理的配置不熟悉,则本文正好满足你们。首先,上述Spring配置片断定义了业务对象 HibernateCustomerDAO,它包裹了Hibernate SessionFactory。注意,默认时,Spring中定义的JavaBean都是单例的,HibernateCustomerDAO也不例外。这 意味:多个线程可能同时执行createCustomer()方法。
其次,配置了Hibernate事务管理器,它包裹了同一Hibernate SessionFactory实例。在事务管理器每次执行时,它都会完成如下几件事情。其一,检查Hibernate Session是否绑定到当前线程。如果已绑定,则直接使用它。如果还未绑定,事务管理器将告知Hibernate SessionFactory创建新的Session,然后将创建的Session绑定到当前线程。其二,如果当前没有处于活动的事务,则事务管理器将启动新的事务,并将Session包裹进来。否则,直接参与到活动事务中。
整个过程是通过使用Spring提供的TransactionProxyFactoryBean实现的。当然,这是一种以声明方式实现的事务管理过 程。TransactionProxyFactoryBean能够为业务对象创建代理对象,从而通过事务管理器管理事务。当每次通过代理对象调用 createCustomer()方法时,事务管理器将根据事务属性管理事务。当前,Spring除了提供 HibernateTransactionManager事务管理器外,还为JDBC数据源、JDO、TopLink提供了相应的事务管理器。
好了,再来看看业务对象吧!当调用createCustomer()方法时,HibernateTemplate将查找绑定到当前线程的 Hibernate Session。由于上述配置文件片断传入到HibernateTemplate构建器的第二个参数为false,因此如果没有绑定Hibernate Session,则将抛出未受查异常。这对于那些未正确配置事务管理功能的场和特别有用(注意,事务管理器很重要)。一旦事务管理配置好后,Hibernate Session将绑定到当前线程,从而启动事务。请注意,HibernateTemplate不会去检查事务是否激活,也不会显示地启动或终止事务。也请注意,如果在声明的方法(事务属性中给出的)中抛出了未受查异常,则当前活动事务将回滚。至于事务属性的研究,本文不再给出。
结论
最后,来总结一下Spring以线程安全方式实现数据访问吧。通过使用事务管理和权衡ThreadLocal提 供的功能,Spring将数据库连接(JDBC连接、HibernateSession、JDO持久化管理器)绑定到当前线程,从而供DAO模板使用。本文在最开始研究了数据库连接并没有在线程间共享。Spring不仅提供了声明式事务管理、J2EE API抽象、最佳实践,而且其提供的模板是线程安全的。当使用Spring访问DB时,通过可重入实现应用的线程安全是最为可靠、常见的做法。
原文链接地址如下:
http://www.javalobby.org/articles/thread-safe/index.jsp
Thread-safe webapps using Spring
Table of Contents
- Introduction
- Reviewing thread-safety
- Thread-safety in web applications
- Spring comes to the rescue
- Conclusion
If you are creating or maintaining servlet based webapplications you should take the time to read the servlet specifications if youhaven't already. They contain a great deal of very useful information on howservlet containers work which developers creating web-based applications shouldunderstand.
One of the more interesting areas of the specifications is how servletcontainers handle incoming requests. The servlet container creates a singletonof each servlet defined in the web.xml configuration file. Requests may andprobably will occur concurrently which means multiple threads will be runningyour code simultaneously. This means thread-safety is an important issue in webapplication. Developers should be aware of this issue and should make suretheir code works in a thread-safe way.
Reviewing thread-safety
Let's take a moment to think about what thread-safety means and how youcan achieve it. There are many resources available on the subject ofthread-safety and I really like the article on Wikipedia. Basically thearticle says a piece of code is thread-safe if it is reentrant or protectedfrom multiple simultaneous execution by some form of mutual exclusion.
Most of you probably know or have heard or read about the synchronizedkeyword in Java. Java offers thread support natively - without additionallibraries - and the synchronized keyword is seen by many as the corner stone ofthread-safety in Java. Synchronization offers mutual exclusion in Java.Synchronizing a block of code or an entire method guarantees not more than onethread will execute the code at any given time, effectively guaranteeingthread-safe execution. One of the side effects of synchronization iscongestion. You probably know the sight of those young girls sitting at thereception desks of big corporations or maybe in a lawyers office. They have totake all incoming phone calls, handle the snail mail, maybe even handle thee-mail of multiple executives and welcome visitors. On busy days this probablyleads to a lot of stress and it certainly leads to congestion.
Congestion is a big problem in web-based applications. Having even asingle synchronized block of code executed by every incoming request may bringyour application to its knees with no more than 5 simultaneous users as onlyone thread can execute a synchronized block of code while other threads remainsuspended until they can obtain a lock. Congestion is not the only problemcreated by mutual exclusion, you may also be faced with deadlocks. A deadlockis usually non-recoverable and is established when thread A has a lock on anartifact in the application and waits for an artifact locked by thread B.Meanwhile, thread B actually waits on thread A to release its lock. Oftendeadlocks are much more complex involving many threads creating a cascade oflocks that together result in a deadlock.
Besides the synchronized keyword Java also offers a number of synchronizedobjects that are widely used and may lead to the same problems. They arejava.util.Hashtable and java.util.Vector. Their methods are synchronized, soonly use them if you really really have to. Otherwise use java.util.HashMap andjava.util.ArrayList. The synchronized methods in java.util.Collections alsooffer synchronized behavior.
Reentrance introduces a different set of problems yet they are easier tomanage. Reentrant code avoids sharing data across threads. Lets put this in aJava perspective. First you need to understand - or maybe you understandalready - methods in Java are explicitly thread-safe. Consider the trivialmethod below:
public Double pi() {
int a = 22;
int b = 7;
return newDouble(a / b);
}
No matter how many threads enter this method simultaneously, never everwill this method be unsafe. Every thread has its own stack that is local to thethread and not shared with other threads. When a thread enters a methodvariables created within the scope of that method are stored in the stack ofthat thread (this also goes for static methods). So when threads A and B enterthis method simultaneously they will both create variables a and b. The exampleis thread-safe because no data is shared. Note: the product of dividing 22 by 7comes close to pi and will do for most use cases but it's not exactly pi.
Consider the following example that optimizes performance by doing thecalculation once and reusing the result afterwards.
private Double pi = null;
public Double pi() {
if (pi ==null) {
pi =new Double(22 / 7);
}
return pi;
}
While this example may improve performance it is not thread-safe. Considerthe case where pi is null and threads A and B enter the method andsimultaneously execute line 4. They both test if pi is null and both testsreturn true. Next, consider thread A continues execution, returns the referenceto the memory address that contains a object variable that contains the resultof 22 divided by 7 and exits the method while thread B is suspended by the VMfor some reason. When the execution of thread B continues afterwards a newobject variable will be created on line 5 and the memory address held by thereference that has already been returned by the method will be overwritten witha new memory address. This is potentially very dangerous and could lead tonasty bugs.
Consider this example which uses ThreadLocal to make the method pi() againthread-safe while still offering performance gains:
private static ThreadLocal pi = new ThreadLocal();
public Double pi() {
if (pi.get() ==null) {
pi.set(newDouble(22 / 7));
}
return(Double)pi.get();
}
The ThreadLocal class wraps any object and binds it to the current threadthus making objects local to the thread. When a thread executes the pi() methodfor the first time there will be no object bound to the thread by ThreadLocalinstance pi so the get() method will return null. The set() method will bind anobject to the thread that is not shared by other threads. If the method pi() iscalled often per thread this approach may still offer a considerableperformance gain while guaranteeing thread-safety.
There's a lot of skepticism about the use of ThreadLocal. While it's truethe performance of ThreadLocal was very poor prior to Java 1.4 this issue hasbeen resolved in the meanwhile. It's also true there's a lot ofmisunderstanding about the use of ThreadLocal resulting in a lot of misuse.However, the usage of ThreadLocal in the example above is perfectly save. Asyou can see, the behavior of the method has stayed the same after introducingThreadLocal, we only made the method thread-safe.
Writing thread-safe code by reentrance requires you to be careful whenusing instance variables or static variables, especially when you are modifyingobjects that may be used by other threads. Synchronization may offer a outcomeis some use cases. However, testing your application to identify performancebottlenecks that may be introduced by synchronization is only possible by usingprofiling tools and testing under load.
Thread-safety in web applications
After this crash-course in thread-safety let's consider how this couldaffect web application by examining a typical use case. We create a number ofpages that allows the manipulation of data in a database through a well-definedprocess. This process is implemented partially in the workflow of the web tierand partially in business logic. We use Hibernate to persist our domain modelto the database. The web tier could be Tapestry, Wicket, Struts, Webwork, JSF,Spring MVC or any other framework designed to run in the servlet container.
How the web tier is implemented is outside the scope of this article.Instead we will focus on how to manage database connectivity which is usuallythe biggest source of problems related to thread-safety in web applications.Database connectivity objects like connections, result sets, statements orHibernate sessions are stateful objects. They are not thread-safe by design andshould not be shared by multiple threads simultaneously. We've decided upfrontwe want to avoid the use of synchronization in our code - either through theuse of the synchronized keyword or synchronized classes like Hashtable orVector - because we do not want to deal with congestion or deadlocks. Insteadwe want to achieve thread-safety through reentrance.
Still, implementing thread-safe database access through reentrance remainsa tedious task. Some clever people have come up with a solution by adding afilter in the servlet container configuration. Such a filter would amongstother things create a JDBC connection or Hibernate session at the start of therequest before the web tier is invoked and bind it to the current thread bymeans of ThreadLocal for use in the business logic. While this approach wouldallow us to achieve thread-safe data access there still is the issue ofmanaging transactions and database errors in addition to the required use a lotof boilerplate code in the business logic. Boilerplate code is particularly badnot only because we have to copy and paste it which makes our application hardto maintain but also because it's potentially buggy.
Spring comes to the rescue
Some of you may have heard of the data access abstraction offered bySpring, some may have used it. Spring's data access abstraction is known fordeclarative transaction demarcation and reuse of data access best practicesthrough templates. One area that is less covered when reviewing Spring is thethread-safe way in which data access is achieved. This achievement is availablein Spring for JDBC, Hibernate, JDO, iBatis and TopLink. Let's have look at howthis works in our typical use case.
We start by defining a data source and session factory for Hibernate.
<bean id="propertyConfigurer"class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<propertyname="locations">
<list>
<value>WEB-INF/jdbc.properties</value>
</list>
</property>
</bean>
<bean id="dataSource"class="org.apache.commons.dbcp.BasicDataSource"destroy-method="close">
<propertyname="driverClassName"><value>${jdbc.driverClassName}</value></property>
<propertyname="url"><value>${jdbc.url}</value></property>
<propertyname="username"><value>${jdbc.username}</value></property>
<propertyname="password"><value>${jdbc.password}</value></property>
</bean>
<bean id="sessionFactory"class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
<propertyname="dataSource">
<refbean="dataSource"/>
</property>
<propertyname="mappingDirectoryLocations">
<list>
<value>classpath:</value>
</list>
</property>
<propertyname="hibernateProperties">
<props>
<propkey="hibernate.dialect">net.sf.hibernate.dialect.HSQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
This is a basic setup for a typical use case using Hibernate. We define adata source to connect to the database and a local session factory that createsan instance of a Hibernate session factory. Our next goal is to add a businessobject that accesses the database through Hibernate and a transaction managerthat manages local transactions through a Hibernate session. The businessobject exposes a method that creates an object in the database while thetransaction manager wraps this method in a transaction. First lets have a lookat the business object and its interface.
public interface CustomerDAO {
public voidcreateCustomer(Customer customer);
}
public class HibernateCustomerDAO implements CustomerDAO{
privateHibernateTemplate hibernateTemplate = null;
public voidsetSessionFactory(SessionFactory sessionFactory) {
this.hibernateTemplate = new HibernateTemplate(sessionFactory, false);
}
public voidcreateCustomer(Customer customer) {
this.hibernateTemplate.save(customer);
}
}
As you can see from the example above we use HibernateTemplate which isoffered by Spring. This template class is implemented following best practiceguidelines and abstracts boilerplate code. It will also convert checkedexceptions to unchecked exceptions thrown by the data access technology anddoes a great job at throwing specific exceptions for specific error conditions.Similar template classes are offered by Spring for JDBC, iBatis SqlMap, JDO andTopLink. All of these template classes and their instance variables arethread-safe through reentrance and can be shared safely by multiple threadsconcurrently. Using these templates not only leverages reuse of code and offersbest practices. There's something else going on that offers us thread-safe dataaccess. Let's have a look at how we set up our business object and transactionmanagement in Spring.
<bean id="customerDAOTarget"class="test.usecase.HibernateCustomerDAO">
<propertyname="sessionFactory"><refbean="sessionFactory"/></property>
</bean>
<bean id="transactionManager"class="org.springframework.orm.hibernate.HibernateTransactionManager">
<propertyname="sessionFactory"><refbean="sessionFactory"/></property>
</bean>
<bean id="customerDAO"class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<propertyname="transactionManager"><refbean="transactionManager"/></property>
<propertyname="target"><refbean="customerDAOTarget"/></property>
<propertyname="transactionAttributes">
<props>
<propkey="create*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
For those that are not familiar with transaction management in Spring,let's go through the configuration. First we setup up our business objectcalled HibernateCustomerDAO which we wire with the Hibernate session factoryinstance. Note that all beans in Spring are by default singleton, so is ourbusiness object. This means multiple threads may execute the createCustomer()method simultaneously.
Next we configure the Hibernate transaction manager which is wired withthe same Hibernate session factory instance. This transaction manager will do anumber of things every time it is called upon. First of all the transactionmanager will check if a Hibernate session is bound to the current thread andwill use it if one is available. If no Hibernate session is bound to thecurrent thread the transaction manager will ask the Hibernate session factoryfor a new Hibernate session and will bind that session to the current thread.Next - as defined in our setup - the transaction manager will start a newtransaction through the Hibernate session if no transaction is currentlyactive, otherwise the current active transaction is joined.
This behavior can be specified declaratively through theTransactionProxyFactoryBean which is also bundled with Spring.TransactionProxyFactoryBean creates a proxy object for our business object thatmanages transactions through a transaction manager. Every time thecreateCustomer() method is called through this proxy object the transactionmanager will manage transactions as defined by the transaction attributes.Spring currently offers a number of transaction manager implementations next tothe HibernateTransactionManager, including transaction managers for JDBC datasources, JDO and TopLink.
Now, let's go back to our business object. When we call thecreateCustomer() method HibernateTemplate will look for a Hibernate sessionbound to the current thread. Because we passed false as a second parameter inthe HibernateTemplate constructor a unchecked exception will be thrown if noHibernate session is found, which offers another security net in case we didnot configure transaction management properly for the createCustomer() method.Remember what we said about the transaction manager. If transaction managementis configured properly a Hibernate session will be bound to the current threadand a transaction will have been started. Note however HibernateTemplate willnot check if a transaction is currently active, nor will it explicitly start orend transactions. Also note the current active transaction will be rolled backif a unchecked exception is thrown in the scope of a demarcated method. Throughtransaction attributes we can modify this behavior declaratively although thisgoes beyond the scope of this article.
Conclusion
Let's briefly sum up what we've learned about thread-safe data accessoffered by Spring. By using transaction management and leveraging the power ofThreadLocal Spring binds a database connectivity artifact - either a JDBCconnection, a Hibernate session or a JDO persistence manager - to the currentthread that is used by the data access templates. If you put this into perspectivebased on what we talked about in the first part of this article you can seethat database connections are not shared between threads concurrently. Springdoes not only offers declarative transaction management, abstraction of boilerplate code and best practices, it also offers thread-safety. Achievingthread-safety in your application through reentrance is straight-forward whenusing Spring for database connectivity.