Entity Relationship Overview:
Restaurant (1) <--> (*) FoodItem:
restaurant_id
).@OneToMany(mappedBy = "myRestaurant", cascade = CascadeType.ALL)
@ManyToOne @JoinColumn(name = "restaurant_id")
myRestaurant
in FoodItem
) to avoid an unnecessary join table (restaurants_food_items
).Cascading: Propagates operations (e.g., persist, remove) from Restaurant
to FoodItem
using cascade = CascadeType.ALL
.
Unique Constraint:
Ensures uniqueness of item_name
and restaurant_id
combination in the food_items
table.
@Table(name = "food_items", uniqueConstraints =
@UniqueConstraint(columnNames = {"item_name", "restaurant_id"}))
Helper Methods:
Add helper methods in Restaurant
(parent) to manage bidirectional relationships:
public void addFoodItem(FoodItem foodItem) {
foodItems.add(foodItem);
foodItem.setMyRestaurant(this);
}
public void removeFoodItem(FoodItem foodItem) {
foodItems.remove(foodItem);
foodItem.setMyRestaurant(null);
}
Cascading Issue:
session.persist(foodItem)
, Hibernate won't insert records into food_items
unless explicitly persisted.cascade = CascadeType.ALL
on @OneToMany
to propagate operations from Restaurant
to FoodItem
.Deletion:
Restaurant
, first delete associated FoodItem
records (child) to avoid foreign key constraint violations.CascadeType.ALL
) automates this.LazyInitializationException:
@OneToMany
is LAZY
, meaning foodItems
is a proxy and not fetched until accessed within a session.tx.commit
).Eager Fetching:
@OneToMany(mappedBy = "myRestaurant", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<FoodItem> foodItems = new ArrayList<>();
Access within Session:
foodItems.size()
within the session to force fetching.JPQL with JOIN FETCH:
select r from Restaurant r left join fetch r.foodItems where r.name = :nm
Restaurant
and FoodItem
in a single query, avoiding N+1 issue.Unidirectional Many-to-One (Category *<-- Product):
Product:
@ManyToOne
@JoinColumn(name = "category_id", nullable = false)
private Category productCategory;
Category: No reference to Product
(no List<Product>
), making it unidirectional.
Query Example (Display products by category name):
select p from Product p where p.productCategory.name = :nm
Bidirectional One-to-Many (Category 1 <--> * Product):
Category:
@OneToMany(mappedBy = "productCategory", cascade = CascadeType.ALL)
private List<Product> products = new ArrayList<>();
Product:
@ManyToOne
@JoinColumn(name = "category_id", nullable = false)
private Category productCategory;
Query Example (Display products by category name):
select p from Product p where p.productCategory.name = :nm
productCategory
reference in Product
.Delete Product from Category (Bidirectional):
Input: categoryId
, productId
Steps:
Session session = sessionFactory.getCurrentSession();
Category category = session.get(Category.class, categoryId);
Product product = session.get(Product.class, productId);
if (category != null && product != null && product.getProductCategory().getId().equals(categoryId)) {
category.removeProduct(product); // Removes product from category's list and sets FK to null
session.commit();
} else {
throw new IllegalStateException("Invalid category or product, or product not in category");
}
Issue: Foreign key may be set to null, causing an exception if nullable = false
on @JoinColumn
.
Solution: Enable orphanRemoval = true
on @OneToMany
:
@OneToMany(mappedBy = "productCategory", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Product> products = new ArrayList<>();
Product
records when removed from the relationship.Unidirectional One-to-One (User 1 --> 1 Address):
User:
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name = "address_id")
private Address myAddress;
Address: No reference to User
.
Fetch type defaults to EAGER
but can be set to LAZY
.
Unidirectional Many-to-Many (Restaurant *<--> Tag):
Tag:
@ManyToMany
@JoinTable(name = "my_restaurant_tags")
private Set<Restaurant> restaurants = new HashSet<>();
Restaurant: No reference to Tag
.
Recommendation: Use Set
to reduce duplicate queries.
Many-to-Many with Additional Columns:
Order *<--> FoodItem
with additional columns (e.g., quantity).@ManyToOne
relationships with an intermediate entity (OrderLine
):
OrderLine:
@ManyToOne
@JoinColumn(name = "order_id")
private Order order;
@ManyToOne
@JoinColumn(name = "food_item_id")
private FoodItem foodItem;
private int quantity; // Additional column
Value Types:
Embeddable (AdhaarCard):
@Embeddable
public class AdhaarCard {
private String number;
// Other fields
}
@Entity
public class User {
@Embedded
private AdhaarCard adhaarCard;
}
Collection of Basic Types (Hobbies):
@ElementCollection
@CollectionTable(name = "user_hobbies", joinColumns = @JoinColumn(name = "user_id"))
@Column(name = "hobby_name")
private List<String> hobbies = new ArrayList<>();
Input: name
, address
, description
, city
Output: Success message
Implementation:
public String addRestaurant(String name, String address, String description, String city) {
Session session = sessionFactory.getCurrentSession();
Restaurant restaurant = new Restaurant();
restaurant.setName(name);
restaurant.setAddress(address);
restaurant.setDescription(description);
restaurant.setCity(city);
session.persist(restaurant);
session.getTransaction().commit();
return "Restaurant added successfully with ID: " + restaurant.getId();
}