Criteria API


Spring Boot এবং JPA/Hibernate ব্যবহার করার সময় আমরা সাধারণত দুটি উপায়ে ডাটাবেজে query চালাই —

JPQL (Java Persistence Query Language)

- Native SQL

তবে অনেক সময় আমাদের দরকার হয় ডাইনামিক query তৈরি করা, যেখানে শর্তগুলো runtime এ নির্ধারিত হবে (যেমন, “name থাকলে name দিয়ে filter করো, না থাকলে age দিয়ে করো”)। এই জায়গাতেই আসে Criteria API — Hibernate এর type-safe এবং flexible query-building system।


Criteria API কী?

Criteria API হলো JPA-র একটি শক্তিশালী feature, যা দিয়ে আমরা প্রোগ্রাম্যাটিকভাবে (কোড লিখে) query তৈরি করতে পারি। এতে compile-time type checking থাকে, ফলে query তে বানান বা টাইপ মিসটেক হওয়ার সম্ভাবনা থাকে না।


কেন Criteria API ব্যবহার করা হয়?

সুবিধা ব্যাখ্যা
Dynamic Query runtime এ শর্ত যোগ/বিয়োগ করা যায়
Type-safe Entity property গুলো IDE তে auto-complete হয়
Refactoring friendly টেবিল কলামের নাম বদলালেও কোড ভাঙে না
Framework-independent Hibernate ছাড়াও যেকোনো JPA provider এর সাথে কাজ করে


উদাহরণ ভিত্তিক ব্যাখ্যা

ধরা যাক আমাদের একটি entity আছে 

@Entity
public class Employee {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;
    private String department;
    private double salary;

    // getters and setters
}


লক্ষ্য

আমরা চাই — department এবং minimum salary ভিত্তিক employee লিস্ট আনতে, কিন্তু এই ফিল্টারগুলো optional থাকবে।


Step-by-Step Implementation

Step 1: Repository তৈরি

@Repository
public class EmployeeCriteriaRepository {

    @PersistenceContext
    private EntityManager entityManager;

    public List<Employee> findEmployees(String department, Double minSalary) {

        // CriteriaBuilder তৈরি
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();

        // CriteriaQuery তৈরি
        CriteriaQuery<Employee> cq = cb.createQuery(Employee.class);

        // Root নির্ধারণ (মূল entity)
        Root<Employee> employee = cq.from(Employee.class);

        // Predicate (শর্ত) তৈরি
        List<Predicate> predicates = new ArrayList<>();

        if (department != null) {
            predicates.add(cb.equal(employee.get("department"), department));
        }

        if (minSalary != null) {
            predicates.add(cb.greaterThanOrEqualTo(employee.get("salary"), minSalary));
        }

        // WHERE clause যোগ
        cq.where(predicates.toArray(new Predicate[0]));

        // Query execute
        return entityManager.createQuery(cq).getResultList();
    }
}


Step 2: Service Layer

@Service
public class EmployeeService {

    @Autowired
    private EmployeeCriteriaRepository employeeRepo;

    public List<Employee> search(String department, Double minSalary) {
        return employeeRepo.findEmployees(department, minSalary);
    }
}


Step 3: Controller Layer

@RestController
@RequestMapping("/api/employees")
public class EmployeeController {

    @Autowired
    private EmployeeService employeeService;

    @GetMapping
    public List<Employee> filterEmployees(
            @RequestParam(required = false) String department,
            @RequestParam(required = false) Double minSalary) {
        return employeeService.search(department, minSalary);
    }
}


Example Request

GET /api/employees?department=IT&minSalary=50000

এটি নিচের SQL এর মতো query execute করবে 

SELECT * FROM employee WHERE department = 'IT' AND salary >= 50000;


Criteria API এর আরও কিছু গুরুত্বপূর্ণ ফিচার

১. Sorting যোগ করা

cq.orderBy(cb.desc(employee.get("salary")));


২. LIKE অপারেটর ব্যবহার

predicates.add(cb.like(employee.get("name"), "%Maruf%"));


৩. GroupBy এবং Having ব্যবহার

cq.groupBy(employee.get("department"));
cq.having(cb.gt(cb.avg(employee.get("salary")), 60000));


৪. Count Query

CriteriaQuery<Long> countQuery = cb.createQuery(Long.class);
Root<Employee> countRoot = countQuery.from(Employee.class);
countQuery.select(cb.count(countRoot));
Long total = entityManager.createQuery(countQuery).getSingleResult();


Criteria API vs JPQL

বিষয় Criteria API JPQL
Type-safety ✅ আছে ❌ নেই
Dynamic Query ✅ সহজ ❌ জটিল
Readability ❌ কম ✅ বেশি
IDE auto-complete ✅ আছে ❌ নেই

সংক্ষেপে, JPQL সহজ কিন্তু static;
Criteria API কিছুটা verbose হলেও powerful এবং dynamic।


Best Practices

- Service layer এ business logic রাখো, query logic Repository তে।

- Criteria API তে Predicate আলাদা মেথডে ভেঙে লেখো।

- Optional ফিল্টার এর ক্ষেত্রে null-check ব্যবহার করো।

- Static query এর জন্য JPQL ব্যবহার করা বেশি উপযুক্ত।

- Pagination এর জন্য setFirstResult() এবং setMaxResults() ব্যবহার করো।


উদাহরণ: Pagination সহ Query

TypedQuery<Employee> query = entityManager.createQuery(cq);
query.setFirstResult(0);
query.setMaxResults(10);
List<Employee> employees = query.getResultList();