Skills101

Advanced Java & Spring Boot · Lesson 7 of 16 · Updated ·

Spring Data JPA and Hibernate — Entities, Repositories and Queries

Writing JDBC for every table (lesson 2) is repetitive. JPA (Jakarta Persistence API) lets you work with Java objects while Hibernate (the JPA implementation Spring Boot uses) writes the SQL. Spring Data JPA goes further: you write an interface and get the implementation for free.

The Course entity class with @Entity and @Id maps to a course table with id, title, fee and seats columns; Hibernate turns save into an INSERT statement

1. An entity

// File: course-api/src/main/java/com/skills101/courses/course/Course.java
package com.skills101.courses.course;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

@Entity
public class Course {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String title;
    private int fee;    // in rupees
    private int seats;

    protected Course() {
        // JPA needs a no-argument constructor
    }

    public Course(String title, int fee, int seats) {
        this.title = title;
        this.fee = fee;
        this.seats = seats;
    }

    public Long getId() { return id; }
    public String getTitle() { return title; }
    public int getFee() { return fee; }
    public int getSeats() { return seats; }

    public void update(String title, int fee, int seats) {
        this.title = title;
        this.fee = fee;
        this.seats = seats;
    }
}
AnnotationMeaning
@Entitythis class is stored in a table (named course)
@Idthe primary key
@GeneratedValue(strategy = IDENTITY)the database generates ids (auto-increment)
@Column(unique = true, nullable = false)column rules (see Student.email)
@Table, @Transient, @Enumeratedtable name/constraints, skip a field, store enums

JPA needs a no-argument constructor (it can be protected) and must be able to read the fields. Notice there's no setter for id — the database owns it.

2. A repository: no implementation needed

// File: course-api/src/main/java/com/skills101/courses/course/CourseRepository.java
package com.skills101.courses.course;

import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

public interface CourseRepository extends JpaRepository<Course, Long> {

    // Spring Data writes the SQL from the method name
    List<Course> findByFeeLessThanEqualOrderByFeeAsc(int maxFee);

    boolean existsByTitleIgnoreCase(String title);

    // or write JPQL yourself (it uses class and field names, not table names)
    @Query("select c from Course c where lower(c.title) like lower(concat('%', :word, '%'))")
    List<Course> search(String word);
}

JpaRepository<Course, Long> already gives you save, findById, findAll, existsById, deleteById, count, paging and sorting.

Derived queries: Spring Data reads the method name and writes the query:

MethodGenerated WHERE
findByTitle(String t)title = ?
findByFeeLessThanEqualOrderByFeeAsc(int max)fee <= ? order by fee asc
existsByTitleIgnoreCase(String t)upper(title) = upper(?)
findByTitleContainingIgnoreCase(String w)upper(title) like %?%
countByCourseId(Long id)count(*) … course_id = ?

For anything complex, write JPQL in @Query — it uses entity and field names (Course, c.title), not table names. Native SQL is possible with nativeQuery = true.

3. Proving it works: a @DataJpaTest

// File: course-api/src/test/java/com/skills101/courses/course/CourseRepositoryTest.java
package com.skills101.courses.course;

import static org.assertj.core.api.Assertions.assertThat;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.data.jpa.test.autoconfigure.DataJpaTest;

// Starts only JPA + an in-memory database; each test is rolled back afterwards
@DataJpaTest
class CourseRepositoryTest {

    @Autowired
    CourseRepository repository;

    @Test
    void derivedQueriesWork() {
        repository.save(new Course("Python Basics", 1999, 50));
        repository.save(new Course("Java Full Stack", 4999, 30));
        repository.save(new Course("Git Basics", 0, 100));

        assertThat(repository.findByFeeLessThanEqualOrderByFeeAsc(2000))
                .extracting(Course::getTitle)
                .containsExactly("Git Basics", "Python Basics");
        assertThat(repository.existsByTitleIgnoreCase("java full stack")).isTrue();
        assertThat(repository.search("basics")).hasSize(2);
    }
}

This test passes in our build: free courses first (Git Basics, then Python Basics at ₹1999), the case-insensitive check finds "java full stack", and the JPQL search finds both "basics" courses.

4. What Hibernate does

  • Generates SQL from entities and method calls (spring.jpa.show-sql=true prints it while learning).
  • Dirty checking: inside a transaction, change a loaded entity's fields and the UPDATE runs automatically at commit — that's why CourseService.update never calls save.
  • First-level cache: loading the same id twice in one transaction hits the database once.
  • Schema generation with ddl-auto (fine for learning; use Flyway/Liquibase migrations for real apps).

5. Entity states

StateMeaning
Transientnew Course(...) — not known to JPA yet
Managedsaved or loaded in the current transaction; changes are tracked
Detachedthe transaction ended; changes are no longer tracked
Removedscheduled for deletion

Common mistakes

  • No @Id → startup error "No identifier specified for entity".
  • Using table/column names in JPQL instead of entity/field names.
  • A typo in a derived method name (findByTitel) → startup error "No property 'titel' found".
  • Making entities final or without a no-arg constructor.

Quick recap

  • @Entity + @Id map a class to a table; Hibernate writes the SQL.
  • JpaRepository gives CRUD; derived query names and @Query (JPQL) give the rest.
  • Inside a transaction, managed entities are saved automatically (dirty checking).
  • Test repositories with @DataJpaTest.

Practice what you learned

📝 Quiz: JPA and Hibernate5 questions · instant answers & explanations · +10 XP · counts towards your free certificateTake the quiz →