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();