Spring Boot | Spring Data JPA
Understanding Relationships in JPA: One-to-Many with Simple Primary Key
Discussing “One-to-Many with Simple Primary Key” and examining how JPA/Hibernate generates the corresponding tables
As mentioned in the introductory article of this series, I’ve faced difficulties in implementing JPA relationships correctly in my applications.
To enhance my understanding of them, I developed a GitHub repository called ivangfr/spring-data-jpa-relationships, containing simple examples for each relationship type.
In this series of articles, we will demonstrate each JPA relationship type by presenting the necessary code to map the entities and examining how JPA/Hibernate generates the tables.
I hope you find these articles and the GitHub repository helpful.
Today, we will talk about “One-to-Many with Simple Primary Key”. So, let’s get started!
One-to-Many with Simple Primary Key
In this example, we will associate Restaurant and Dish entities. A restaurant can have zero, one, or more dishes, and a dish can only be present in the menu of one restaurant. Additionally, the Dish entity will have its own auto-increment ID.
Below is the desired database model we aim to achieve.

This is the complete and final code. Next, we will explain it in detail.
@Entity
@Table(name = "restaurants")
public class Restaurant {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
@OneToMany(mappedBy = "restaurant", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Dish> dishes = new LinkedHashSet<>();
@Column(nullable = false)
private String name;
// getters and setters
}
@Entity
@Table(name = "dishes")
public class Dish {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "restaurant_id")
private Restaurant restaurant;
@Column(nullable = false)
private String name;
// getters and setters
}We create two classes, Restaurant and Dish, and use the @Entity annotation to indicate that these classes correspond to database tables.
By default, the table name would be the same as the entity name, but we want them to be in plural form. We can achieve this by using the @Table annotation, where we specify the desired table name. For the Restaurant entity, we use “restaurants”, and for the Dish entity, we use “dishes”.
Next, we add the id field of type Long and the name field of type String to both Restaurant and Dish classes.
To ensure that the name field in the Person and Dish entities cannot have null values, we add the @Column(nullable = false) annotation to it. This annotation tells JPA that the corresponding database column should be defined as NOT NULL.
For theid field in both Restaurant and Dish entities, we add the annotations: @Id and @GeneratedValue(strategy = GenerationType.SEQUENCE). The @Id annotation indicates that the field is the primary key for the entity, while the @GeneratedValue annotation specifies the strategy for generating the ID.
In the Restaurant class, we create a dishes field that contains a Set of Dish objects. In the Dish class, we create a restaurant field that contains a Restaurant object.
To establish the relationship between these entities, we use the @OneToMany annotation on the dishes field in the Restaurant class and the @ManyToOne annotation on the restaurant field in the Dish class.
The mappedBy used in the @OneToMany annotation on the dishes field in the Restaurant class indicates that the relationship between Restaurant and Dish is bidirectional, and that the owning side of the relationship is the Dish entity. The mappedBy attribute specifies the name of the field in the Dish class that owns the relationship, which in this case is the restaurant field.
The cascade = CascadeType.ALL is used to specify that all cascading operations should be applied to the Dish entity when the Restaurant entity is persisted, updated, or deleted. This means that if a Restaurant is deleted, the associated Dish will also be deleted.
The orphanRemoval = true means that when a dish is removed from the Set of dishes in the Restaurant entity, it will also be deleted from the database.
The fetch = FetchType.LAZY parameter, specified in the @ManyToOne annotation of the Dish class, indicates that the associated restaurant should be loaded lazily only when necessary to enhance performance.
We use the @JoinColumn annotation on the restaurant field in the Dish class to specify that this field is the join column for the relationship between the Dish entity and the Restaurant entity. The name attribute in the @JoinColumn annotation specifies the name of the foreign key column in the Dish table. In our example, the foreign key column’s name is restaurant_id.
Lastly, let’s describe the tables in PostgreSQL terminal.
jparelationshipsdb=# \d restaurants
Table "public.restaurants"
Column | Type | Collation | Nullable | Default
--------+------------------------+-----------+----------+---------
id | bigint | | not null |
name | character varying(255) | | not null |
Indexes:
"restaurants_pkey" PRIMARY KEY, btree (id)
Referenced by:
TABLE "dishes" CONSTRAINT "fkpslsa9mci7gsfhwukb3mx7s6n" FOREIGN KEY (restaurant_id) REFERENCES restaurants(id)
jparelationshipsdb=# \d dishes
Table "public.dishes"
Column | Type | Collation | Nullable | Default
---------------+------------------------+-----------+----------+---------
id | bigint | | not null |
name | character varying(255) | | not null |
restaurant_id | bigint | | |
Indexes:
"dishes_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
"fkpslsa9mci7gsfhwukb3mx7s6n" FOREIGN KEY (restaurant_id) REFERENCES restaurants(id)The restaurants and dishes tables both have their own id column as the Primary Key. Additionally, the dishes table has a restaurant_id column which acts as a Foreign Key referencing the id column in the restaurants table.
Conclusion
In this series of articles, we are exploring the four different types of relationships in JPA: one-to-one, one-to-many / many-to-one, and many-to-many. By presenting simple examples for each type of relationship and providing necessary code to map the entities, we hope to help developers to gain a better understanding of the JPA relationships.
Support and Engagement
If you enjoyed this article and would like to show your support, please consider taking the following actions:
- 👏 Engage by clapping, highlighting, and replying to my story. I’ll be happy to answer any of your questions;
- 🌐 Share my story on Social Media;
- 🔔 Follow me on: Medium | LinkedIn | Twitter | GitHub;
- ✉️ Subscribe to my newsletter, so you don’t miss out on my latest posts.





