eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Course – LJB – NPI EA (cat = Core Java)
announcement - icon

Code your way through and build up a solid, practical foundation of Java:

>> Learn Java Basics

Partner – LambdaTest – NPI EA (cat= Testing)
announcement - icon

Distributed systems often come with complex challenges such as service-to-service communication, state management, asynchronous messaging, security, and more.

Dapr (Distributed Application Runtime) provides a set of APIs and building blocks to address these challenges, abstracting away infrastructure so we can focus on business logic.

In this tutorial, we'll focus on Dapr's pub/sub API for message brokering. Using its Spring Boot integration, we'll simplify the creation of a loosely coupled, portable, and easily testable pub/sub messaging system:

>> Flexible Pub/Sub Messaging With Spring Boot and Dapr

1. Overview

Transaction management is one of those topics that appears simple on the surface but becomes confusing the moment frameworks are introduced. When developers work with plain JDBC, the rules are clear. We open a connection, disable auto-commit, execute SQL statements, and explicitly call commit() or rollback() based on the outcome. Everything is visible and under our control.

However, when Spring and JdbcTemplate are introduced, that mental model starts to break down. Many developers coming from a JDBC background ask a very natural question: where exactly should commit be called when using JdbcTemplate or a DataSource? Some attempt to retrieve the connection manually, others try to mix Spring abstractions with low-level JDBC calls, and a few even attempt to call commit() directly on the DataSource. All of these approaches usually lead to subtle bugs, unexpected rollbacks, or inconsistent data.

This tutorial aims to clarify that confusion. We’ll examine how Spring manages transactions internally, why manual commits don’t work as expected, and how to define transaction boundaries correctly using both declarative and programmatic approaches. By the end, we’ll have clear visibility into who owns the transaction, when commits occur, and how to take control only when it truly makes sense.

2. Understanding the Core Confusion

2.1. Why commit() Feels Unclear with JdbcTemplate

The root of the confusion lies in how JdbcTemplate operates. Unlike plain JDBC, JdbcTemplate doesn’t expose a Connection directly to our application code. Instead, it requests a connection from the configured DataSource, performs the database operation, and then releases the connection back to the pool. This entire process is invisible to the developer.

Because of this abstraction, there is no place where a commit can be called. Developers often assume that JdbcTemplate itself might expose some commit-related API, but it doesn’t. That design is intentional. JdbcTemplate is focused purely on executing SQL safely and efficiently, not on transaction control.

When developers attempt to work around this by manually retrieving a Connection from the DataSource, disabling auto-commit, and then passing control back to JdbcTemplate, they’re unknowingly fighting against Spring’s transaction infrastructure. This usually causes unpredictable behavior, especially when connection pooling or nested transactions are involved.

2.2. Who Actually Controls the Transaction

In a Spring-based application, the transaction is not owned by the DAO, repository, or service class. Instead, it’s owned by the Spring container itself. Spring decides when a transaction starts, which connection participates, and when it is committed or rolled back.

To make this easier to visualize, consider the following logical flow:

flow digram for internal working

This separation of responsibility is intentional. It allows business logic to remain clean and focused, while transaction mechanics are handled consistently across the application. Once this concept is understood, the idea of manually calling commit() begins to feel out of place.

3. Declarative Transaction Management With @Transactional

3.1. Using @Transactional to Define Boundaries

The most common and recommended way to manage transactions in Spring is declarative transaction management using @Transactional. Instead of explicitly managing commits, developers declare transactional boundaries and let Spring handle the lifecycle.

Before looking at the code, notice that there’s no commit or rollback logic inside the method. The business logic stays focused and readable.

Below is a simple service example that demonstrates this pattern:

@Service
public class OrderService {

    private JdbcTemplate jdbcTemplate;

    public OrderService(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @Transactional
    public void placeOrder(long orderId, long productId) {
        jdbcTemplate.update(
          "insert into orders(id, product_id) values (?, ?)",
          orderId,
          productId
        );

        jdbcTemplate.update(
          "update products set stock = stock - 1 where id = ?",
          productId
        );
    }
}

Spring starts a transaction before placeOrder() executes. Both SQL statements participate in the same transaction. If the method completes normally, Spring commits the transaction automatically. If a runtime exception occurs, Spring rolls the transaction back.

3.2. Transaction Manager Configuration

Declarative transactions require a transaction manager. When using JDBC-based access, this is typically a DataSourceTransactionManager.

Below is a minimal Java-based configuration that enables transaction management:

@Configuration
@EnableTransactionManagement
public class TransactionConfig {

    @Bean
    public PlatformTransactionManager transactionManager(
      DataSource dataSource
    ) {
        return new DataSourceTransactionManager(dataSource);
    }
}

Once this configuration is in place, Spring knows how to start, commit, and roll back transactions whenever it encounters @Transactional. At this point, there is no reason to interact with commits manually in most applications.

3.3. Verifying Automatic Commit

The following test validates the claim that Spring commits automatically when using @Transactional.

@SpringBootTest
class OrderServiceTest {

    @Autowired
    private OrderService orderService;

    @Test
    @Transactional
    void givenTransactionalMethod_whenNoException_thenTransactionCommits() {
        orderService.placeOrder(1L, 100L);
    }
}

This test executes a transactional method without any explicit commit. The successful execution confirms that Spring started and committed the transaction automatically.

Because the test itself is transactional, Spring rolls back changes after the test completes, ensuring isolation between tests.

4. Programmatic Transaction Management With Explicit Commit Control

Declarative transactions are sufficient for most use cases, but there are scenarios where developers must make commit and rollback decisions programmatically. Spring supports this through the PlatformTransactionManager API.

The key distinction in this approach is that the system commits nothing unless the application explicitly requests it.

4.1. Programmatic Transaction Implementation

Before reviewing the code, it is important to understand the intent. This repository explicitly starts a transaction, performs multiple database operations, and commits only when instructed. The code never interacts with JDBC’s Connection or calls commit() directly.

The following repository example demonstrates this approach:

@Repository
public class PaymentRepository {

    private JdbcTemplate jdbcTemplate;
    private PlatformTransactionManager transactionManager;

    public PaymentRepository(
      JdbcTemplate jdbcTemplate,
      PlatformTransactionManager transactionManager
    ) {
        this.jdbcTemplate = jdbcTemplate;
        this.transactionManager = transactionManager;
    }

    public void processPayment(long paymentId, long amount) {
        TransactionDefinition definition =
          new DefaultTransactionDefinition();

        TransactionStatus status =
          transactionManager.getTransaction(definition);

        jdbcTemplate.update(
          "insert into payments(id, amount) values (?, ?)",
          paymentId,
          amount
        );

        jdbcTemplate.update(
          "update accounts set balance = balance - ? where id = 1",
          amount
        );

        transactionManager.commit(status);
    }
}

Here, the transaction starts when Spring calls the getTransaction() method. Spring finalizes the transaction only when we invoke the commit(status).

4.2. Verifying Explicit Commit Behavior

The following test verifies that the program persists data only when it explicitly calls commit():

@SpringBootTest
class PaymentRepositoryTest {

    @Autowired
    private PaymentRepository paymentRepository;

    @Test
    void givenProgrammaticTransaction_whenCommitIsCalled_thenChangesArePersisted() {
        paymentRepository.processPayment(1L, 200L);
    }
}

This test directly maps to the repository behavior. Because commit(status) is invoked, the transaction completes successfully and changes are persisted.

If the transactionManager.commit(status) call is removed and the test is run again, no data persists. This confirms that programmatic transactions never auto-commit. Without an explicit commit request, Spring rolls the transaction back when the method completes.

5. Why Is Committing to the DataSource Incorrect?

Calling commit() directly on a JDBC Connection bypasses Spring’s transaction synchronization. Spring may be coordinating a larger transactional context involving nested calls or multiple resources. A manual commit disrupts that coordination and results in partial persistence.

To illustrate the risk, let’s consider a REST endpoint that performs multiple database operations across different services. Even if an HTTP request succeeds, manually calling commit() on a single connection doesn’t guarantee overall consistency:

POST /payments
{
  "paymentId": 1,
  "amount": 200
}

If one service commits manually while another fails, the system ends up in an inconsistent state. By routing all commit and rollback operations through PlatformTransactionManager, Spring guarantees predictable and consistent transactional behavior.

6. Conclusion

The question of where to commit when using JdbcTemplate or a DataSource is ultimately a question of responsibility. In a Spring application, transaction responsibility lies with the framework, not with individual components.

By embracing Spring’s transaction management model, we write cleaner code, avoid subtle bugs, and gain consistent behavior across our application. Whether we use declarative annotations or programmatic control, the key principle remains the same, never bypass Spring’s transaction manager.

As always, the code for these examples is available over on GitHub.

Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

eBook Jackson – NPI EA – 3 (cat = Jackson)