Baeldung

Java, Spring and Web Development tutorials

 

Commit on JdbcTemplate or DataSource in Java
2025-12-30 05:20 UTC by Sagar Verma

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.

The post Commit on JdbcTemplate or DataSource in Java first appeared on Baeldung.
       

 

Content mobilized by FeedBlitz RSS Services, the premium FeedBurner alternative.