Spring Data JPA

1. JPA Entity

1.1 Entity Mapping

@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "first_name", nullable = false)
    private String firstName;

    @Column(name = "last_name")
    private String lastName;

    @Column(unique = true)
    private String email;

    @Temporal(TemporalType.TIMESTAMP)
    private Date createdAt;

    @Enumerated(EnumType.STRING)
    private UserStatus status;

    // Getters and setters
}

1.2 Relationships

// One-to-One
@Entity
public class User {
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "address_id", referencedColumnName = "id")
    private Address address;
}

// One-to-Many
@Entity
public class User {
    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
    private List<Order> orders = new ArrayList<>();
}

@Entity
public class Order {
    @ManyToOne
    @JoinColumn(name = "user_id")
    private User user;
}

// Many-to-Many
@Entity
public class User {
    @ManyToMany
    @JoinTable(
        name = "user_roles",
        joinColumns = @JoinColumn(name = "user_id"),
        inverseJoinColumns = @JoinColumn(name = "role_id")
    )
    private Set<Role> roles = new HashSet<>();
}

2. JPA Repository

2.1 Repository Interface

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    // Derived Query Methods
    List<User> findByLastName(String lastName);
    Optional<User> findByEmail(String email);
    List<User> findByStatusAndCreatedAtBefore(
        UserStatus status, Date date);

    // Query Methods with JPQL
    @Query("SELECT u FROM User u WHERE u.status = :status")
    List<User> findByStatus(@Param("status") UserStatus status);

    // Native Query
    @Query(value = "SELECT * FROM users WHERE status = ?1", 
           nativeQuery = true)
    List<User> findByStatusNative(String status);

    // Modifying Queries
    @Modifying
    @Transactional
    @Query("UPDATE User u SET u.status = :status WHERE u.id = :id")
    int updateUserStatus(@Param("id") Long id, 
                        @Param("status") UserStatus status);
}

2.2 Pagination và Sorting

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;

    public Page<User> findPaginated(int page, int size) {
        return userRepository.findAll(
            PageRequest.of(page, size, 
                Sort.by("lastName").ascending()
                    .and(Sort.by("firstName")))
        );
    }

    public List<User> findAllSorted() {
        return userRepository.findAll(
            Sort.by("lastName").ascending()
        );
    }
}

3. Specifications

3.1 Custom Specifications

@Repository
public interface UserRepository extends 
    JpaRepository<User, Long>, 
    JpaSpecificationExecutor<User> {
}

public class UserSpecifications {
    public static Specification<User> hasStatus(UserStatus status) {
        return (root, query, cb) -> {
            if (status == null) {
                return cb.isTrue(cb.literal(true));
            }
            return cb.equal(root.get("status"), status);
        };
    }

    public static Specification<User> emailContains(String email) {
        return (root, query, cb) -> {
            if (email == null) {
                return cb.isTrue(cb.literal(true));
            }
            return cb.like(root.get("email"), "%" + email + "%");
        };
    }
}

@Service
public class UserService {
    public List<User> findUsers(UserStatus status, String email) {
        return userRepository.findAll(
            Specification
                .where(hasStatus(status))
                .and(emailContains(email))
        );
    }
}

4. Auditing

4.1 Configuration

@Configuration
@EnableJpaAuditing
public class JpaConfig {
    @Bean
    public AuditorAware<String> auditorProvider() {
        return new SpringSecurityAuditorAware();
    }
}

public class SpringSecurityAuditorAware 
    implements AuditorAware<String> {

    @Override
    public Optional<String> getCurrentAuditor() {
        Authentication auth = 
            SecurityContextHolder.getContext().getAuthentication();

        if (auth == null || !auth.isAuthenticated()) {
            return Optional.empty();
        }

        return Optional.of(auth.getName());
    }
}

4.2 Auditable Entity

@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
public abstract class Auditable {
    @CreatedBy
    protected String createdBy;

    @CreatedDate
    protected Instant createdDate;

    @LastModifiedBy
    protected String lastModifiedBy;

    @LastModifiedDate
    protected Instant lastModifiedDate;

    // Getters and setters
}

@Entity
public class User extends Auditable {
    // User specific fields
}

5. Transaction Management

5.1 Transaction Annotation

@Service
public class UserService {
    @Transactional(readOnly = true)
    public User findById(Long id) {
        return userRepository.findById(id)
            .orElseThrow(() -> new UserNotFoundException(id));
    }

    @Transactional(
        rollbackFor = Exception.class,
        propagation = Propagation.REQUIRED,
        isolation = Isolation.READ_COMMITTED
    )
    public User createUser(User user) {
        // Business logic
        return userRepository.save(user);
    }
}

5.2 Programmatic Transaction Management

@Service
public class UserService {
    @Autowired
    private TransactionTemplate transactionTemplate;

    public User createUserProgrammatically(User user) {
        return transactionTemplate.execute(status -> {
            try {
                // Business logic
                return userRepository.save(user);
            } catch (Exception e) {
                status.setRollbackOnly();
                throw e;
            }
        });
    }
}

6. Best Practices

6.1 Lazy Loading

@Entity
public class User {
    @OneToMany(
        mappedBy = "user",
        fetch = FetchType.LAZY,
        cascade = CascadeType.ALL
    )
    private List<Order> orders;
}

@Service
public class UserService {
    @Transactional(readOnly = true)
    public List<Order> getUserOrders(Long userId) {
        User user = userRepository.findById(userId)
            .orElseThrow(() -> new UserNotFoundException(userId));

        // Orders will be loaded here
        return user.getOrders();
    }
}

6.2 Batch Operations

```java @Repository public interface UserRepository extends JpaRepository { @Modifying @Query(value = "UPDATE users SET status = ?1 WHERE id IN ?2", nativeQuery = true) void updateStatusInBatch(String status, List ids); }

@Service public class UserService { @Transactional public void deactivateUsers(List userIds) { // Process in batches of 1000 List> batches = Lists.partition(userIds, 1000); for (List batch : batches) { userRepository.updateStatusInBatch( UserStatus.INACTIVE.name(), batch ); } } }