Tutorials Logic, IN info@tutorialslogic.com

Hibernate Spring Integration @Transactional

Spring Owns the Persistence Boundary

This page is for learners building Hibernate inside Spring applications. The unique problem is transaction ownership, EntityManager lifecycle, repositories, DTO boundaries, and lazy loading in service methods.

Spring integration is not another Hibernate setup tutorial. It explains how Spring coordinates persistence infrastructure around business use cases.

In standalone Hibernate you open sessions manually. In Spring, EntityManager and transaction management are container-managed. That changes where code belongs.

  • Controller handles HTTP.
  • Service owns @Transactional business work.
  • Repository or EntityManager performs persistence access.

Service Transactions

A transaction should wrap a business action such as transfer employee, approve invoice, reserve inventory, or update profile. It should not be scattered across tiny helper methods.

  • Put @Transactional on service methods.
  • Keep slow external calls outside DB transactions.
  • Use readOnly for read service methods.

Lazy Loading and DTOs

Most Spring/Hibernate pain comes from returning entities after the transaction closes. Fetch what the response needs inside the service and map it to a DTO.

  • Avoid making every association EAGER.
  • Use fetch joins or entity graphs intentionally.
  • Return DTOs from web endpoints.

Spring + Hibernate Integration

Spring integrates seamlessly with Hibernate. The most common approach is to use Spring Data JPA with Hibernate as the JPA provider. Spring manages the SessionFactory/EntityManagerFactory, transactions, and exception translation automatically.

Spring Boot + Hibernate Configuration

Spring Boot + Hibernate Configuration
# Spring Boot auto-configures Hibernate as JPA provider
spring.datasource.url=jdbc:mysql://localhost:3306/mydb?useSSL=false
spring.datasource.username=root
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# JPA/Hibernate settings
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect

# Connection pool (HikariCP - Spring Boot default)
spring.datasource.hikari.maximum-pool-size=10
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.connection-timeout=30000

# Hibernate statistics (for debugging)
spring.jpa.properties.hibernate.generate_statistics=false

Spring + Hibernate Integration - XML Configuration

Spring + Hibernate Integration - XML Configuration
<dependencies>
    <!-- Spring Boot Starter Data JPA (includes Hibernate) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>

    <!-- MySQL Driver -->
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <scope>runtime</scope>
    </dependency>
</dependencies>

@Transactional with Spring

@Transactional Service Layer

@Transactional Service Layer
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@Transactional // All methods are transactional by default
public class OrderService {

    private final OrderRepository orderRepository;
    private final UserRepository userRepository;
    private final ProductRepository productRepository;

    public OrderService(OrderRepository orderRepository,
                        UserRepository userRepository,
                        ProductRepository productRepository) {
        this.orderRepository = orderRepository;
        this.userRepository  = userRepository;
        this.productRepository = productRepository;
    }

    // Read-only transaction (optimization)
    @Transactional(readOnly = true)
    public List<Order> getUserOrders(Long userId) {
        return orderRepository.findByUserId(userId);
    }

    // Write transaction - rolls back on any RuntimeException
    @Transactional
    public Order placeOrder(Long userId, Long productId, int quantity) {
        User user = userRepository.findById(userId)
                .orElseThrow(() -> new RuntimeException("User not found"));

        Product product = productRepository.findById(productId)
                .orElseThrow(() -> new RuntimeException("Product not found"));

        if (product.getStock() < quantity) {
            throw new RuntimeException("Insufficient stock"); // Triggers rollback
        }

        // Deduct stock
        product.setStock(product.getStock() - quantity);
        productRepository.save(product);

        // Create order
        Order order = new Order();
        order.setUser(user);
        order.setProduct(product);
        order.setQuantity(quantity);
        order.setTotal(product.getPrice() * quantity);

        return orderRepository.save(order);
        // Transaction commits here if no exception
    }

    // Rollback only on specific exceptions
    @Transactional(rollbackFor = {Exception.class},
                   noRollbackFor = {IllegalArgumentException.class})
    public void processPayment(Long orderId) throws Exception {
        // ...
    }
}

Using EntityManager Directly

EntityManager in Spring

EntityManager in Spring
import jakarta.persistence.*;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

@Repository
@Transactional
public class CustomUserRepository {

    // Spring injects EntityManager (thread-safe proxy)
    @PersistenceContext
    private EntityManager em;

    public User findById(Long id) {
        return em.find(User.class, id);
    }

    public List<User> findByRole(String role) {
        return em.createQuery("FROM User u WHERE u.role = :role", User.class)
                .setParameter("role", role)
                .getResultList();
    }

    public User save(User user) {
        if (user.getId() == null) {
            em.persist(user);
            return user;
        } else {
            return em.merge(user);
        }
    }

    public void delete(Long id) {
        User user = em.find(User.class, id);
        if (user != null) em.remove(user);
    }

    // Access Hibernate Session from EntityManager
    public void hibernateSpecificOperation(Long id) {
        org.hibernate.Session session = em.unwrap(org.hibernate.Session.class);
        User user = session.get(User.class, id);
        // Use Hibernate-specific features
    }
}

Employee Service Transaction

Employee Service Transaction
@Transactional
public void transferEmployee(Long employeeId, Long departmentId) {
    Employee employee = employees.findById(employeeId).orElseThrow();
    Department department = departments.getReferenceById(departmentId);
    employee.transferTo(department);
}

// Hibernate flushes the changed entity when the Spring transaction commits.
Before you move on

Hibernate Spring Integration @Transactional Mastery Check

5 checks
  • In standalone Hibernate you open sessions manually.
  • In Spring, EntityManager and transaction management are container-managed.
  • A transaction should wrap a business action such as transfer employee, approve invoice, reserve inventory, or update profile.
  • It should not be scattered across tiny helper methods.
  • Most Spring/Hibernate pain comes from returning entities after the transaction closes.

Hibernate Questions Learners Ask

Spring commonly applies transactions through a proxy. A self-invocation does not pass through that proxy, so the called method’s transactional annotation may be ignored. Move the boundary to a separate bean, call through the proxied service, or redesign the public transaction entry point.

DTO mapping may read lazy associations.

By default, Spring rolls back for unchecked exceptions and Errors, not every checked exception.

Next Step
Next Practice

Finish the concept here, then reinforce it with hands-on coding, interview prep, or a tool that matches the topic.

Browse Free Tutorials

Explore 500+ free tutorials across 20+ languages and frameworks.