In this post we’ll be talking about a subject that is little explored by most of the developers: Transaction attributes. When we’re creating a service (EJB, Spring..), we can either control the transaction manually (BMT – Bean Managed Transaction) or delegate this responsibility to the container (CMT – Container Managed Transaction). CMT is used in most of the cases and with declarative configuration and it’s for this type of configuration that the transaction attributes exist. They define, for example, if your method must or must not be invoked from within a transactional scope.
Just to make things easier and clearer, the term client in this post means the caller, that can be a standalone application, an EJB, a Spring Service, etc.
Types of transaction attributes
There are 6 types of transaction attributes:
Required
This is the default one. It means that the method can or cannot be invoked from within a transactional scope, but that it will obligatorily be executed inside a transaction.
If the client invokes it from a transaction, the method will be executed insided this same transaction, and the client is responsible for performing commit/rollback. If the client is not running a transaction, then a new transaction will be created to run the method and, by the end of its execution, the transaction will be committed/rolled back. This attribute is the most used one.
RequiresNew
It means that regardless of the client be running a transaction or not, a new transaction will be created to run the this method and, by the end of its execution, this transaction will be committed/rolled back.
If the client is running a transaction, it will be suspended until the completion of the new transaction. It’s worth to note that they are 2 distinct transactions, they are isolated from each other.
NotSupported
It means that the method will always be executed out of the transactional scope. If the client is running a transaction, it’s suspended until the method finishes. As the code is executed out of the transactional scope, it does not impact on the original transaction. If the client is not running a transaction, the method will be executed normally and no transaction will be created.
Supports
It means that the method will be executed in or out of a transaction, depending on the caller. If the caller invokes the method out of a transactional scope, no transaction will be created and the method will be executed out of a transaction. However, if the caller invokes this method from within a transaction, it will be executed within this same transaction.
Mandatory
It means that every time the caller invokes the method, if must be done from within a running transaction. In this case, the method will be executed within this same transaction. If the caller invokes the method out of a transactional scope, an exception will be thrown and the method won’t be executed.
Never
It means the caller must never be in a running transaction when it invokes the method. In this case, the method will be executed normally without creating any transaction. If the caller invokes the method from within a running transaction, an exception will be thrown and the method won’t be executed.
Defining the transaction attribute
Let’s briefly see how to declare the transaction attribute for your services using both EJB and Spring.
EJB
The simplest way to define the transaction attribute in an EJB is through annotations. This configuration can be done at class, method or both levels. For the declaration, the annotation @javax.ejb.TransactionAttribute must be used and the following values are accepted (of type javax.ejb.TransactionAttributeType):
- TransactionAttributeType.REQUIRED
- TransactionAttributeType.REQUIRES_NEW
- TransactionAttributeType.NOT_SUPPORTED
- TransactionAttributeType.SUPPORTS
- TransactionAttributeType.MANDATORY
- TransactionAttributeType.NEVER
Example:
import javax.ejb.Stateless; import javax.ejb.TransactionAttribute; import javax.ejb.TransactionAttributeType; import javax.ejb.TransactionManagement; import javax.ejb.TransactionManagementType; @Stateless //TransactionAttribute is only valid if TransactionManagement == TransactionManagementType.CONTAINER. //That's the default value, so you can remove this annotation @TransactionManagement(TransactionManagementType.CONTAINER) //Required is the default value. This annotation can be removed. @TransactionAttribute(TransactionAttributeType.REQUIRED) public class Services { //No annotation uses the value defined at class level. public void service1() { } //Overrides the value defined at class level only for this method. @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public void service2() { } }
As any EJB configuration, XML option is also available. More details can be found here.
Spring
The configuration for Spring is very similar to the EJB one and can also be done by both annotations and XML. More details can be found here and here.
Conclusion
Despite the default configuration for transaction attribute be enough for most of the cases, it’s important to know what other options you have. This way, you can make fine adjustments that may be required for your application to work properly.
Olá Luciano! Este post está bem escrito e me foi muito útil para entender sobre os tipos de atributo de Transação EJB. Valeu!
Abs,
Ivo
LikeLike
Olá Ivo, fico feliz que tenha ajudado.
Abs!
LikeLike
Luciano,
O que esses escopos interferem na conexao ? Se for Mandatory ou required ele vai aproveitar a mesma conexão aberta ? E no caso de requires_new ele vai abrir uma nova conexao ?
Obrigado.
Abs,
Rodrigo
LikeLike
Oi Rodrigo,
Boa pergunta. Não tenho 100% de certeza, mas pela lógica parece que é isso mesmo que você disse, já que a transação é associada à uma conexão.
Abs
LikeLike
Post muito bom!! Obrigado!!
LikeLike
Que bom que foi útil Cezar. Abraço.
LikeLike
Luciano muito bom o post, bem objetivo. Estou com um problema onde minhas entidades são buscadas por um serviço ao retornar pra minha controller elas são detached, onde eu irei salvar uma entidade nova com referencia a estas entidades antes buscadas e o hibernate me fala que estão transientes e lança a famosa exception TransientObjectException.
Estava vendo a documentação e me corrigi se eu estiver errado mas o padrão do JTA é session-per-request. Pra resolver este caso meu teria quer dar load() nas entidades de referencia dentro do escopo do serviço que salva a nova entidade.
Sabe alguma outra solução?
LikeLike
Olá Raul,
Não sei se entendi muito bem sua pergunta..o escopo da transação pode ser definida de forma automática no seu EJB/Spring Bean..ou você pode iniciá-la e fechá-la manualmente..enfim, pode ser feito de várias maneiras.
Abs!
LikeLike