JPA Annotations
JPA (Java Persistence API) annotations map Java classes to database tables and define entity behavior.
Identity and Basic Mapping
@Entity and @Table
@Entity // Marks class as JPA entity
@Table(name = "SUPPLIER") // Maps to database table "SUPPLIER"
public class Supplier {
@Id // Primary key field
@Column(name = "ID") // Column name in database
private String id;
}@Column Constraints
@Column(name = "NAME", nullable = false, unique = true)
private String name; // NOT NULL, UNIQUE constraint
@Column(name = "EMAIL", unique = true)
private String email; // UNIQUE constraint (NULL values allowed)
@Column(nullable = false)
private String createdBy; // NOT NULL constraintAttributes: - name - Explicit
column name in database - nullable - Corresponds to
NOT NULL constraint - unique - Enforces UNIQUE
constraint - length - Max length for strings
Timestamps and Audit
Auto-Set Timestamps
@CreationTimestamp // Auto-set on INSERT only
@Column(updatable = false) // Prevent UPDATEs
private LocalDateTime createdAt;
@UpdateTimestamp // Auto-set on INSERT and UPDATE
private LocalDateTime updatedAt;Usage: - @CreationTimestamp -
Set once when record created, never changes -
@UpdateTimestamp - Updated every time record is
modified - updatable = false - Protects field from
being overwritten
Audit Fields
@Column(name = "CREATED_BY", nullable = false, updatable = false)
private String createdBy; // Set from SecurityContext, immutableRelationships
Many-to-One Relationship
@ManyToOne(fetch = FetchType.EAGER) // Multiple items → one supplier
@JoinColumn(name = "SUPPLIER_ID") // Foreign key column
private Supplier supplier;Attributes: -
fetch = FetchType.EAGER - Always load related
supplier with item - fetch = FetchType.LAZY - Load
supplier only when accessed (slower if accessed often) -
@JoinColumn - Specifies foreign key column
One-to-Many Relationship
@OneToMany(mappedBy = "supplier") // Inverse side of @ManyToOne
private List<InventoryItem> items;Attributes: - mappedBy -
References the field on the other side of relationship - Defines
inverse relationship (not stored as column)
Enumerations
Enum Storage Options
// ✅ GOOD - Stores enum name as string ("ADMIN", "USER")
@Enumerated(EnumType.STRING)
@Column(name = "ROLE")
private Role role;
// ❌ BAD - Stores enum ordinal (0, 1, 2...) - breaks if enum order changes
@Enumerated(EnumType.ORDINAL)
@Column(name = "ROLE")
private Role role;Best Practice: Always use
EnumType.STRING for readability and stability.
Optimistic Locking
Version Field
@Version // Automatic version tracking
@Column(name = "VERSION")
private Long version; // Incremented on each updateHow It Works: 1. Hibernate reads current
version from database (e.g., version = 5) 2. Business logic
modifies entity 3. On UPDATE, Hibernate increments version
(version = 6) 4. WHERE clause includes version check:
WHERE id = ? AND version = 5 5. If another thread
updated first, version won’t match 6. Update fails with
OptimisticLockException
Example:
// Thread 1 reads item (version = 5)
InventoryItem item = repository.findById("abc123");
item.setQuantity(100);
// Thread 2 reads same item (version = 5) and updates first
// ...updates item, version becomes 6...
// Thread 1 tries to save - FAILS
repository.save(item); // OptimisticLockException: version mismatch