Calicoder
Calicoder

Reputation: 1452

@Transactional has no effect in my Spring Boot Application

This is a pretty confusing one. I've read dozens of links that purport to explain how to use @Transactional but I've verified no transaction is being created.

Main.java

@SpringBootApplication
@EnableJpaRepositories(basePackages="com.mypackage")
@EnableTransactionManagement
@EntityScan(basePackages=["com.mypackage"])
@EnableJpaAuditing
public class Main {
    public static void main(String[] args) {
        SpringApplication.run(Main.class, args);
    }
}

SubscriptionController.java

@RequestMapping("/api/subscription")
@RestController
public class SubscriptionController {
    @Autowired SubscriptionService subscriptionService;
    Logger log = LogManager.getLogger(this.getClass().getName());
    public Collection<Subscriptions> subscribe(...) {
        log.info("transName: " + TransactionSynchronizationManager.getCurrentTransactionName + ", isAlive: " + TransactionSynchronizationManager.isActualTransactionActive());
        return subscriptionService.getAllSubscriptions();
    }
} 

SubscriptionService.java

@Service
public class SubscriptionService {
    @Transactional public Collection<Subscription> getAllSubscriptions() {
        log.info("transName: " + TransactionSynchronizationManager.getCurrentTransactionName() + ", isAlive: " + TransactionSynchronizationManager.isActualTransactionActive());
        //return subscriptions via JPQL queries here
    }
}

build.gradle

buildscript {
    repositories {
        mavenCentral()
    }

    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:2.0.0.RELEASE")
    }
}

plugins {
    id 'war'
}

apply plugin: 'org.springframework.boot'

repositories {
    mavenCentral()
}

def springVersion = '5.0.3.RELEASE'
dependencies {
    compile 'org.codehaus.groovy:groovy-all:2.3.11'
    testCompile group: 'junit', name: 'junit', version: '4.12'
    compile "org.springframework.security:spring-security-core:${springVersion}", exclude
    compile "org.springframework.security:spring-security-config:${springVersion}", exclude
    compile "org.springframework.security:spring-security-web:${springVersion}", exclude
    testCompile group: 'junit', name: 'junit', version: '4.12'
    testCompile "org.springframework:spring-test:${springVersion}", exclude
    implementation 'org.springframework.boot:spring-boot-starter-web:2.0.5.RELEASE', exclude
    compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa', version: '2.0.5.RELEASE'
    compile group: 'mysql', name: 'mysql-connector-java', version: '8.0.11'
    compile 'org.liquibase:liquibase-core:3.6.1'
    compile 'org.liquibase:liquibase-groovy-dsl:1.2.2'
}

So when I run the api call, I get null, false as the log output. The contract for @Transactional says that the transactional aspect code will be weaved into the annotated transactional method, such that there will be a transaction (and thus an entitymanager and db connection) set up before the method and closed some time afterward. But that is irrelevant becuase shouldn't spring be creating an entitymanager before the controller is run? Both things aren't working here. Neither spring, nor @Transactional, is setting up any transaction. This results in failure to do any kind of query except for what's doable via a subclasses of JpaRepository. Somehow my Jpa repositories are able to set up transactions for their own methods. But what if their results have lazily initialized properties? I need a hibernate session to get those. So I need a transaction.

Upvotes: 3

Views: 2547

Answers (4)

Tomasz
Tomasz

Reputation: 155

What class are you using for @Transactional?
You should use org.springframework.transaction.annotation.Transactional.

Upvotes: 1

shankulk
shankulk

Reputation: 591

You may also want to post your pom.xml file. spring-data or spring-tx dependency should be added for auto configurer to create transaction manager. Otherwise explicitly create a transaction manager bean and run the code again.

You can enable TRACE level logs for org.springframework and see if transaction manager is initialized. Also check transaction interceptor logs. Use logging.level.org.springframework.transaction.interceptor=TRACE

Upvotes: 0

Stephen
Stephen

Reputation: 1058

An out-of-the-box Spring-boot application using spring initializer (https://start.spring.io/) will handle transactions properly. If you make yourself a sandbox with a unit test you can observer that the transaction demarcation done in your example will produce a transaction. Here is my git hub example: https://github.com/skjenco/hibernateSandbox.git

Code:

@Test
@Transactional
public void test() {

    logger.info(TransactionSynchronizationManager.getCurrentTransactionName());
    .....

For transaction demarcation to work you must be in an object managed by a Spring (Component, Service, Managed Beans, etc). In the case above it appears that you are in a Spring managed service. Using a working sandbox may be helpful in trouble shooting your issue--that is what I have done to solve perplexing hibernate issues.

Upvotes: 0

Vitalii Muzalevskyi
Vitalii Muzalevskyi

Reputation: 662

Try to remove

@EnableJpaRepositories(basePackages="com.mypackage")
@EnableTransactionManagement

SpringBoot should do those things automatically

Upvotes: 1

Related Questions