JPA Custom Queries Sinhala: ඔබේ Spring Boot Project එකට Super Powers

ආයුබෝවන් යාළුවනේ! කොහොමද ඉතින් ඔයාලට? අද අපි කතා කරන්න යන්නේ Spring Boot Applications වල Database interactions කරන කෙනෙක්ට නැතුවම බැරි, හරිම වැදගත් මාතෘකාවක් ගැන – ඒ තමයි Custom JPA Queries.
අපි දන්නවනේ Spring Data JPA කියන්නේ Database එකත් එක්ක වැඩ කරන්න තියෙන මරුම tool එකක් කියලා. නිකන්ම Repository එකක් හදලා දීලා, methods ටිකක් ලිව්වම ඉබේටම SQL queries generate කරලා දෙන එක පුදුම දෙයක් නේද? ඒත් හැම වෙලාවෙම අපිට මේ auto-generate වෙන methods ප්රමාණවත් වෙන්නේ නැහැ. සමහර වෙලාවට අපිට තව ටිකක් සංකීර්ණ queries ලියන්න වෙනවා, අපේ Business Logic එකට හරියන්න. අන්න ඒ වගේ වෙලාවට තමයි Custom JPA Queries වල වැදගත්කම තේරෙන්නේ.
මේ article එකෙන් අපි බලමු කොහොමද Method Naming Conventions හරහා simple queries ලියන්නේ, ඒ වගේම @Query
annotation එක පාවිච්චි කරලා complex queries ලියන්නේ කොහොමද කියලා. ඔයාලගේ Spring Boot project එකට තව ටිකක් power එකක් එකතු කරගන්න ලෑස්ති වෙන්න එහෙනම්!
ඇයි මේ Custom Queries ඕනෙ වෙන්නෙ?
සාමාන්යයෙන් Spring Data JPA කියන්නෙ අපේ ජීවිතේ ලේසි කරන සුපිරි ටූල් එකක්. findByProductName()
, findByPriceGreaterThan()
වගේ methods ලිව්වම ඒකට අදාළ SQL query එක ඉබේම හැදෙන එක හරිම ලේසියි. ඒකට අපි කියනවා Derived Queries කියලා. ඒත් සමහර වෙලාවට, අපිට තියෙන සීමා මායිම් ටිකක් තද වෙන්න පුළුවන්.
උදාහරණයක් විදියට, ඔයාට ඕනෙ වෙන්න පුළුවන් Products ටිකක් ගන්න, හැබැයි ඒ Products ටික මිලියන 10,000කට වඩා වැඩි වෙන්නත් ඕනේ, එව්වා active වෙන්නත් ඕනේ, ඒ වගේම ඒ Product එක අයිති වෙන්න ඕනේ 'Electronics' කියන category එකට විතරයි නම්? මේ වගේ වෙලාවට සාමාන්යයෙන් Spring Data JPA අපිට automatically generate කරලා දෙන methods, එහෙම නැත්තම් Derived Queries, ප්රමාණවත් වෙන්නේ නැහැ.
සමහර complex joins, aggregate functions (SUM
, AVG
, COUNT
වගේ) නැත්නම් GROUP BY
වගේ දේවල් අපිට අවශ්ය වුණාම, අපිට මේ simple methods use කරන්න බැහැ. ඒ වගේම database එකේ තියෙන data වලට අලුත් logic එකක් දාලා, අපි හිතන විදියටම query එක ලියන්න අපිට ඕනෙ වෙනවා. අන්න ඒ වගේ අවස්ථා වලට තමයි අපි Custom Queries පාවිච්චි කරන්නේ.
Method Naming Conventions – මේ "මැජික්" එක කොහොමද?
Spring Data JPA වල තියෙන සුපිරිම features වලින් එකක් තමයි Method Naming Conventions. අපේ Repository interface එකේ අපි දාන method එකේ නමට අනුවම Spring Data JPA එකේ engine එකට පුළුවන් ඒකට අදාළ SQL query එක generate කරන්න. මේක පොඩි queries වලට නම් හරිම ලේසියි.
මේකේදී Spring Data JPA එක බලන්නේ අපේ method එකේ නම පටන් ගන්න හැටි (find...By
, read...By
, count...By
වගේ) සහ By
කියන keyword එකෙන් පස්සේ එන entity attributes (properties) මොනවද කියලා. ඊට පස්සේ ඒවා AND
, OR
, LessThan
, GreaterThan
වගේ keywords වලින් combine කරන හැටිත් එයාට තේරෙනවා.
අපි හිතමු අපිට Product
කියලා entity එකක් තියෙනවා කියලා මෙහෙම:
@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private double price;
private boolean active;
private String category;
// Getters and Setters
}
මේකට අදාළ Repository එක මෙහෙම වෙන්න පුළුවන්:
public interface ProductRepository extends JpaRepository<Product, Long> {
// Product name එකෙන් හොයන්න
List<Product> findByName(String name);
// Active products හොයන්න
List<Product> findByActiveTrue();
// Category එකට අයිති active products හොයන්න
List<Product> findByCategoryAndActiveTrue(String category);
// Price එකට වඩා වැඩි products හොයන්න
List<Product> findByPriceGreaterThan(double price);
// Product name එකෙන් හොයලා ඒක count කරන්න
long countByName(String name);
// Product name එකෙන් හොයලා case-insensitive විදියට
List<Product> findByNameIgnoreCase(String name);
}
දැක්කනේ වැඩේ කොච්චර ලේසිද කියලා? අපි දාන method name එකට අනුව Spring Data JPA එකෙන් JPQL query එකක් හදලා, ඒක execute කරලා අපිට result එක දෙනවා. හැබැයි, මේක හැමවෙලාවෙම ප්රමාණවත් වෙන්නේ නැහැ. උදාහරණයක් විදියට, Complex Joins, Subqueries, හෝ aggregate functions පාවිච්චි කරන්න අපිට මේ Method Naming Conventions ප්රමාණවත් වෙන්නේ නැහැ. අන්න එතකොට තමයි අපේ Power Tool එක එලියට එන්නේ.
@Query Annotation – අපේ "Power Tool" එක!
ඔයාට තියෙන්නේ ටිකක් සංකීර්ණ query එකක් නම්, එහෙමත් නැත්නම් database එකට direct SQL query එකක් ලියන්න ඕනේ නම්, @Query
annotation එක තමයි හොඳම විසඳුම. මේකෙන් අපිට පුළුවන් JPQL (Java Persistence Query Language) හෝ native SQL queries ලියන්න.
JPQL with @Query
JPQL කියන්නේ SQL වගේම Query Language එකක්, හැබැයි මේක database tables එක්ක නෙමෙයි, අපේ Java Entities එක්ක වැඩ කරන්නේ. මේක object-oriented query language එකක්.
public interface ProductRepository extends JpaRepository<Product, Long> {
// මිලියන 10000ට වඩා වැඩි active products හොයන්න
@Query("SELECT p FROM Product p WHERE p.price > :minPrice AND p.active = true")
List<Product> findExpensiveActiveProducts(@Param("minPrice") double minPrice);
// නමයි, category එකයි දෙකම match වෙන products හොයන්න
@Query("SELECT p FROM Product p WHERE p.name = :productName AND p.category = :productCategory")
List<Product> findByNameAndCategory(@Param("productName") String name, @Param("productCategory") String category);
// Product name එකේ 'TV' වගේ substring එකක් තියෙන products හොයන්න
@Query("SELECT p FROM Product p WHERE p.name LIKE %:keyword%")
List<Product> findByKeywordInName(@Param("keyword") String keyword);
}
මෙහිදී :minPrice
, :productName
, :productCategory
, :keyword
වගේ ඒවා තමයි parameters. ඒවාට අදාළ values අපි method එකේ arguments වලින් @Param
annotation එක පාවිච්චි කරලා provide කරනවා.
Native SQL with @Query
සමහර වෙලාවට අපිට JPQL වලින් කරන්න බැරි දෙයක්, නැත්නම් database-specific features පාවිච්චි කරන්න ඕනේ නම්, අපිට පුළුවන් native SQL queries ලියන්න. ඒකට nativeQuery = true
කියලා දාන්න ඕනේ.
public interface ProductRepository extends JpaRepository<Product, Long> {
// database එකේ තියෙන specific function එකක් පාවිච්චි කරලා product count එක ගන්න
@Query(value = "SELECT count(*) FROM products WHERE active = true", nativeQuery = true)
long countActiveProductsNative();
// Complex join එකක් තියෙන products හොයන්න (උදාහරණයක් විදියට Product එකට අදාළ Order Details ගන්න)
@Query(value = "SELECT p.* FROM products p JOIN order_items oi ON p.id = oi.product_id WHERE oi.quantity > ?1", nativeQuery = true)
List<Product> findProductsByMinOrderQuantityNative(int minQuantity);
}
Native queries වලදී parameters specify කරන්න පුළුවන් ?1
, ?2
වගේ positional parameters විදියට. එහෙමත් නැත්නම් JPQL වල වගේම :paramName
විදියටත් පුළුවන්.
Modifying Queries with @Query and @Modifying
SELECT
queries වලට අමතරව, අපිට INSERT
, UPDATE
, DELETE
වගේ operations කරන්නත් @Query
annotation එක පාවිච්චි කරන්න පුළුවන්. ඒකට @Modifying
annotation එකත් එක්ක use කරන්න ඕනේ.
public interface ProductRepository extends JpaRepository<Product, Long> {
// Product එකක මිල update කරන්න
@Modifying
@Query("UPDATE Product p SET p.price = :newPrice WHERE p.id = :productId")
int updateProductPrice(@Param("newPrice") double newPrice, @Param("productId") Long productId);
// Active නැති products delete කරන්න
@Modifying
@Query("DELETE FROM Product p WHERE p.active = false")
int deleteInactiveProducts();
// Native SQL එකෙන් update කරන්න
@Modifying
@Query(value = "UPDATE products SET active = false WHERE price < ?1", nativeQuery = true)
int deactivateProductsCheaperThan(double price);
}
වැදගත්: @Modifying
queries execute කරද්දී Transaction එකක් ඇතුලේ කරන්න ඕනේ. නැත්නම් TransactionRequiredException
එකක් එන්න පුළුවන්. ඒකට Service layer එකේ method එකට @Transactional
annotation එක දාන්න.
@Service
public class ProductService {
@Autowired
private ProductRepository productRepository;
@Transactional
public int updatePriceForProduct(double newPrice, Long productId) {
return productRepository.updateProductPrice(newPrice, productId);
}
@Transactional
public int cleanupInactiveProducts() {
return productRepository.deleteInactiveProducts();
}
}
Practical Tips & Best Practices – ටිකක් පරිස්සමින්!
Custom queries කියන්නේ බලගතු tool එකක් වුණාට, ඒවා පාවිච්චි කරද්දී ටිකක් පරිස්සම් වෙන්න ඕනේ. නැත්නම් ඔයාගේ application එකේ performance issue එන්න පුළුවන්, නැත්නම් maintain කරන්න අමාරු වෙන්න පුළුවන්.
- Readable සහ Maintainable වෙන්න: ඔයා ලියන query එක තව කෙනෙක්ට, එහෙමත් නැත්නම් අනාගතේ ඔයාටම තේරෙන්න ලේසි වෙන්න ඕනේ. Complex queries කොටස් වලට කඩලා ලියන්න පුළුවන් නම් හොඳයි.
- Performance ගැන හිතන්න: Queries ලියද්දී database performance ගැන හිතන්න. අනවශ්ය joins, subqueries minimize කරන්න. N+1 problem එක ගැන හිතන්න. අවශ්ය තැන් වලදී
JOIN FETCH
වගේ දේවල් පාවිච්චි කරන්න (JPQL වලදී). - SQL Injection වලින් පරිස්සම් වෙන්න:
@Query
annotation එක පාවිච්චි කරද්දී, parameters bind කරන්න:paramName
or?
syntax එක පාවිච්චි කරන්න. ඒකෙන් SQL Injection වලින් ඔයාගේ application එක ආරක්ෂා වෙනවා. String concatenation වලින් parameters directly query එකට දාන්න එපා! - Test කරන්න: ඔයා ලියන හැම Custom Query එකක්ම හොඳට Test කරන්න. ඒකට Unit Tests සහ Integration Tests ලියන්න. Spring Boot Test Context එකත් එක්ක Database එකක් setup කරලා Test කරන එක හරිම වැදගත්.
- Derived Queries First: හැමවෙලාවෙම මුලින්ම බලන්න Method Naming Conventions වලින් ඔයාගේ query එක ලියන්න පුළුවන්ද කියලා. ඒක ලේසිම සහ maintain කරන්න ලේසිම ක්රමය. බැරිම තැනට විතරක්
@Query
annotation එකට යන්න.
අවසන් වශයෙන්
ඉතින් යාලුවනේ, ඔයාලට දැන් තේරෙනවා ඇති Custom JPA Queries කියන්නේ අපේ Spring Boot Applications වලට Database interactions වලදී කොච්චර ලොකු Power එකක්ද කියලා. Method Naming Conventions වල සරල බවත්, @Query
annotation එකේ තියෙන බලයත් හරියට පාවිච්චි කරොත් ඔයාලට ඕනම database query එකක් ලියන්න පුළුවන්.
වැදගත්ම දේ තමයි, මේ concepts practice කරන එක. ඔයාලගේ project එකක පොඩි repo එකක් හදලා මේ methods ටික try කරලා බලන්න. එතකොට ඔයාලට මේවා ගැන හොඳ අවබෝධයක් ලැබෙයි.
ඔයාලට මේ ගැන මොනවද හිතෙන්නේ? ඔයාලා මොන වගේ custom queries ද පාවිච්චි කරලා තියෙන්නේ? Comments වලින් ඔයාලගේ අදහස් share කරන්න අමතක කරන්න එපා. මොකක් හරි ප්රශ්නයක් තියෙනවා නම් ඒකත් අහන්න! අපි ඊළඟ article එකෙන් හමුවෙමු, හැමෝටම ජය!