Working with Spring Data JPA
1. Configuring Data Source Properties
| Property | Description |
|---|---|
spring.datasource.url |
JDBC URL |
spring.datasource.username/password |
Credentials |
spring.datasource.driver-class-name |
Optional (auto-detect) |
spring.datasource.hikari.maximum-pool-size |
HikariCP max conn |
spring.jpa.hibernate.ddl-auto |
none/validate/update/create/create-drop |
spring.jpa.show-sql |
Log SQL (use logging.level.org.hibernate.SQL=DEBUG in prod) |
spring.jpa.open-in-view |
Set false in services |
2. Creating Entity Classes (@Entity, @Table)
Example: JPA entity with index and audit timestamps
@Entity
@Table(name="orders", indexes=@Index(columnList="customer_id,status"))
public class Order {
@Id @GeneratedValue(strategy=GenerationType.IDENTITY) private Long id;
@Column(nullable=false) private String reference;
@Enumerated(EnumType.STRING) private OrderStatus status;
@Column(name="customer_id") private Long customerId;
@CreationTimestamp private Instant createdAt;
// getters/setters/protected no-arg ctor
}
3. Defining Primary Keys
| Strategy | Behavior |
|---|---|
IDENTITY |
DB autoincrement; one INSERT per save |
SEQUENCE |
Uses DB sequence; supports batching |
TABLE |
Portable but slower |
UUID |
Java/DB-side generated UUID |
AUTO |
Provider-chosen |
4. Mapping Columns (@Column annotations)
| Attribute | Use |
|---|---|
name |
DB column name |
nullable |
Default true |
unique |
Adds unique constraint at DDL |
length |
VARCHAR size |
precision/scale |
Numeric |
insertable/updatable |
Read-only fields |
columnDefinition |
Raw DDL fragment |
5. Defining Relationships
| Annotation | Cardinality | Default Fetch |
|---|---|---|
@OneToOne |
1:1 | EAGER |
@OneToMany |
1:N | LAZY |
@ManyToOne |
N:1 | EAGER (override to LAZY) |
@ManyToMany |
M:N | LAZY |
6. Creating Repository Interfaces
Example: Repository with derived query methods
public interface OrderRepository extends JpaRepository<Order, Long> {
Optional<Order> findByReference(String reference);
List<Order> findByStatusAndCustomerId(OrderStatus s, Long customerId);
long countByStatus(OrderStatus status);
}
| Interface | Provides |
|---|---|
Repository<T,ID> |
Marker only |
CrudRepository |
CRUD |
PagingAndSortingRepository |
+Page/Sort |
JpaRepository |
+flush, batch, getReferenceById |
ListCrudRepository |
List instead of Iterable |
7. Using Derived Query Methods
| Keyword | SQL |
|---|---|
findBy / readBy / queryBy |
SELECT |
findFirst10ByOrderByCreatedAtDesc |
LIMIT + ORDER BY |
findByEmailContainingIgnoreCase |
LIKE |
findByAgeBetween |
BETWEEN |
findByStatusIn |
IN |
existsBy / countBy / deleteBy |
EXISTS / COUNT / DELETE |
8. Writing Custom JPQL Queries (@Query)
Example: Custom JPQL select and update queries
public interface OrderRepository extends JpaRepository<Order, Long> {
@Query("select o from Order o where o.status = :s and o.total > :min")
List<Order> findHighValue(@Param("s") OrderStatus s, @Param("min") BigDecimal min);
@Modifying
@Query("update Order o set o.status='CANCELLED' where o.id = :id")
int cancel(@Param("id") Long id);
}
9. Using Native SQL Queries
Example: Native query with interface projection
@Query(value = "SELECT * FROM orders WHERE created_at > ?1",
nativeQuery = true)
List<Order> recent(Instant since);
// Projection via interface
public interface OrderSummary { String getReference(); BigDecimal getTotal(); }
@Query(value="SELECT reference, total FROM orders", nativeQuery=true)
List<OrderSummary> summaries();
10. Implementing Pagination (Pageable, Page<T>)
Example: Paginate with sort
Page<Order> page = repo.findAll(PageRequest.of(0, 20, Sort.by("createdAt").descending()));
page.getTotalElements();
page.getContent();
| Type | Use |
|---|---|
Page<T> |
Content + total count (extra COUNT query) |
Slice<T> |
Content + hasNext (no count) |
Window<T> |
Keyset pagination (Spring Data 3) |
11. Implementing Sorting
| API | Example |
|---|---|
Sort.by(...) |
Sort.by("name").ascending() |
| Multiple | Sort.by(Order.desc("ts"), Order.asc("id")) |
| Method param | List<X> findAll(Sort sort); |
12. Using Query by Example
Example: Query by example with null ignore
Order probe = new Order();
probe.setStatus(OrderStatus.PENDING);
ExampleMatcher matcher = ExampleMatcher.matchingAll().withIgnoreNullValues();
List<Order> results = repo.findAll(Example.of(probe, matcher));
13. Using Specifications for Dynamic Queries
Example: Dynamic query with Specification
public class OrderSpecs {
public static Specification<Order> status(OrderStatus s) {
return (root, q, cb) -> cb.equal(root.get("status"), s);
}
public static Specification<Order> minTotal(BigDecimal min) {
return (root, q, cb) -> cb.greaterThanOrEqualTo(root.get("total"), min);
}
}
// Usage
repo.findAll(where(status(PENDING)).and(minTotal(BigDecimal.TEN)));
Note: Repository must extend
JpaSpecificationExecutor<T>.