JPA Query Types | انواع کوئری در JPA
📖توضیحات درس
📄جزوه
مفاهیم کلیدی
- JPA Query
در JPA برای خواندن یا تغییر دادهها از مکانیزمی به نام Query استفاده میشود. این Queryها معمولاً با استفاده از EntityManager اجرا میشوند و دادههای دیتابیس را به Entityهای جاوا map میکنند. در JPA سه نوع اصلی Query وجود دارد: JPQL Query، Native Query و Criteria API Query.
- JPQL (Java Persistence Query Language)
یک زبان Query سطح بالا مشابه SQL است اما به جای table و column با Entity و fieldهای آن کار میکند.
JPA این Queryها را به SQL مناسب دیتابیس تبدیل میکند، بنابراین مستقل از نوع دیتابیس هستند.
مثال:
Query query = entityManager.createQuery(
"SELECT u FROM UserEntity u WHERE u.id = :id"
);
query.setParameter("id", id);
- TypedQuery
نوعی Query در JPA است که نوع خروجی آن مشخص شده است. این کار باعث افزایش type safety میشود و نیاز به casting را حذف میکند.
مثال:
TypedQuery<UserEntity> query =
entityManager.createQuery(
"SELECT u FROM UserEntity u WHERE u.id = :id",
UserEntity.class
);
UserEntity user = query.getSingleResult();
مزیت اصلی آن جلوگیری از خطاهای runtime ناشی از cast اشتباه است.
- NamedQuery
یک Query از پیش تعریف شده است که معمولاً روی کلاس Entity یا در فایلهای پیکربندی تعریف میشود.
هدف آن متمرکز کردن Queryها و افزایش maintainability است.
مثال تعریف:
@Entity
@NamedQuery(
name = "UserEntity.findByUserId",
query = "SELECT u FROM UserEntity u WHERE u.id = :userId"
)
public class UserEntity {
}
استفاده:
Query query = entityManager.createNamedQuery("UserEntity.findByUserId");
query.setParameter("userId", id);
- Native Query
یک Query که مستقیماً با SQL دیتابیس نوشته میشود. در این حالت JPA هیچ abstraction خاصی روی SQL اعمال نمیکند.
مثال:
Query query = entityManager.createNativeQuery(
"SELECT * FROM users WHERE id = :id",
UserEntity.class
);
مزیت اصلی آن استفاده از قابلیتهای خاص دیتابیس است.
- Criteria API
یک روش برنامهنویسی برای ساخت Query به صورت programmatic و type-safe است. به جای نوشتن Query به صورت string، از objectها و متدها استفاده میشود.
مثال:
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<UserEntity> cq = cb.createQuery(UserEntity.class);
Root<UserEntity> root = cq.from(UserEntity.class);
cq.select(root)
.where(cb.equal(root.get("id"), id));
UserEntity result =
entityManager.createQuery(cq).getSingleResult();
این روش مخصوصاً برای Queryهای dynamic بسیار مفید است.
- Database Portability
یکی از مزیتهای اصلی JPQL این است که Queryها مستقل از دیتابیس هستند. اما در NativeQuery ممکن است از قابلیتهای خاص دیتابیس استفاده شود که portability را کاهش میدهد.
موارد مصاحبه ای
- What are the main types of queries in JPA?
سه نوع اصلی وجود دارد:
JPQL Query، Native Query و Criteria API Query.
- What is the difference between JPQL and SQL?
در JPQL به جای table و column از Entity و fieldهای آن استفاده میشود. JPA این Queryها را به SQL مناسب دیتابیس تبدیل میکند.
- Why is TypedQuery preferred over Query?
چون TypedQuery نوع خروجی را مشخص میکند و از نیاز به casting جلوگیری کرده و type safety را افزایش میدهد.
- What is NamedQuery in JPA?
یک Query از پیش تعریف شده است که معمولاً در کلاس Entity یا فایل configuration تعریف میشود و با نام مشخصی قابل فراخوانی است.
- When should we use NativeQuery?
زمانی که به قابلیتهای خاص دیتابیس نیاز داریم یا میخواهیم SQL بهینهتری نسبت به SQL تولیدی JPA بنویسیم.
- What is the advantage of Criteria API?
امکان ساخت Queryهای dynamic و type-safe به صورت programmatic را فراهم میکند.
- Why are NamedQueries more performant?
چون هنگام بارگذاری Persistence Unit کامپایل و validate میشوند و در زمان اجرا نیاز به parse مجدد ندارند.
- What is the drawback of NativeQuery?
از بین رفتن portability بین دیتابیسهای مختلف به دلیل وابستگی به SQL مخصوص vendor.
سناریو کاربردی
فرض کنید در یک سیستم، Entity زیر وجود دارد:
@Entity
@Table(name = "users")
public class UserEntity {
@Id
private Long id;
private String name;
}
سه روش برای دریافت کاربر با id مشخص وجود دارد.
روش اول: JPQL Query
Query query = entityManager.createQuery(
"SELECT u FROM UserEntity u WHERE u.id = :id"
);
query.setParameter("id", id);
UserEntity user = (UserEntity) query.getSingleResult();
روش دوم: TypedQuery
TypedQuery<UserEntity> query =
entityManager.createQuery(
"SELECT u FROM UserEntity u WHERE u.id = :id",
UserEntity.class
);
UserEntity user = query.getSingleResult();
روش سوم: NativeQuery
Query query = entityManager.createNativeQuery(
"SELECT * FROM users WHERE id = :id",
UserEntity.class
);
روش چهارم: Criteria API
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<UserEntity> cq = cb.createQuery(UserEntity.class);
Root<UserEntity> root = cq.from(UserEntity.class);
cq.select(root)
.where(cb.equal(root.get("id"), id));
UserEntity user = entityManager.createQuery(cq).getSingleResult();
در پروژههای واقعی معمولاً از JPQL یا TypedQuery برای Queryهای ساده و از Criteria API برای Queryهای dynamic استفاده میشود.
بیشتر بدانید
- استفاده از
setParameter
به جای قرار دادن مستقیم مقدار در Query از setParameter استفاده کنید تا از مشکلات امنیتی مانند SQL Injection جلوگیری شود.
- مزیت JPQL نسبت به NativeQuery
JPQL مستقل از دیتابیس است و JPA خودش Query مناسب دیتابیس را تولید میکند.
- استفاده از NamedQuery در پروژههای بزرگ
در پروژههایی که تعداد Query زیاد است، تعریف Queryها به صورت NamedQuery باعث خوانایی و maintainability بهتر میشود.
- Criteria API برای Queryهای Dynamic
اگر شرایط Query در runtime تغییر میکند (مثلاً فیلترهای جستجو)، استفاده از Criteria API بسیار مناسبتر از ساخت Query string است.
- مشکل نگهداری Queryهای پراکنده
اگر Queryها در متدهای مختلف نوشته شوند، نگهداری پروژه سخت میشود. استفاده از NamedQuery یا لایه Repository میتواند این مشکل را کاهش دهد.
