Setting Up JPA and Your First Entity
🎧 Focus sounds
Soft beat sounds that many learners like while reading. Pick one, press Play, and keep studying.
Keep the volume low. These sounds are for comfort while studying, not a medical treatment. If you have epilepsy or are sensitive to pulsing sounds, use the background noise only.
In this lesson
Let’s run JPA for real. You’ll use Hibernate 6 as the JPA provider and H2, an in-memory database that needs no installation. Later you can switch to MySQL or PostgreSQL by changing two lines.
Step 1: Maven dependencies#
<dependencies>
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-core</artifactId>
<version>6.5.2.Final</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>2.2.224</version>
</dependency>
</dependencies>
hibernate-core brings in the jakarta.persistence API too.
Step 2: persistence.xml#
Create src/main/resources/META-INF/persistence.xml:
<persistence xmlns="https://jakarta.ee/xml/ns/persistence" version="3.0">
<persistence-unit name="school">
<class>com.vitalstack.Course</class>
<properties>
<property name="jakarta.persistence.jdbc.url" value="jdbc:h2:mem:school;DB_CLOSE_DELAY=-1"/>
<property name="jakarta.persistence.jdbc.user" value="sa"/>
<property name="jakarta.persistence.jdbc.password" value=""/>
<!-- create tables from entities (development only) -->
<property name="jakarta.persistence.schema-generation.database.action" value="drop-and-create"/>
<!-- print the SQL so you can learn from it -->
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.format_sql" value="true"/>
</properties>
</persistence-unit>
</persistence>
A persistence unit is a named configuration: which database, which entity classes, which settings.
Step 3: The entity#
package com.vitalstack;
import jakarta.persistence.*;
@Entity // this class maps to a table
public class Course {
@Id // primary key
@GeneratedValue(strategy = GenerationType.IDENTITY) // auto-increment column
private Long id;
private String title;
private int price;
protected Course() { } // JPA needs a no-argument constructor
public Course(String title, int price) {
this.title = title;
this.price = price;
}
public Long getId() { return id; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public int getPrice() { return price; }
public void setPrice(int price) { this.price = price; }
@Override public String toString() { return "Course{id=" + id + ", title='" + title + "', price=" + price + "}"; }
}
Entity rules:
- Annotated with
@Entityand has an@Id. - Has a public or protected no-argument constructor (JPA creates objects through it).
- Not
final, and persistent fields notfinal(Hibernate may create proxy subclasses). - Use wrapper types like
Longfor ids, so “not saved yet” isnull.
Step 4: Save and load#
package com.vitalstack;
import jakarta.persistence.*;
public class Main {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("school"); // heavy: create once per app
EntityManager em = emf.createEntityManager(); // light: one per unit of work
em.getTransaction().begin();
Course java = new Course("Java", 499);
em.persist(java); // schedule INSERT
em.persist(new Course("Spring Boot", 799));
em.getTransaction().commit(); // SQL is sent here
System.out.println("Saved with id " + java.getId());
em.close();
EntityManager em2 = emf.createEntityManager();
Course found = em2.find(Course.class, java.getId()); // SELECT by primary key
System.out.println(found);
em2.close();
emf.close();
}
}
Console output (formatted SQL shortened):
create table Course (price integer not null, id bigint generated by default as identity, title varchar(255), primary key (id))
insert into Course (price,title,id) values (?,?,default)
insert into Course (price,title,id) values (?,?,default)
Saved with id 1
select c1_0.id,c1_0.price,c1_0.title from Course c1_0 where c1_0.id=?
Course{id=1, title='Java', price=499}
Hibernate created the table, inserted the rows and mapped the result back into an object. You wrote zero SQL.
The main JPA objects#
| Object | What it is | Lifetime |
|---|---|---|
EntityManagerFactory | built from the persistence unit; expensive | one per application |
EntityManager | your workspace to save/find/query entities | one per request / unit of work |
EntityTransaction | begin / commit / rollback | around each set of changes |
Changes (persist, update, remove) must happen inside a transaction. In Spring you’ll simply annotate a method with @Transactional instead of calling begin/commit yourself.
Switching to MySQL#
Add the driver dependency and change the properties:
<property name="jakarta.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/school"/>
<property name="jakarta.persistence.jdbc.user" value="school"/>
<property name="jakarta.persistence.jdbc.password" value="change-me"/>
Hibernate detects the database and generates MySQL-flavoured SQL. Your Java code doesn’t change.
Use drop-and-create (or update) only while learning. In real projects, the schema is managed with migration tools like Flyway or Liquibase, and Hibernate is set to validate or none. Otherwise a deployment could drop your production tables!
Common mistakes#
No Persistence provider for EntityManager named school: persistence.xml isn’t inMETA-INFon the classpath, or the unit name doesn’t match.No default constructor for entity: add a protected no-arg constructor.- Persisting outside a transaction: nothing is saved, or
TransactionRequiredException. - Creating a new
EntityManagerFactoryfor every request (slow and leaks resources).
Exercise#
- Run the example and find the generated
CREATE TABLEandINSERTin the console. - Add a
levelfield (String) and apublishedboolean. Check the new columns. - Save five courses, then load one by id and print it.
- Change a course’s price inside a transaction without calling any save method. Is an UPDATE printed?
Hint for 4
Yes. A managed entity is tracked; at commit, Hibernate compares it with its snapshot and sends an UPDATE for changed fields. This is called dirty checking (lesson 4).
Interview corner#
- What is persistence.xml? The JPA configuration file defining persistence units (data source, entities, provider properties).
- EntityManagerFactory vs EntityManager? EMF is heavyweight, thread-safe, one per app; EM is lightweight, not thread-safe, one per unit of work.
- Why does an entity need a no-arg constructor? The provider instantiates entities by reflection when loading rows.
- **What does
hibernate.hbm2ddl.auto/ schema-generation do?** Generates or updates the schema from entities; use only in development.
Summary#
- Add hibernate-core + a JDBC driver, configure a persistence unit in
META-INF/persistence.xml. @Entity+@Id+ a no-arg constructor makes a class persistent.EntityManagerFactoryonce;EntityManagerper unit of work; changes inside transactions.
Next: control exactly how fields map to columns.
Quick quiz
3 questions. Check what stuck and earn 2 XP for each right answer.
Progress is saved in this browser. Create a free account to keep it on every device.
Get new lessons in your inbox
One email when we publish a new tutorial or guide. No spam, unsubscribe in one click.