@MappedSuperclass
Long id
with @Id
).@CreationTimestamp
for tracking entity creation.@UpdateTimestamp
for tracking entity updates.@Version private int version
for optimistic locking (optional, used to manage concurrent updates).Example:
import jakarta.persistence.Id;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.MappedSuperclass;
import jakarta.persistence.Version;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import java.time.LocalDateTime;
@MappedSuperclass
public abstract class BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@CreationTimestamp
private LocalDateTime createdAt;
@UpdateTimestamp
private LocalDateTime updatedAt;
@Version
private int version; // For optimistic locking
// Getters and Setters (or use Lombok @Getter/@Setter)
}
Category
↔ Product
Category
(one, parent, inverse) ↔ Product
(many, child, owning, contains FK).Restaurant
↔ FoodItem
relationship in the Food Delivery App from previous queries.Category
Entityimport jakarta.persistence.Entity;
import jakarta.persistence.OneToMany;
import jakarta.persistence.CascadeType;
import java.util.ArrayList;
import java.util.List;
import lombok.Getter;
import lombok.Setter;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.ToString;
@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString(exclude = {"products"}, callSuper = true)
public class Category extends BaseEntity {
private String name;
private String description;
@OneToMany(mappedBy = "productCategory", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Product> products = new ArrayList<>();
// Helper methods (per Gavin King’s recommendation)
public void addProduct(Product product) {
products.add(product);
product.setProductCategory(this);
}
public void removeProduct(Product product) {
products.remove(product);
product.setProductCategory(null);
}
}
Product
Entityimport jakarta.persistence.Entity;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.JoinColumn;
import lombok.Getter;
import lombok.Setter;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.ToString;
@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
public class Product extends BaseEntity {
private String name;
private double price;
@ManyToOne
@JoinColumn(name = "category_id", nullable = false)
private Category productCategory;
}
Category
and Product
extend BaseEntity
, inheriting id
, createdAt
, updatedAt
, and version
.Category
is the inverse (non-owning) side, using mappedBy = "productCategory"
.Product
is the owning side, containing the foreign key (category_id
).cascade = CascadeType.ALL
propagates operations (e.g., persist, merge, remove) from Category
to Product
.