Repository Katmanı
Bu bölümde, e-ticaret uygulaması için repository katmanını oluşturmayı detaylı olarak ele alacağız. Repository katmanı, veritabanı işlemlerini gerçekleştiren ve entity’ler ile veritabanı arasında bir köprü görevi gören katmandır.
Repository Nedir?
Repository, veritabanı işlemlerini soyutlayan ve entity’ler üzerinde CRUD (Create, Read, Update, Delete) işlemlerini gerçekleştiren bir tasarım desenidir. Spring Data JPA, repository desenini uygulamak için hazır arayüzler ve implementasyonlar sağlar.
Spring Data JPA Repository Arayüzleri
Spring Data JPA, repository desenini uygulamak için çeşitli arayüzler sağlar. Bu arayüzler, temel CRUD işlemleri için hazır metodlar içerir ve özel sorgular oluşturmak için bir DSL (Domain Specific Language) sunar.
Repository Arayüzü
Repository
arayüzü, tüm repository arayüzlerinin temel arayüzüdür. Bu arayüz, herhangi bir metod içermez ve sadece bir işaretleyici (marker) arayüzüdür.
public interface Repository<T, ID> {
}
CrudRepository Arayüzü
CrudRepository
arayüzü, temel CRUD işlemleri için metodlar sağlar.
public interface CrudRepository<T, ID> extends Repository<T, ID> {
<S extends T> S save(S entity);
<S extends T> Iterable<S> saveAll(Iterable<S> entities);
Optional<T> findById(ID id);
boolean existsById(ID id);
Iterable<T> findAll();
Iterable<T> findAllById(Iterable<ID> ids);
long count();
void deleteById(ID id);
void delete(T entity);
void deleteAllById(Iterable<? extends ID> ids);
void deleteAll(Iterable<? extends T> entities);
void deleteAll();
}
PagingAndSortingRepository Arayüzü
PagingAndSortingRepository
arayüzü, CrudRepository
arayüzünü genişletir ve sayfalama ve sıralama işlemleri için ek metodlar sağlar.
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {
Iterable<T> findAll(Sort sort);
Page<T> findAll(Pageable pageable);
}
JpaRepository Arayüzü
JpaRepository
arayüzü, PagingAndSortingRepository
arayüzünü genişletir ve JPA’ya özgü ek metodlar sağlar.
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
List<T> findAll();
List<T> findAll(Sort sort);
List<T> findAllById(Iterable<ID> ids);
<S extends T> List<S> saveAll(Iterable<S> entities);
void flush();
<S extends T> S saveAndFlush(S entity);
<S extends T> List<S> saveAllAndFlush(Iterable<S> entities);
void deleteAllInBatch(Iterable<T> entities);
void deleteAllByIdInBatch(Iterable<ID> ids);
void deleteAllInBatch();
T getOne(ID id);
T getById(ID id);
<S extends T> List<S> findAll(Example<S> example);
<S extends T> List<S> findAll(Example<S> example, Sort sort);
}
Repository Sınıfları Oluşturma
E-ticaret uygulaması için repository sınıflarını oluşturalım. Her entity için bir repository sınıfı oluşturacağız.
UserRepository
package com.example.ecommerce.repository;
import com.example.ecommerce.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByUsername(String username);
Optional<User> findByEmail(String email);
boolean existsByUsername(String username);
boolean existsByEmail(String email);
}
Bu repository sınıfı, User
entity’si için temel CRUD işlemlerini sağlar ve kullanıcı adı ve e-posta ile kullanıcı arama işlemleri için özel metodlar içerir.
CategoryRepository
package com.example.ecommerce.repository;
import com.example.ecommerce.model.Category;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
@Repository
public interface CategoryRepository extends JpaRepository<Category, Long> {
Optional<Category> findByName(String name);
List<Category> findByParentIsNull();
List<Category> findByParentId(Long parentId);
@Query("SELECT c FROM Category c WHERE c.parent IS NULL")
List<Category> findAllParentCategories();
@Query("SELECT c FROM Category c WHERE c.parent.id = :parentId")
List<Category> findAllSubcategoriesByParentId(Long parentId);
}
Bu repository sınıfı, Category
entity’si için temel CRUD işlemlerini sağlar ve kategori adı, üst kategorisi olmayan kategoriler ve belirli bir üst kategoriye ait alt kategoriler için özel metodlar içerir.
ProductRepository
package com.example.ecommerce.repository;
import com.example.ecommerce.model.Product;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.math.BigDecimal;
import java.util.List;
import java.util.Optional;
@Repository
public interface ProductRepository extends JpaRepository<Product, Long>, ProductRepositoryCustom {
Optional<Product> findByName(String name);
List<Product> findByCategoryId(Long categoryId);
Page<Product> findByCategoryId(Long categoryId, Pageable pageable);
List<Product> findByPriceBetween(BigDecimal minPrice, BigDecimal maxPrice);
Page<Product> findByPriceBetween(BigDecimal minPrice, BigDecimal maxPrice, Pageable pageable);
@Query("SELECT p FROM Product p WHERE p.name LIKE %:keyword% OR p.description LIKE %:keyword%")
Page<Product> searchProducts(String keyword, Pageable pageable);
@Query("SELECT p FROM Product p WHERE p.category.id = :categoryId AND (p.name LIKE %:keyword% OR p.description LIKE %:keyword%)")
Page<Product> searchProductsByCategory(String keyword, Long categoryId, Pageable pageable);
@Query("SELECT p FROM Product p WHERE p.stockQuantity < :threshold")
List<Product> findProductsWithLowStock(Integer threshold);
}
Bu repository sınıfı, Product
entity’si için temel CRUD işlemlerini sağlar ve ürün adı, kategori, fiyat aralığı, anahtar kelime araması ve düşük stok durumu için özel metodlar içerir.
ProductRepositoryCustom
Özel sorgu metodları için bir custom repository arayüzü oluşturabiliriz.
package com.example.ecommerce.repository;
import com.example.ecommerce.model.Product;
import java.util.List;
import java.util.Map;
public interface ProductRepositoryCustom {
List<Product> findProductsByFilters(Map<String, Object> filters);
List<Product> findProductsByMultipleCategories(List<Long> categoryIds);
}
ProductRepositoryImpl
Custom repository arayüzünün implementasyonu:
package com.example.ecommerce.repository;
import com.example.ecommerce.model.Product;
import org.springframework.stereotype.Repository;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Repository
public class ProductRepositoryImpl implements ProductRepositoryCustom {
@PersistenceContext
private EntityManager entityManager;
@Override
public List<Product> findProductsByFilters(Map<String, Object> filters) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Product> query = cb.createQuery(Product.class);
Root<Product> product = query.from(Product.class);
List<Predicate> predicates = new ArrayList<>();
if (filters.containsKey("categoryId")) {
predicates.add(cb.equal(product.get("category").get("id"), filters.get("categoryId")));
}
if (filters.containsKey("minPrice")) {
predicates.add(cb.greaterThanOrEqualTo(product.get("price"), (BigDecimal) filters.get("minPrice")));
}
if (filters.containsKey("maxPrice")) {
predicates.add(cb.lessThanOrEqualTo(product.get("price"), (BigDecimal) filters.get("maxPrice")));
}
if (filters.containsKey("keyword")) {
String keyword = "%" + filters.get("keyword") + "%";
predicates.add(cb.or(
cb.like(product.get("name"), keyword),
cb.like(product.get("description"), keyword)
));
}
query.select(product).where(predicates.toArray(new Predicate[0]));
return entityManager.createQuery(query).getResultList();
}
@Override
public List<Product> findProductsByMultipleCategories(List<Long> categoryIds) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Product> query = cb.createQuery(Product.class);
Root<Product> product = query.from(Product.class);
query.select(product).where(product.get("category").get("id").in(categoryIds));
return entityManager.createQuery(query).getResultList();
}
}
AddressRepository
package com.example.ecommerce.repository;
import com.example.ecommerce.model.Address;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
@Repository
public interface AddressRepository extends JpaRepository<Address, Long> {
List<Address> findByUserId(Long userId);
Optional<Address> findByUserIdAndIsDefaultTrue(Long userId);
List<Address> findByUserIdAndAddressType(Long userId, String addressType);
}
Bu repository sınıfı, Address
entity’si için temel CRUD işlemlerini sağlar ve kullanıcı ID’si, varsayılan adres ve adres tipi için özel metodlar içerir.
OrderRepository
package com.example.ecommerce.repository;
import com.example.ecommerce.model.Order;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.time.LocalDateTime;
import java.util.List;
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
List<Order> findByUserId(Long userId);
Page<Order> findByUserId(Long userId, Pageable pageable);
List<Order> findByStatus(String status);
Page<Order> findByStatus(String status, Pageable pageable);
List<Order> findByOrderDateBetween(LocalDateTime startDate, LocalDateTime endDate);
@Query("SELECT o FROM Order o WHERE o.user.id = :userId AND o.status = :status")
List<Order> findByUserIdAndStatus(Long userId, String status);
@Query("SELECT o FROM Order o WHERE o.orderDate BETWEEN :startDate AND :endDate AND o.status = :status")
List<Order> findByOrderDateBetweenAndStatus(LocalDateTime startDate, LocalDateTime endDate, String status);
@Query("SELECT SUM(o.totalAmount) FROM Order o WHERE o.status = 'DELIVERED'")
Double getTotalRevenue();
@Query("SELECT SUM(o.totalAmount) FROM Order o WHERE o.orderDate BETWEEN :startDate AND :endDate AND o.status = 'DELIVERED'")
Double getRevenueByDateRange(LocalDateTime startDate, LocalDateTime endDate);
}
Bu repository sınıfı, Order
entity’si için temel CRUD işlemlerini sağlar ve kullanıcı ID’si, sipariş durumu, tarih aralığı ve gelir hesaplama için özel metodlar içerir.
OrderDetailRepository
package com.example.ecommerce.repository;
import com.example.ecommerce.model.OrderDetail;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface OrderDetailRepository extends JpaRepository<OrderDetail, Long> {
List<OrderDetail> findByOrderId(Long orderId);
List<OrderDetail> findByProductId(Long productId);
@Query("SELECT od FROM OrderDetail od WHERE od.order.id = :orderId AND od.product.id = :productId")
List<OrderDetail> findByOrderIdAndProductId(Long orderId, Long productId);
@Query("SELECT od.product.id, SUM(od.quantity) FROM OrderDetail od GROUP BY od.product.id ORDER BY SUM(od.quantity) DESC")
List<Object[]> findMostSoldProducts();
@Query("SELECT od.product.id, SUM(od.quantity) FROM OrderDetail od WHERE od.order.orderDate BETWEEN :startDate AND :endDate GROUP BY od.product.id ORDER BY SUM(od.quantity) DESC")
List<Object[]> findMostSoldProductsByDateRange(java.time.LocalDateTime startDate, java.time.LocalDateTime endDate);
}
Bu repository sınıfı, OrderDetail
entity’si için temel CRUD işlemlerini sağlar ve sipariş ID’si, ürün ID’si ve en çok satılan ürünler için özel metodlar içerir.
PaymentRepository
package com.example.ecommerce.repository;
import com.example.ecommerce.model.Payment;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
@Repository
public interface PaymentRepository extends JpaRepository<Payment, Long> {
Optional<Payment> findByOrderId(Long orderId);
List<Payment> findByStatus(String status);
List<Payment> findByPaymentMethod(String paymentMethod);
List<Payment> findByPaymentDateBetween(LocalDateTime startDate, LocalDateTime endDate);
@Query("SELECT p FROM Payment p WHERE p.order.id = :orderId AND p.status = :status")
Optional<Payment> findByOrderIdAndStatus(Long orderId, String status);
@Query("SELECT p.paymentMethod, COUNT(p) FROM Payment p WHERE p.status = 'COMPLETED' GROUP BY p.paymentMethod")
List<Object[]> countPaymentsByMethod();
@Query("SELECT p.paymentMethod, COUNT(p) FROM Payment p WHERE p.paymentDate BETWEEN :startDate AND :endDate AND p.status = 'COMPLETED' GROUP BY p.paymentMethod")
List<Object[]> countPaymentsByMethodAndDateRange(LocalDateTime startDate, LocalDateTime endDate);
}
Bu repository sınıfı, Payment
entity’si için temel CRUD işlemlerini sağlar ve sipariş ID’si, ödeme durumu, ödeme yöntemi ve ödeme istatistikleri için özel metodlar içerir.
ReviewRepository
package com.example.ecommerce.repository;
import com.example.ecommerce.model.Review;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
@Repository
public interface ReviewRepository extends JpaRepository<Review, Long> {
List<Review> findByProductId(Long productId);
Page<Review> findByProductId(Long productId, Pageable pageable);
List<Review> findByUserId(Long userId);
Page<Review> findByUserId(Long userId, Pageable pageable);
Optional<Review> findByProductIdAndUserId(Long productId, Long userId);
@Query("SELECT AVG(r.rating) FROM Review r WHERE r.product.id = :productId")
Double getAverageRatingByProductId(Long productId);
@Query("SELECT r.rating, COUNT(r) FROM Review r WHERE r.product.id = :productId GROUP BY r.rating ORDER BY r.rating DESC")
List<Object[]> countReviewsByRatingForProduct(Long productId);
@Query("SELECT p.id, AVG(r.rating) FROM Review r JOIN r.product p GROUP BY p.id ORDER BY AVG(r.rating) DESC")
List<Object[]> findProductsWithHighestRating();
}
Bu repository sınıfı, Review
entity’si için temel CRUD işlemlerini sağlar ve ürün ID’si, kullanıcı ID’si, ortalama değerlendirme puanı ve değerlendirme istatistikleri için özel metodlar içerir.
ProductImageRepository
package com.example.ecommerce.repository;
import com.example.ecommerce.model.ProductImage;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
@Repository
public interface ProductImageRepository extends JpaRepository<ProductImage, Long> {
List<ProductImage> findByProductId(Long productId);
Optional<ProductImage> findByProductIdAndIsPrimaryTrue(Long productId);
}
Bu repository sınıfı, ProductImage
entity’si için temel CRUD işlemlerini sağlar ve ürün ID’si ve ana görsel için özel metodlar içerir.
Metod İsimlendirme Kuralları
Spring Data JPA, repository metodlarının isimlerine göre otomatik olarak sorgu oluşturur. Bu, özel sorgular oluşturmak için JPQL veya SQL yazmak zorunda kalmadan, sadece metod isimlerine göre sorgular oluşturmanıza olanak tanır.
Temel Metod İsimlendirme Kuralları
- find…By: Belirli bir kritere göre entity’leri bulmak için kullanılır.
- get…By:
find...By
ile aynıdır.
- read…By:
find...By
ile aynıdır.
- query…By:
find...By
ile aynıdır.
- count…By: Belirli bir kritere göre entity sayısını saymak için kullanılır.
- exists…By: Belirli bir kritere göre entity’nin var olup olmadığını kontrol etmek için kullanılır.
- delete…By: Belirli bir kritere göre entity’leri silmek için kullanılır.
- remove…By:
delete...By
ile aynıdır.
Özel Metod İsimlendirme Örnekleri
- findByName:
name
alanına göre entity’leri bulmak için kullanılır.
- findByNameAndDescription:
name
ve description
alanlarına göre entity’leri bulmak için kullanılır.
- findByNameOrDescription:
name
veya description
alanlarına göre entity’leri bulmak için kullanılır.
- findByPriceBetween:
price
alanının belirli bir aralıkta olduğu entity’leri bulmak için kullanılır.
- findByPriceGreaterThan:
price
alanının belirli bir değerden büyük olduğu entity’leri bulmak için kullanılır.
- findByPriceLessThan:
price
alanının belirli bir değerden küçük olduğu entity’leri bulmak için kullanılır.
- findByNameLike:
name
alanının belirli bir desene uyduğu entity’leri bulmak için kullanılır.
- findByNameStartingWith:
name
alanının belirli bir değerle başladığı entity’leri bulmak için kullanılır.
- findByNameEndingWith:
name
alanının belirli bir değerle bittiği entity’leri bulmak için kullanılır.
- findByNameContaining:
name
alanının belirli bir değeri içerdiği entity’leri bulmak için kullanılır.
- findByOrderByNameAsc: Entity’leri
name
alanına göre artan sırada sıralamak için kullanılır.
- findByOrderByNameDesc: Entity’leri
name
alanına göre azalan sırada sıralamak için kullanılır.
@Query Anotasyonu
Spring Data JPA, metod isimlerine göre otomatik sorgu oluşturmanın yanı sıra, @Query
anotasyonu ile özel JPQL veya SQL sorguları tanımlamanıza da olanak tanır.
JPQL Sorguları
JPQL (Java Persistence Query Language), SQL’e benzer bir sorgu dilidir, ancak veritabanı tablolarını ve sütunlarını değil, entity’leri ve alanlarını hedef alır.
@Query("SELECT p FROM Product p WHERE p.name LIKE %:keyword% OR p.description LIKE %:keyword%")
Page<Product> searchProducts(String keyword, Pageable pageable);
Native SQL Sorguları
Native SQL sorguları, veritabanına özgü SQL dilini kullanmanıza olanak tanır.
@Query(value = "SELECT * FROM products WHERE name LIKE %:keyword% OR description LIKE %:keyword%", nativeQuery = true)
Page<Product> searchProductsNative(String keyword, Pageable pageable);
Named Parameters
@Query
anotasyonu ile tanımlanan sorgularda, :parameterName
sözdizimi ile named parameters kullanabilirsiniz.
@Query("SELECT p FROM Product p WHERE p.category.id = :categoryId AND (p.name LIKE %:keyword% OR p.description LIKE %:keyword%)")
Page<Product> searchProductsByCategory(String keyword, Long categoryId, Pageable pageable);
Positional Parameters
@Query
anotasyonu ile tanımlanan sorgularda, ?1
, ?2
gibi sözdizimi ile positional parameters kullanabilirsiniz.
@Query("SELECT p FROM Product p WHERE p.category.id = ?1 AND (p.name LIKE %?2% OR p.description LIKE %?2%)")
Page<Product> searchProductsByCategory(Long categoryId, String keyword, Pageable pageable);
Sayfalama ve Sıralama
Spring Data JPA, sayfalama ve sıralama işlemleri için Pageable
ve Sort
sınıflarını sağlar.
Sayfalama
Sayfalama işlemleri için, repository metodlarına Pageable
parametresi ekleyebilirsiniz.
Page<Product> findByCategoryId(Long categoryId, Pageable pageable);
Bu metodu kullanırken, PageRequest
sınıfını kullanarak bir Pageable
nesnesi oluşturabilirsiniz:
Pageable pageable = PageRequest.of(0, 10); // Sayfa numarası 0, sayfa boyutu 10
Page<Product> products = productRepository.findByCategoryId(1L, pageable);
Sıralama
Sıralama işlemleri için, repository metodlarına Sort
parametresi ekleyebilirsiniz veya Pageable
nesnesine sıralama bilgisi ekleyebilirsiniz.
List<Product> findByCategoryId(Long categoryId, Sort sort);
Bu metodu kullanırken, Sort
sınıfını kullanarak bir sıralama nesnesi oluşturabilirsiniz:
Sort sort = Sort.by(Sort.Direction.ASC, "name");
List<Product> products = productRepository.findByCategoryId(1L, sort);
Veya Pageable
nesnesine sıralama bilgisi ekleyebilirsiniz:
Pageable pageable = PageRequest.of(0, 10, Sort.by(Sort.Direction.ASC, "name"));
Page<Product> products = productRepository.findByCategoryId(1L, pageable);
Specification API
Spring Data JPA, dinamik sorgular oluşturmak için Specification API’sini sağlar. Bu API, JPA Criteria API’sini kullanarak, çalışma zamanında dinamik olarak sorgular oluşturmanıza olanak tanır.
Specification Arayüzü
Specification
arayüzü, bir sorgu kriterini temsil eder ve toPredicate
metodunu içerir.
public interface Specification<T> {
Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder);
}
JpaSpecificationExecutor Arayüzü
JpaSpecificationExecutor
arayüzü, Specification
nesneleri ile sorgu yapmak için metodlar sağlar.
public interface JpaSpecificationExecutor<T> {
Optional<T> findOne(Specification<T> spec);
List<T> findAll(Specification<T> spec);
Page<T> findAll(Specification<T> spec, Pageable pageable);
List<T> findAll(Specification<T> spec, Sort sort);
long count(Specification<T> spec);
}
ProductRepository ile Specification Kullanımı
package com.example.ecommerce.repository;
import com.example.ecommerce.model.Product;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;
@Repository
public interface ProductRepository extends JpaRepository<Product, Long>, JpaSpecificationExecutor<Product> {
// ...
}
ProductSpecification Sınıfı
package com.example.ecommerce.repository.specification;
import com.example.ecommerce.model.Product;
import org.springframework.data.jpa.domain.Specification;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.math.BigDecimal;
public class ProductSpecification {
public static Specification<Product> hasCategory(Long categoryId) {
return (root, query, criteriaBuilder) -> {
if (categoryId == null) {
return criteriaBuilder.conjunction();
}
return criteriaBuilder.equal(root.get("category").get("id"), categoryId);
};
}
public static Specification<Product> priceBetween(BigDecimal minPrice, BigDecimal maxPrice) {
return (root, query, criteriaBuilder) -> {
if (minPrice == null && maxPrice == null) {
return criteriaBuilder.conjunction();
}
if (minPrice == null) {
return criteriaBuilder.lessThanOrEqualTo(root.get("price"), maxPrice);
}
if (maxPrice == null) {
return criteriaBuilder.greaterThanOrEqualTo(root.get("price"), minPrice);
}
return criteriaBuilder.between(root.get("price"), minPrice, maxPrice);
};
}
public static Specification<Product> nameLike(String keyword) {
return (root, query, criteriaBuilder) -> {
if (keyword == null || keyword.isEmpty()) {
return criteriaBuilder.conjunction();
}
String likePattern = "%" + keyword + "%";
return criteriaBuilder.like(root.get("name"), likePattern);
};
}
public static Specification<Product> descriptionLike(String keyword) {
return (root, query, criteriaBuilder) -> {
if (keyword == null || keyword.isEmpty()) {
return criteriaBuilder.conjunction();
}
String likePattern = "%" + keyword + "%";
return criteriaBuilder.like(root.get("description"), likePattern);
};
}
public static Specification<Product> hasStock() {
return (root, query, criteriaBuilder) -> criteriaBuilder.greaterThan(root.get("stockQuantity"), 0);
}
}
Specification Kullanımı
Specification<Product> spec = Specification.where(ProductSpecification.hasCategory(1L))
.and(ProductSpecification.priceBetween(new BigDecimal("100"), new BigDecimal("1000")))
.and(ProductSpecification.nameLike("iPhone"))
.and(ProductSpecification.hasStock());
List<Product> products = productRepository.findAll(spec);
Sonuç
Bu bölümde, e-ticaret uygulaması için repository katmanını oluşturmayı detaylı olarak ele aldık. Repository katmanı, veritabanı işlemlerini soyutlayan ve entity’ler ile veritabanı arasında bir köprü görevi gören katmandır. Spring Data JPA, repository desenini uygulamak için hazır arayüzler ve implementasyonlar sağlar ve özel sorgular oluşturmak için çeşitli yöntemler sunar.
Bir sonraki bölümde, service katmanını oluşturmayı ele alacağız.