Contents

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <scope>runtime</scope> </dependency> spring.datasource.url=jdbc:postgresql://localhost:5432/mydb spring.datasource.username=user spring.datasource.password=pass spring.jpa.hibernate.ddl-auto=update spring.jpa.show-sql=true

@Entity maps a Java class to a database table. @Id marks the primary key and @GeneratedValue lets the database auto-generate it.

import jakarta.persistence.*; import java.time.LocalDateTime; @Entity @Table(name = "products") public class Product { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false, length = 200) private String name; private Double price; @Column(name = "created_at") private LocalDateTime createdAt; @PrePersist private void prePersist() { createdAt = LocalDateTime.now(); } // Getters and setters public Long getId() { return id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Double getPrice() { return price; } public void setPrice(Double price) { this.price = price; } }

Extend JpaRepository<T, ID> and Spring Data generates the implementation at startup — no SQL required for basic operations.

import org.springframework.data.jpa.repository.JpaRepository; public interface ProductRepository extends JpaRepository<Product, Long> { // Built-in: save, findById, findAll, deleteById, count, existsById, etc. } @Service public class ProductService { private final ProductRepository repo; public ProductService(ProductRepository repo) { this.repo = repo; } public Product create(Product p) { return repo.save(p); } public List<Product> findAll() { return repo.findAll(); } public void delete(Long id) { repo.deleteById(id); } public Product findById(Long id) { return repo.findById(id) .orElseThrow(() -> new ResourceNotFoundException("Product", id)); } }

Spring Data parses method names and generates the JPQL query automatically. Follow the naming convention: findBy[FieldName][Condition].

public interface ProductRepository extends JpaRepository<Product, Long> { List<Product> findByName(String name); List<Product> findByPriceLessThan(Double maxPrice); List<Product> findByNameContainingIgnoreCase(String keyword); Optional<Product> findFirstByOrderByCreatedAtDesc(); boolean existsByName(String name); }

Use @Query for complex queries that are hard to express through method names. You can write JPQL (object-based) or native SQL.

import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.transaction.annotation.Transactional; import java.util.List; public interface ProductRepository extends JpaRepository<Product, Long> { // JPQL @Query("SELECT p FROM Product p WHERE p.price BETWEEN :min AND :max ORDER BY p.price") List<Product> findInPriceRange(@Param("min") double min, @Param("max") double max); // Native SQL @Query(value = "SELECT * FROM products WHERE name ILIKE %:kw%", nativeQuery = true) List<Product> searchNative(@Param("kw") String keyword); // Modifying (UPDATE/DELETE) @Modifying @Transactional @Query("UPDATE Product p SET p.price = p.price * :factor WHERE p.id IN :ids") int bulkUpdatePrice(@Param("factor") double factor, @Param("ids") List<Long> ids); }