Bharath Kumar
Bharath Kumar

Reputation: 59

Transaction Management in Spring Data REST

Do we have any in-built transaction management when using Spring Data REST or it has to be manually maintained/handled from the calling application/service.

Say, I have two calls(first POST(for creation) and PATCH(update/insert association resource using uri-lists)) which I want to be considered as a transaction.

Thanks Bharath

Upvotes: 2

Views: 5362

Answers (3)

Laures
Laures

Reputation: 5489

I found this question when researching the transaction behavior of data-rest.

I needed control over the transaction behavior of the default controllers, especially during serialization of relationships, so i could mark transactions as read-only and route them to read-replicas of our database.

By default every call to a repository has its own transaction and database access during events or resource-assembly is managed by hibernate directly, outside of these transactions. There is no intended way for users to control transactions.

Spring does however provide methods to do that using aop-interceptors. These interceptors use a property source do decide weather or not a method requires a transaction. Springs own Transactional-Annotation is nothing more than one of these property-sources.

The following Configuration creates an advisor for the default rest-controllers that creates customized transactions for some Methods:


@Configuration
@ConditionalOnClass(name = {
    "org.springframework.data.rest.webmvc.RepositoryEntityController",
    "org.springframework.data.rest.webmvc.RepositoryPropertyReferenceController",
    "org.springframework.data.rest.webmvc.RepositorySearchController"
})
public class RepositoryRestControllerTransactionConfiguration {

    /**
     * A custom Transaction Advisor that starts customized transactions based on what data-rest controller method is called
     *
     * @param transactionManager
     * @return
     */
    @Bean
    public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor(
        TransactionManager transactionManager) {

        //Specify transaction management methods and attribute values required for transaction control
        MethodMapTransactionAttributeSource source = new MethodMapTransactionAttributeSource();
        Map<String, TransactionAttribute> methodMap = new HashMap<>();

        // methods are identified by their FQDN, simple placeholders using * are possible
        // methods that should only require reads
        methodMap.put("org.springframework.data.rest.webmvc.RepositoryEntityController.get*", createTransactionRule(true));
        methodMap.put("org.springframework.data.rest.webmvc.RepositoryEntityController.headForItemResource", createTransactionRule(true));
        methodMap.put("org.springframework.data.rest.webmvc.RepositoryPropertyReferenceController.follow*", createTransactionRule(true));
        methodMap.put("org.springframework.data.rest.webmvc.RepositorySearchController.execute*", createTransactionRule(true));

        // methods that will require write
        methodMap.put("org.springframework.data.rest.webmvc.RepositoryEntityController.put*", createTransactionRule(false));
        methodMap.put("org.springframework.data.rest.webmvc.RepositoryEntityController.patch*", createTransactionRule(false));
        methodMap.put("org.springframework.data.rest.webmvc.RepositoryEntityController.delete*", createTransactionRule(false));
        methodMap.put("org.springframework.data.rest.webmvc.RepositoryEntityController.post*", createTransactionRule(false));
        methodMap.put("org.springframework.data.rest.webmvc.RepositoryPropertyReferenceController.delete*", createTransactionRule(false));
        methodMap.put("org.springframework.data.rest.webmvc.RepositoryPropertyReferenceController.create*", createTransactionRule(false));

        source.setMethodMap(methodMap);
        source.afterPropertiesSet();

        // Generate an AOP Advisor that controls transactions
        // Advice for transaction control (TransactionInteceptor)
        BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
        advisor.setTransactionAttributeSource(source);
        advisor.setAdvice(new TransactionInterceptor(transactionManager, source));
        return advisor;
    }

    private RuleBasedTransactionAttribute createTransactionRule(boolean readOnly) {
        RuleBasedTransactionAttribute rule = new RuleBasedTransactionAttribute();

        rule.setReadOnly(readOnly);

        return rule;
    }
}

Upvotes: 0

Mathias Dpunkt
Mathias Dpunkt

Reputation: 12184

In spring data rest every repository action runs within a transaction. Even the event handlers are not running inside the repository transaction. And of course different actions on the REST API do run in separate transactions.

Here is an interesting question on this topic: Handle spring-data-rest application events within the transaction

If you want to have creation and association in one transaction then the only way to achieve this with your current entity mapping is to create a custom controller that does just that.

An alternative mapping strategy would be to treat the associated entity as a containment - so the associated entity does not have an exported repository and is maintained within the parent.

Upvotes: 5

Lee
Lee

Reputation: 738

The who point of REST is to have isolated units of work. If you are wanting to keep transactions open across calls it's probably a sign you need to adjust your RESTful strategy.

Have a look at: Transaction strategy for microservices

Upvotes: 0

Related Questions