Posted on: June 24, 2025 Posted by: rahulgite Comments: 0

1. @Transactional Annotation Issues

Problem: Changes are not reflected in the database despite using @Transactional.

Reason 1: Calling from the Same Class

  • Explanation: Spring uses proxy-based AOP for transaction management. If a @Transactional method is called from the same class, proxy is bypassed.
  • Example:
@Service
public class MyService {
    @Transactional
    public void transactionalMethod() {
        // Database update logic
    }

    public void callTransactional() {
        transactionalMethod(); // No transaction applied
    }
}
  • Solution: Move the transactionalMethod() to another Spring-managed bean.

Reason 2: Wrong Propagation Type

  • Explanation: Using Propagation.NEVER or NOT_SUPPORTED disables transactional behavior.
  • Solution: Use Propagation.REQUIRED unless there’s a specific need.
@Transactional(propagation = Propagation.REQUIRED)

Reason 3: No Exception Thrown (or Checked Exception)

  • Explanation: By default, Spring only rolls back on unchecked exceptions.
  • Example:
@Transactional
public void updateData() throws IOException {
    // Some DB logic
    throw new IOException("Checked exception"); // Transaction won't roll back
}
  • Solution:
@Transactional(rollbackFor = IOException.class)

Reason 4: No Explicit Flush (rare in Spring Boot)

  • Spring handles flush implicitly at commit time, but older setups might not.
  • Solution: Call entityManager.flush() manually if needed.

Reason 5: Incorrect Isolation Levels

  • Explanation: READ_UNCOMMITTED may result in dirty reads.
  • Solution:
@Transactional(isolation = Isolation.READ_COMMITTED)

Reason 6: Read-Only Transaction

  • Explanation: Marking transaction as readOnly = true will block write operations.
  • Solution: Remove the readOnly flag for write logic.

Reason 7: @EnableTransactionManagement Not Enabled

  • Explanation: Required in non-Spring Boot apps.
  • Solution:
@Configuration
@EnableTransactionManagement
public class AppConfig {}

2. ID Generation Gaps

Problem: Gaps in ID generation like 1, 10, 51

Reason 1: Hibernate’s Batch Pre-Allocation

  • Hibernate pre-allocates sequences in memory.
  • Example: Sequence allocationSize = 50, uses 1–50 in memory. If restart happens at 10, next allocation is 51–100.

Reason 2: Transaction Rollbacks

  • Rollback discards IDs.

Reason 3: Multiple App Instances

  • Each instance may pre-allocate separate ID batches.

Reason 4: Database-Specific Behavior

  • MySQL: Uses auto-increment.
  • PostgreSQL/Oracle: Uses sequences with caching.

Solutions:

  • Use GenerationType.IDENTITY for sequential IDs (MySQL-friendly).
  • Disable batch insert if strict order is needed.
  • Configure sequences:
CREATE SEQUENCE my_seq INCREMENT BY 1 CACHE 1;
  • Use a custom table to generate and track IDs manually.

3. N+1 Query Problem

Problem:

  • One query for parent, and N additional queries for child collections.
  • Example: Fetching all Departments and their lazy-loaded Employees → 1 (Department) + N (Employee).

Solutions:

1. Join Fetch
@Query("SELECT d FROM Department d JOIN FETCH d.employees")
List<Department> findAllDepartmentsWithEmployees();
2. Entity Graph
@NamedEntityGraph(name = "Department.employees", attributeNodes = @NamedAttributeNode("employees"))
@Entity
public class Department { ... }

@EntityGraph(value = "Department.employees")
List<Department> findAll();
3. Batch Fetching
@OneToMany(fetch = FetchType.LAZY)
@BatchSize(size = 10)
private List<Employee> employees;

@Fetch(FetchMode.SUBSELECT)
4. Eager Loading
  • Avoid unless absolutely necessary. Can lead to performance issues.

4. Soft Delete in JPA/Hibernate

Concept:

  • Logically delete records instead of removing from DB. Useful for audit and recovery.

Method 1: Boolean Flag + @Where

@Column(name = "is_deleted")
private boolean isDeleted = false;

@Where(clause = "is_deleted = false")
@Entity
public class User { ... }

Method 2: @SQLDelete

@SQLDelete(sql = "UPDATE user SET is_deleted = true WHERE id = ?")
@Entity
public class User { ... }

Caveats:

  • Cache might still hold deleted data.
  • Manual handling needed for cascade deletions.

Best Practice:

  • Use both @SQLDelete and @Where for safe and consistent soft deletion behavior.

Leave a Comment