Skip to main content

Chapter 3: Data and JPA Annotations

1. Entity Annotations​

@Entity​

Marks a class as a JPA entity (database table).

@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private BigDecimal price;
}

@Table​

Customizes the table name and its constraints.

@Entity
@Table(
name = "products",
schema = "ecommerce",
uniqueConstraints = @UniqueConstraint(columnNames = {"sku"}),
indexes = @Index(name = "idx_product_name", columnList = "name")
)
public class Product {
// ...
}

@Id​

Marks the field as a primary key.

@Id
private Long id;

@GeneratedValue​

Primary key generation strategy.

// Auto-increment (PostgreSQL SERIAL, MySQL AUTO_INCREMENT)
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

// Sequence (recommended for PostgreSQL)
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "product_seq")
@SequenceGenerator(name = "product_seq", sequenceName = "product_sequence", allocationSize = 1)
private Long id;

// UUID
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private String id;

// The DB chooses the strategy
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
StrategyDescriptionTypical DB
IDENTITYAuto-incrementMySQL, PostgreSQL
SEQUENCEDB sequencePostgreSQL, Oracle
TABLESequence tableAll
UUIDUUID identifierAll
AUTOAutomatic choiceAll

@Column​

Customizes the mapping of a column.

@Column(name = "product_name", nullable = false, length = 100)
private String name;

@Column(unique = true)
private String sku;

@Column(precision = 10, scale = 2)
private BigDecimal price;

@Column(columnDefinition = "TEXT")
private String description;

@Column(updatable = false) // Cannot be modified after creation
private LocalDateTime createdAt;

@Column(insertable = false) // Not included in INSERT
private LocalDateTime updatedAt;

@Enumerated​

Maps a Java enum to the database.

public enum Role {
USER, ADMIN, MANAGER
}

@Entity
public class User {
@Enumerated(EnumType.STRING) // Stores "USER", "ADMIN" (RECOMMENDED)
private Role role;

@Enumerated(EnumType.ORDINAL) // Stores 0, 1, 2 (DISCOURAGED)
private Role roleOrdinal;
}

@Temporal​

Specifies the date type (for java.util.Date only, unnecessary with java.time).

@Temporal(TemporalType.DATE)       // Date only
private Date birthDate;

@Temporal(TemporalType.TIMESTAMP) // Date + time
private Date createdAt;
tip

With Java 8+, use LocalDate, LocalDateTime, Instant directly. @Temporal is not needed.

@Lob​

Indicates a Large Object field (BLOB/CLOB).

@Lob
private String longDescription; // CLOB (long text)

@Lob
private byte[] profileImage; // BLOB (binary)

@Transient​

Excludes a field from persistence (no DB column).

@Entity
public class User {
private String firstName;
private String lastName;

@Transient
private String fullName; // Calculated, not stored in DB

public String getFullName() {
return firstName + " " + lastName;
}
}

2. Relationship Annotations​

@OneToOne​

One-to-one relationship.

@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;

@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name = "address_id")
private Address address;
}

@Entity
public class Address {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String street;
private String city;

@OneToOne(mappedBy = "address")
private User user;
}

@OneToMany / @ManyToOne​

One-to-many relationship.

@Entity
public class Department {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;

@OneToMany(mappedBy = "department", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Employee> employees = new ArrayList<>();
}

@Entity
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "department_id")
private Department department;
}

@ManyToMany​

Many-to-many relationship.

@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;

@ManyToMany
@JoinTable(
name = "student_course",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id")
)
private Set<Course> courses = new HashSet<>();
}

@Entity
public class Course {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;

@ManyToMany(mappedBy = "courses")
private Set<Student> students = new HashSet<>();
}

Relationship Parameter Summary​

ParameterDescription
mappedByInverse side of the relationship (no FK column)
cascadePropagation of operations (PERSIST, MERGE, REMOVE, ALL)
fetchLAZY (lazy loading) or EAGER (immediate loading)
orphanRemovalAutomatically remove orphans
optionalThe relationship can be null

3. Query Annotations​

@Query​

Defines a custom JPQL or native SQL query.

public interface UserRepository extends JpaRepository<User, Long> {

// JPQL
@Query("SELECT u FROM User u WHERE u.email = :email")
Optional<User> findByEmail(@Param("email") String email);

// JPQL with JOIN
@Query("SELECT u FROM User u JOIN u.orders o WHERE o.total > :amount")
List<User> findUsersWithOrdersAbove(@Param("amount") BigDecimal amount);

// Native SQL query
@Query(value = "SELECT * FROM users WHERE created_at > :date", nativeQuery = true)
List<User> findRecentUsers(@Param("date") LocalDate date);

// Projection (returns part of the data)
@Query("SELECT new com.example.dto.UserSummary(u.id, u.name, u.email) FROM User u")
List<UserSummary> findAllSummaries();
}

@Param​

Names a parameter in a @Query.

@Query("SELECT u FROM User u WHERE u.name = :name AND u.role = :role")
List<User> findByNameAndRole(@Param("name") String name, @Param("role") Role role);

@Modifying​

Indicates a modifying query (UPDATE, DELETE).

@Modifying
@Transactional
@Query("UPDATE User u SET u.active = false WHERE u.lastLogin < :date")
int deactivateInactiveUsers(@Param("date") LocalDateTime date);

@Modifying
@Transactional
@Query("DELETE FROM User u WHERE u.active = false")
void deleteInactiveUsers();

@NamedQuery​

Defines a named query on the entity.

@Entity
@NamedQuery(name = "User.findActive", query = "SELECT u FROM User u WHERE u.active = true")
@NamedQuery(name = "User.countByRole", query = "SELECT COUNT(u) FROM User u WHERE u.role = :role")
public class User {
// ...
}

4. Transaction and Repository Annotations​

@Transactional​

Manages database transactions.

@Service
@Transactional(readOnly = true) // Default read-only
public class OrderService {

public List<Order> findAll() {
return orderRepository.findAll(); // Read-only transaction
}

@Transactional // Write transaction (override readOnly)
public Order createOrder(OrderDto dto) {
Order order = new Order();
// ... creation logic
return orderRepository.save(order);
}

@Transactional(
propagation = Propagation.REQUIRED, // Uses existing transaction or creates one
isolation = Isolation.READ_COMMITTED, // Isolation level
timeout = 30, // Timeout in seconds
rollbackFor = Exception.class // Rollback on any exception
)
public void processPayment(Long orderId) {
// If an exception is thrown, everything is rolled back
}
}
ParameterDescription
readOnlyOptimization for reads
propagationTransaction behavior (REQUIRED, REQUIRES_NEW, etc.)
isolationIsolation level (READ_COMMITTED, SERIALIZABLE, etc.)
timeoutMaximum duration in seconds
rollbackForExceptions that trigger a rollback
noRollbackForExceptions that do NOT trigger a rollback

@EnableJpaRepositories​

Enables Spring Data JPA repositories (usually auto-configured by Spring Boot).

@Configuration
@EnableJpaRepositories(basePackages = "com.example.repository")
public class JpaConfig {
}

@EnableJpaAuditing​

Enables automatic auditing (creation/modification dates).

@Configuration
@EnableJpaAuditing
public class JpaAuditConfig {
}

@Entity
@EntityListeners(AuditingEntityListener.class)
public class Article {

@CreatedDate
@Column(updatable = false)
private LocalDateTime createdAt;

@LastModifiedDate
private LocalDateTime updatedAt;

@CreatedBy
@Column(updatable = false)
private String createdBy;

@LastModifiedBy
private String updatedBy;
}

5. JPA Inheritance Annotations​

@Inheritance​

Inheritance strategy between entities.

// SINGLE_TABLE: all subclasses in a single table (default)
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "type")
public abstract class Payment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private BigDecimal amount;
}

@Entity
@DiscriminatorValue("CARD")
public class CardPayment extends Payment {
private String cardNumber;
}

@Entity
@DiscriminatorValue("PAYPAL")
public class PaypalPayment extends Payment {
private String paypalEmail;
}
StrategyDescriptionPerformance
SINGLE_TABLEOne table for the entire hierarchyFast (no JOIN)
TABLE_PER_CLASSOne table per subclassMedium
JOINEDBase table + subclass tables with JOINNormalized but slower

6. Hibernate Specific Annotations​

@Formula​

SQL-side calculated column (no physical column).

@Formula("(SELECT COUNT(*) FROM orders o WHERE o.user_id = id)")
private int orderCount;

@Where​

Default SQL filter on a collection.

@OneToMany(mappedBy = "user")
@Where(clause = "active = true")
private List<Order> activeOrders;

@BatchSize​

Optimizes collection loading (prevents N+1 problem).

@OneToMany(mappedBy = "department")
@BatchSize(size = 20)
private List<Employee> employees;

Quick Summary​

AnnotationRole
@EntityClass = DB table
@TableTable configuration
@IdPrimary key
@GeneratedValueAuto ID generation
@ColumnColumn configuration
@EnumeratedEnum mapping
@OneToOne1:1 relationship
@OneToMany1:N relationship
@ManyToOneN:1 relationship
@ManyToManyN:N relationship
@JoinColumnJoin column (FK)
@JoinTableJoin table
@QueryCustom query
@ModifyingUPDATE/DELETE query
@TransactionalTransaction management
@TransientNon-persisted field

➡️ Chapter 4: Security, Test, and Advanced Annotations


⬅️ Back to Module 17