Master Spring Boot 3 Entity Design: Best Practices & Real‑World Examples
This article walks through essential Spring Boot 3 JPA entity design techniques—including inheritance, primary‑key mapping, relationship handling, cascade operations, validation, auditing, DTO projection, and index creation—providing clear code examples and configuration tips for building robust, maintainable backend services.
Environment
Spring Boot 3.4.2
1. Introduction
Effective entity design is crucial for building robust and maintainable applications. Combining JPA with Spring Boot simplifies database operations and enables highly functional applications.
2. Best Practices
2.1 Entity and Inheritance
Mark entity classes with @Entity .
Use @Table when the table name differs from the class name.
Extract common fields into a superclass annotated with @MappedSuperclass .
Example of a base entity:
<code>@MappedSuperclass
public abstract class BaseEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// other common fields
@Temporal(TemporalType.TIMESTAMP)
private Date createTime;
// getters, setters
}
</code>The Employee class extends BaseEntity , inheriting id and createTime :
<code>@Entity
@Table(name = "t_employees")
public class Employee extends BaseEntity {
private String name;
private String address;
// getters, setters
}
</code>Primary Key
Annotate the primary‑key field with @Id .
Specify generation strategy with @GeneratedValue (e.g., GenerationType.IDENTITY , GenerationType.SEQUENCE ).
Relationships
Define associations using @OneToOne , @OneToMany , @ManyToOne , @ManyToMany .
Control loading behavior with the fetch attribute (LAZY or EAGER).
Use mappedBy to designate the owning side in bidirectional relationships.
Example of a department‑employee relationship:
<code>@Entity
@Table(name = "t_department")
public class Department extends BaseEntity {
private String name;
private String code;
@OneToMany(mappedBy = "department")
private List<Employee> employees = new ArrayList<>();
// getters, setters
}
@Entity
@Table(name = "t_employee")
public class Employee extends BaseEntity {
private String name;
private String address;
@ManyToOne
@JoinColumn(name = "department_id")
private Department department;
// getters, setters
}
</code>Cascade Operations
Specify cascade behavior with the cascade attribute (e.g., CascadeType.ALL , CascadeType.PERSIST ).
Be cautious with CascadeType.DELETE to avoid accidental data loss.
Saving a department cascades to its employees:
<code>@Resource
private DepartmentRepository departmentRepository;
@Test
public void testSave() {
Department department = new Department();
department.setCode("S0001");
department.setName("研发部");
Employee e1 = new Employee("张三", "SC");
e1.setDepartment(department);
Employee e2 = new Employee("Pack", "XJ");
e2.setDepartment(department);
department.setEmployees(List.of(e1, e2));
departmentRepository.saveAndFlush(department);
}
</code>Console output:
2.2 Validation
Apply validation annotations such as @NotNull , @Size directly on entity fields.
Combine JPA validation with Spring's @Valid to automatically validate incoming data.
Example with validation constraints:
<code>@Entity
@Table(name = "t_department")
public class Department extends BaseEntity {
@NotEmpty(message = "部门名称不能为空")
@Length(min = 2)
private String name;
@NotEmpty(message = "部门代码不能为空")
private String code;
}
</code>Saving a department without setting name triggers a validation exception:
<code>@Test
public void testSave() {
Department department = new Department();
department.setCode("S0001");
// name not set
departmentRepository.saveAndFlush(department);
}
</code>Enable validation in application.yml (or application.properties ) with:
<code>spring:
jpa:
properties:
hibernate:
'[javax.persistence.validation.mode]': auto
</code>2.3 Auditing
Use @CreatedBy , @CreatedDate , @LastModifiedBy , @LastModifiedDate to track who created or modified an entity.
Apply @EntityListeners(AuditingEntityListener.class) to the base class.
Auditable base entity example:
<code>@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public abstract class AuditableEntity implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Temporal(TemporalType.TIMESTAMP)
private Date createTime = new Date();
@CreatedBy
protected String createdBy;
@CreatedDate
@Column(nullable = false, updatable = false)
protected LocalDateTime createdDate;
@LastModifiedBy
protected String lastModifiedBy;
@LastModifiedDate
protected LocalDateTime lastModifiedDate;
}
</code>Entity extending the auditable base:
<code>@Entity
@Table(name = "t_product")
public class Product extends AuditableEntity {
private String name;
private Double price;
// getters, setters
}
</code>Enable auditing in a configuration class:
<code>@Configuration
@EnableJpaAuditing
public class JpaConfig {
}
</code>Provide the current auditor bean:
<code>@Component
public class SystemAuditorAware implements AuditorAware<String> {
@Override
public Optional<String> getCurrentAuditor() {
return Optional.of("Pack");
}
}
</code>2.4 DTO Projection
Define a projection interface to fetch only required fields:
<code>public interface AuthorProjection {
String getName();
String getSex();
}
</code>Repository query using @Query with aliases:
<code>public interface AuthorRepository extends JpaRepository<Author, Long> {
@Query("select e.name as name, e.sex as sex from Author e")
List<AuthorProjection> queryAuthors();
}
</code>2.5 Indexes
Create database indexes via the indexes attribute of @Table :
<code>@Entity
@Table(name = "t_author", indexes = {@Index(columnList = "name, sex")})
public class Author extends BaseEntity {
// fields
}
</code>Indexes improve query performance, especially for large data sets; choose indexed columns based on your query patterns.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.