JPQL


Spring Boot এর সাথে কাজ করার সময় আমরা প্রায়ই ডাটাবেজে query চালাতে চাই। তবে সাধারণ SQL এর পরিবর্তে JPA framework একটি বিশেষ query language দেয় — যেটির নাম JPQL (Java Persistence Query Language)। এটি Hibernate বা JPA provider কে বলে দেয় entity object এর উপর ভিত্তি করে ডেটা query করতে, অর্থাৎ টেবিল নয়, entity নিয়ে কাজ করে।


JPQL কী?

JPQL হলো SQL-এর মতো একটি query language,
কিন্তু এটি table নাম বা column নামের পরিবর্তে Entity class এবং property name ব্যবহার করে।

উদাহরণস্বরূপ,
তুমি যদি Employee নামে একটি entity ব্যবহার করো —
তাহলে SELECT e FROM Employee e এর মানে হবে,
Employee entity থেকে সব রেকর্ড আনো।


কেন JPQL ব্যবহার করবো?

সুবিধা ব্যাখ্যা
✅ Object-oriented Entity এবং property নাম ব্যবহার করে
✅ Database independent SQL dialect আলাদা হলেও query একই থাকে
✅ Type-safe Refactoring এ কম error হয়
✅ ORM integrated Hibernate এর entity lifecycle এর সাথে কাজ করে


উদাহরণ Entity

@Entity
public class Employee {

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

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

    // Getter & Setter
}


Simple Select Query

@Query("SELECT e FROM Employee e")
List<Employee> findAllEmployees();

এটি সমান হলো SQL এর:

SELECT * FROM employee;


Conditional Query (WHERE clause)

@Query("SELECT e FROM Employee e WHERE e.department = :dept")
List<Employee> findByDepartment(@Param("dept") String department);

SQL সমতুল্য:

SELECT * FROM employee WHERE department = ?;


Sorting & Ordering

@Query("SELECT e FROM Employee e ORDER BY e.salary DESC")
List<Employee> findAllOrderBySalary();

ORDER BY ঠিক SQL এর মতো কাজ করে।


Named Parameters ব্যবহার

Named parameter ব্যবহার করা যায় :parameterName দিয়ে 👇

@Query("SELECT e FROM Employee e WHERE e.name = :name AND e.salary >= :salary")
List<Employee> searchByNameAndSalary(@Param("name") String name, 
                                     @Param("salary") double salary);


LIKE অপারেটর (Pattern Search)

@Query("SELECT e FROM Employee e WHERE e.name LIKE %:keyword%")
List<Employee> findByNameContains(@Param("keyword") String keyword);

SQL এর LIKE '%keyword%' এর সমান।


Aggregate Functions (COUNT, SUM, AVG, MAX, MIN)

@Query("SELECT COUNT(e) FROM Employee e WHERE e.department = :dept")
long countByDepartment(@Param("dept") String department);

আরও উদাহরণ:

@Query("SELECT AVG(e.salary) FROM Employee e")
Double findAverageSalary();


JOIN Query

ধরা যাক একটি Department entity আছে 👇

@Entity
public class Department {
    @Id
    private Long id;
    private String name;

    @OneToMany(mappedBy = "department")
    private List<Employee> employees;
}

এখন JOIN query হবে 👇

@Query("SELECT e FROM Employee e JOIN e.department d WHERE d.name = :deptName")
List<Employee> findByDepartmentName(@Param("deptName") String deptName);

এটি SQL এর মতোই, কিন্তু entity ভিত্তিক।


Projection (Specific Fields আনা)

সব ফিল্ড না এনে নির্দিষ্ট ফিল্ড আনতে পারো 👇

@Query("SELECT e.name, e.salary FROM Employee e WHERE e.salary > :minSalary")
List<Object[]> findNameAndSalary(@Param("minSalary") double minSalary);

প্রত্যেক রো হবে Object[] আকারে।
তবে আরও ভালো উপায় হলো DTO Projection 


DTO Projection (Best Practice)

@Query("SELECT new com.example.dto.EmployeeDTO(e.name, e.salary) FROM Employee e WHERE e.salary > :minSalary")
List<EmployeeDTO> findEmployeeDTOs(@Param("minSalary") double minSalary);

EmployeeDTO হবে:

public class EmployeeDTO {
    private String name;
    private double salary;

    public EmployeeDTO(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }
}

Hibernate স্বয়ংক্রিয়ভাবে constructor mapping করবে।


Update Query

@Modifying
@Transactional
@Query("UPDATE Employee e SET e.salary = e.salary + :bonus WHERE e.department = :dept")
int updateSalary(@Param("bonus") double bonus, @Param("dept") String dept);

@Modifying এবং @Transactional লাগবে কারণ এটি data পরিবর্তন করে।


Delete Query

@Modifying
@Transactional
@Query("DELETE FROM Employee e WHERE e.department = :dept")
int deleteByDepartment(@Param("dept") String department);


Named Query (Reusable Query)

Entity এর উপরে Named Query define করা যায় 

@Entity
@NamedQuery(name = "Employee.findByDept", query = "SELECT e FROM Employee e WHERE e.department = :dept")
public class Employee { ... }

এবং Repository থেকে ব্যবহার করো 

@Query(name = "Employee.findByDept")
List<Employee> getByDepartment(@Param("dept") String dept);


JPQL বনাম Native SQL

বিষয় JPQL Native SQL
Object-oriented ✅ হ্যাঁ ❌ না
Database-independent
Performance ⚡ মাঝারি ⚡ দ্রুত (specific DB tuning এ)
Syntax Entity-based Table-based
Use case সাধারণ query, reusable Complex DB-specific query


Best Practices

- Entity নাম ও property নাম ব্যবহার করো, table নাম নয়
- DTO Projection ব্যবহার করো performance এর জন্য
- Query তে Named Parameter (:param) ব্যবহার করো
- Complex query হলে Native SQL fallback করো
- Always use @Modifying with UPDATE or DELETE


উদাহরণ Repository (Full Example)

@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {

    @Query("SELECT e FROM Employee e WHERE e.salary > :minSalary")
    List<Employee> findRichEmployees(@Param("minSalary") double minSalary);

    @Modifying
    @Transactional
    @Query("UPDATE Employee e SET e.department = :dept WHERE e.id = :id")
    void updateDepartment(@Param("id") Long id, @Param("dept") String dept);
}