Spring Data JPA Kapsamlı Kullanım Rehberi
İçindekiler
- Basit bir varlık (entity) oluşturma
- Temel CRUD işlemleri için repository oluşturma
- Özel sorgu metodu ekleme
- Birden fazla koşulla sorgu
- LIKE operatörü kullanma
- Sıralama ile sorgu
- Sayfalandırma
- @Query anotasyonu ile özel SQL sorgusu
- Native SQL sorgusu
- İlişkili varlıklar (One-to-Many)
- Many-to-Many ilişki
- Derived query metodu ile ilişkili varlıkları sorgulama
- @Query ile JOIN kullanımı
- Toplu güncelleme işlemi
- Soft delete işlemi
- Specification kullanımı
- Projection kullanımı
- Auditing özelliğini kullanma
- İndeks oluşturma
- Composite key kullanımı
- @Transactional kullanımı
- Criteria API kullanımı
- Inheritance mapping (Single Table)
- Composite primary key with @IdClass
- @NamedQuery kullanımı
- @Converter ile özel tip dönüşümü
- @Formula kullanımı
- Dinamik native query
- @Version ile optimistic locking
- Custom repository implementasyonu
- @Embeddable ve @Embedded kullanımı
- @ElementCollection ile basit koleksiyonların yönetimi
- @EntityGraph ile fetch stratejisini özelleştirme
- @Lob ile büyük nesnelerin yönetimi
- @SQLDelete ve @Where ile soft delete implementasyonu
- @Filter ile dinamik filtreleme
- @JsonIgnore ile sonsuz döngüleri önleme
- Specification ile dinamik sorgu oluşturma
- @QueryHints ile cache kontrolü
- Stored procedure çağırma
Senaryo 1: Basit bir varlık (entity) oluşturma
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
// Getter ve setter metodları
}
Senaryo 2: Temel CRUD işlemleri için repository oluşturma
public interface UserRepository extends JpaRepository<User, Long> {
}
Senaryo 3: Özel sorgu metodu ekleme
public interface UserRepository extends JpaRepository<User, Long> {
List<User> findByName(String name);
}
Senaryo 4: Birden fazla koşulla sorgu
public interface UserRepository extends JpaRepository<User, Long> {
List<User> findByNameAndEmail(String name, String email);
}
Senaryo 5: LIKE operatörü kullanma
public interface UserRepository extends JpaRepository<User, Long> {
List<User> findByNameLike(String namePattern);
}
Senaryo 6: Sıralama ile sorgu
public interface UserRepository extends JpaRepository<User, Long> {
List<User> findByNameOrderByEmailDesc(String name);
}
Senaryo 7: Sayfalandırma
public interface UserRepository extends JpaRepository<User, Long> {
Page<User> findAll(Pageable pageable);
}
Senaryo 8: @Query anotasyonu ile özel SQL sorgusu
public interface UserRepository extends JpaRepository<User, Long> {
@Query("SELECT u FROM User u WHERE u.email LIKE %:domain")
List<User> findUsersWithEmailDomain(@Param("domain") String domain);
}
Senaryo 9: Native SQL sorgusu
public interface UserRepository extends JpaRepository<User, Long> {
@Query(value = "SELECT * FROM users WHERE name = ?1", nativeQuery = true)
List<User> findUsersByNameNative(String name);
}
Senaryo 10: İlişkili varlıklar (One-to-Many)
@Entity
public class Department {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "department")
private List<User> users;
// Getter ve setter metodları
}
@Entity
public class User {
// ... diğer alanlar
@ManyToOne
@JoinColumn(name = "department_id")
private Department department;
// Getter ve setter metodları
}
Senaryo 11: Many-to-Many ilişki
@Entity
public class Project {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToMany(mappedBy = "projects")
private Set<User> users = new HashSet<>();
// Getter ve setter metodları
}
@Entity
public class User {
// ... diğer alanlar
@ManyToMany
@JoinTable(
name = "user_project",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "project_id")
)
private Set<Project> projects = new HashSet<>();
// Getter ve setter metodları
}
Senaryo 12: Derived query metodu ile ilişkili varlıkları sorgulama
public interface UserRepository extends JpaRepository<User, Long> {
List<User> findByProjectsName(String projectName);
}
Senaryo 13: @Query ile JOIN kullanımı
public interface UserRepository extends JpaRepository<User, Long> {
@Query("SELECT u FROM User u JOIN u.department d WHERE d.name = :deptName")
List<User> findUsersByDepartmentName(@Param("deptName") String departmentName);
}
Senaryo 14: Toplu güncelleme işlemi
public interface UserRepository extends JpaRepository<User, Long> {
@Modifying
@Query("UPDATE User u SET u.email = :newEmail WHERE u.id = :userId")
int updateUserEmail(@Param("userId") Long userId, @Param("newEmail") String newEmail);
}
Senaryo 15: Soft delete işlemi
@Entity
public class User {
// ... diğer alanlar
private boolean isDeleted = false;
}
public interface UserRepository extends JpaRepository<User, Long> {
List<User> findByIsDeletedFalse();
}
Senaryo 16: Specification kullanımı
public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {
}
public class UserSpecifications {
public static Specification<User> hasNameLike(String name) {
return (root, query, cb) -> cb.like(root.get("name"), "%" + name + "%");
}
public static Specification<User> isInDepartment(String departmentName) {
return (root, query, cb) -> cb.equal(root.get("department").get("name"), departmentName);
}
}
// Kullanımı:
// userRepository.findAll(Specification.where(hasNameLike("John")).and(isInDepartment("IT")));
Senaryo 17: Projection kullanımı
public interface UserNameProjection {
String getName();
String getEmail();
}
public interface UserRepository extends JpaRepository<User, Long> {
List<UserNameProjection> findByDepartmentName(String departmentName);
}
Senaryo 18: Auditing özelliğini kullanma (devam)
@Entity
public class User extends Auditable {
// ... diğer alanlar
}
Senaryo 19: İndeks oluşturma
@Entity
@Table(indexes = @Index(columnList = "email"))
public class User {
// ... diğer alanlar
}
Senaryo 20: Composite key kullanımı
@Entity
public class BookAuthor {
@EmbeddedId
private BookAuthorId id;
// Diğer alanlar ve metodlar
}
@Embeddable
public class BookAuthorId implements Serializable {
private Long bookId;
private Long authorId;
// Constructors, equals, hashCode metodları
}
Senaryo 21: @Transactional kullanımı
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Transactional
public void updateUserAndProject(Long userId, String newEmail, String newProjectName) {
User user = userRepository.findById(userId).orElseThrow(() -> new RuntimeException("User not found"));
user.setEmail(newEmail);
Project newProject = new Project();
newProject.setName(newProjectName);
user.getProjects().add(newProject);
userRepository.save(user);
}
}
Senaryo 22: Criteria API kullanımı
@Repository
public class UserRepositoryImpl implements UserRepositoryCustom {
@PersistenceContext
private EntityManager entityManager;
@Override
public List<User> findUsersByComplexCriteria(String name, Integer minAge, String departmentName) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> user = query.from(User.class);
List<Predicate> predicates = new ArrayList<>();
if (name != null) {
predicates.add(cb.like(user.get("name"), "%" + name + "%"));
}
if (minAge != null) {
predicates.add(cb.greaterThan(user.get("age"), minAge));
}
if (departmentName != null) {
predicates.add(cb.equal(user.get("department").get("name"), departmentName));
}
query.where(predicates.toArray(new Predicate[0]));
return entityManager.createQuery(query).getResultList();
}
}
Senaryo 23: Inheritance mapping (Single Table)
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "employee_type")
public abstract class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// Getter ve setter metodları
}
@Entity
@DiscriminatorValue("MANAGER")
public class Manager extends Employee {
private String department;
// Getter ve setter metodları
}
@Entity
@DiscriminatorValue("DEVELOPER")
public class Developer extends Employee {
private String programmingLanguage;
// Getter ve setter metodları
}
Senaryo 24: Composite primary key with @IdClass
@Entity
@IdClass(OrderItemId.class)
public class OrderItem {
@Id
private Long orderId;
@Id
private Long productId;
private int quantity;
// Getter ve setter metodları
}
public class OrderItemId implements Serializable {
private Long orderId;
private Long productId;
// Constructors, equals, hashCode metodları
}
Senaryo 25: @NamedQuery kullanımı
@Entity
@NamedQuery(name = "User.findByEmailDomain",
query = "SELECT u FROM User u WHERE u.email LIKE :domain")
public class User {
// ... diğer alanlar
}
public interface UserRepository extends JpaRepository<User, Long> {
List<User> findByEmailDomain(@Param("domain") String domain);
}
Senaryo 26: @Converter ile özel tip dönüşümü
@Converter(autoApply = true)
public class BooleanToStringConverter implements AttributeConverter<Boolean, String> {
@Override
public String convertToDatabaseColumn(Boolean attribute) {
return (attribute != null && attribute) ? "Y" : "N";
}
@Override
public Boolean convertToEntityAttribute(String dbData) {
return "Y".equals(dbData);
}
}
@Entity
public class User {
// ... diğer alanlar
@Convert(converter = BooleanToStringConverter.class)
private Boolean active;
}
Senaryo 27: @Formula kullanımı
@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private BigDecimal price;
private BigDecimal taxRate;
@Formula("price * tax_rate")
private BigDecimal totalPrice;
// Getter ve setter metodları
}
Senaryo 28: Dinamik native query
public interface ProductRepository extends JpaRepository<Product, Long> {
@Query(nativeQuery = true)
List<Product> findProductsByDynamicCriteria(@Param("sql") String sql);
}
// Kullanımı:
// String sql = "SELECT * FROM product WHERE category = 'Electronics' AND price < 1000;
// List<Product> products = productRepository.findProductsByDynamicCriteria(sql);
Senaryo 29: @Version ile optimistic locking
@Entity
public class Inventory {
@Id
private Long productId;
private Integer quantity;
@Version
private Integer version;
// Getter ve setter metodları
}
Senaryo 30: Custom repository implementasyonu
public interface UserRepositoryCustom {
List<User> findUsersByComplexCriteria(String name, Integer minAge, String departmentName);
}
public interface UserRepository extends JpaRepository<User, Long>, UserRepositoryCustom {
}
public class UserRepositoryImpl implements UserRepositoryCustom {
@PersistenceContext
private EntityManager entityManager;
@Override
public List<User> findUsersByComplexCriteria(String name, Integer minAge, String departmentName) {
// Criteria API implementasyonu (Senaryo 22'deki gibi)
}
}
Senaryo 31: @Embeddable ve @Embedded kullanımı
@Embeddable
public class Address {
private String street;
private String city;
private String country;
// Getter ve setter metodları
}
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@Embedded
private Address address;
// Getter ve setter metodları
}
Senaryo 32: @ElementCollection ile basit koleksiyonların yönetimi
@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ElementCollection
@CollectionTable(name = "product_category", joinColumns = @JoinColumn(name = "product_id"))
@Column(name = "category")
private Set<String> categories = new HashSet<>();
// Getter ve setter metodları
}
Senaryo 33: @EntityGraph ile fetch stratejisini özelleştirme
@Entity
@NamedEntityGraph(name = "User.withProjects",
attributeNodes = @NamedAttributeNode("projects")
)
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToMany
private Set<Project> projects = new HashSet<>();
// Getter ve setter metodları
}
public interface UserRepository extends JpaRepository<User, Long> {
@EntityGraph(value = "User.withProjects")
List<User> findByName(String name);
}
Senaryo 34: @Lob ile büyük nesnelerin yönetimi
@Entity
public class Document {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@Lob
private byte[] content;
// Getter ve setter metodları
}
Senaryo 35: @SQLDelete ve @Where ile soft delete implementasyonu
@Entity
@SQLDelete(sql = "UPDATE user SET deleted = true WHERE id = ?")
@Where(clause = "deleted = false")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private boolean deleted = false;
// Getter ve setter metodları
}
Senaryo 36: @Filter ile dinamik filtreleme
@Entity
@FilterDef(name = "deletedProductFilter", parameters = @ParamDef(name = "isDeleted", type = "boolean"))
@Filter(name = "deletedProductFilter", condition = "deleted = :isDeleted")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private boolean deleted = false;
// Getter ve setter metodları
}
// Kullanımı:
// Session session = entityManager.unwrap(Session.class);
// session.enableFilter("deletedProductFilter").setParameter("isDeleted", false);
// List<Product> products = productRepository.findAll();
Senaryo 37: @JsonIgnore ile sonsuz döngüleri önleme
@Entity
public class Department {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "department")
@JsonIgnore
private List<User> users;
// Getter ve setter metodları
}
Senaryo 38: Specification ile dinamik sorgu oluşturma
public class UserSpecification {
public static Specification<User> hasNameLike(String name) {
return (root, query, criteriaBuilder) ->
criteriaBuilder.like(root.get("name"), "%" + name + "%");
}
public static Specification<User> isOlderThan(int age) {
return (root, query, criteriaBuilder) ->
criteriaBuilder.greaterThan(root.get("age"), age);
}
}
// Kullanımı:
// userRepository.findAll(Specification.where(hasNameLike("John")).and(isOlderThan(30)));
Senaryo 39: @QueryHints ile cache kontrolü
public interface UserRepository extends JpaRepository<User, Long> {
@QueryHints(value = { @QueryHint(name = "org.hibernate.cacheable", value = "true") })
List<User> findByAgeGreaterThan(int age);
}
Senaryo 40: Stored procedure çağırma
@Entity
public class User {
// ... diğer alanlar
}
public interface UserRepository extends JpaRepository<User, Long> {
@Procedure(name = "calculate_user_score")
Integer calculateUserScore(@Param("userId") Long userId);
}
Önemli Noktalar
- @Embeddable ve @Embedded ile kompleks tiplerin gömülmesi.
- @ElementCollection ile basit koleksiyonların yönetimi.
- @EntityGraph ile ilişkili varlıkların yükleme stratejisini özelleştirme.
- @Lob ile büyük nesnelerin (Large Objects) yönetimi.
- @SQLDelete ve @Where ile soft delete implementasyonu.
- @Filter ile dinamik filtreleme.
- @JsonIgnore ile sonsuz döngüleri önleme (özellikle REST API'lerde faydalı).
- Specification ile dinamik sorgu oluşturma.
- @QueryHints ile cache kontrolü.
- Stored procedure çağırma örneği.