在实际的软件开发中,我们常常需要处理复杂的实体关系,尤其是在数据库设计中,复合主键的使用场景非常常见。本文将通过一个具体的例子,展示如何在 Java Persistence API (JPA) 中使用 @EmbeddedId 和 @ManyToOne 注解来实现复合主键,并通过 Hibernate 进行数据持久化和查询。
一、背景与需求
假设我们有一个员工任务管理系统,其中员工(Employee)和任务(Task)是一对多的关系。每个任务都属于一个员工,但一个员工可以有多个任务。为了唯一标识一个任务,我们需要一个复合主键,它由员工的 ID 和任务的 ID 组成。
二、代码实现
@Embeddable
public class CompositeTaskId implements Serializable {
private long employeeKey;
private long taskId;
public CompositeTaskId() {
}
public CompositeTaskId(long employeeId, long taskId) {
this.employeeKey = employeeId;
this.taskId = taskId;
}
// Getter 和 Setter 方法
public long getEmployeeKey() {
return employeeKey;
}
public void setEmployeeKey(long employeeKey) {
this.employeeKey = employeeKey;
}
public long getTaskId() {
return taskId;
}
public void setTaskId(long taskId) {
this.taskId = taskId;
}
}
2. 定义实体类
接下来,我们定义 Employee 和 Task 实体类。Employee 是一个简单的实体,包含员工 ID、姓名和部门。Task 实体使用 @EmbeddedId 注解嵌入复合主键,并通过 @ManyToOne 关联到 Employee 实体。
java复制
@Entity
public class Employee {
@Id
private long employeeId;
private String name;
private String dept;
public Employee() {
}
public Employee(long employeeId, String name, String dept) {
this.employeeId = employeeId;
this.name = name;
this.dept = dept;
}
// Getter 和 Setter 方法
public long getEmployeeId() {
return employeeId;
}
public void setEmployeeId(long employeeId) {
this.employeeId = employeeId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDept() {
return dept;
}
public void setDept(String dept) {
this.dept = dept;
}
}
java复制
@Entity
public class Task {
@EmbeddedId
private CompositeTaskId taskId;
@MapsId("employeeKey")
@ManyToOne
private Employee employee;
private String taskName;
private Date date;
public Task() {
}
public Task(CompositeTaskId taskId, Employee employee) {
this.taskId = taskId;
this.employee = employee;
}
// Getter 和 Setter 方法
public CompositeTaskId getTaskId() {
return taskId;
}
public void setTaskId(CompositeTaskId taskId) {
this.taskId = taskId;
}
public Employee getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
this.employee = employee;
}
public String getTaskName() {
return taskName;
}
public void setTaskName(String taskName) {
this.taskName = taskName;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}
3. 数据持久化与查询
我们通过 Hibernate 的 EntityManager 来实现数据的持久化和查询。以下是一个简单的主类,展示了如何插入数据并查询数据。
java复制
public class ExampleMain {
public static void main(String[] args) throws Exception {
EntityManagerFactory emf =
Persistence.createEntityManagerFactory(“example-unit”);
try {
persistEntity(emf);
runNativeQuery(emf);
findEntityById(emf);
} finally {
emf.close();
}
}
private static void persistEntity(EntityManagerFactory emf) throws Exception {
System.out.println("-- Persisting entity --");
EntityManager em = emf.createEntityManager();
Employee e = new Employee(1L, "Mike", "IT");
CompositeTaskId cti = new CompositeTaskId(1L, 100L);
Task task = new Task(cti, e);
task.setTaskName("Coding");
task.setDate(new Date());
CompositeTaskId cti2 = new CompositeTaskId(1L, 200L);
Task task2 = new Task(cti2, e);
task2.setTaskName("Refactoring");
task2.setDate(new Date());
em.getTransaction().begin();
em.persist(e);
em.persist(task);
em.persist(task2);
em.getTransaction().commit();
em.close();
}
private static void runNativeQuery(EntityManagerFactory emf) {
System.out.println("-- Native query --");
EntityManager em = emf.createEntityManager();
nativeQuery(em, "Select * from EMPLOYEE");
nativeQuery(em, "Select * from Task");
}
private static void findEntityById(EntityManagerFactory emf) {
System.out.println("-- Finding entity --");
EntityManager em = emf.createEntityManager();
CompositeTaskId taskId = new CompositeTaskId(1, 100);
Task task = em.find(Task.class, taskId);
System.out.println(task);
em.close();
}
public static void nativeQuery(EntityManager em, String s) {
System.out.printf("'%s'%n", s);
Query query = em.createNativeQuery(s);
List list = query.getResultList();
for (Object o : list) {
if (o instanceof Object[]) {
System.out.println(Arrays.toString((Object[]) o));
} else {
System.out.println(o);
}
}
}
}
三、运行结果
运行上述代码后,我们可以看到以下输出:
复制
– Persisting entity –
– Native query –
‘Select * from EMPLOYEE’
[1, IT, Mike]
‘Select * from Task’
[100, 2024-01-24 15:59:22.392, Coding, 1]
[200, 2024-01-24 15:59:22.392, Refactoring, 1]
– Finding entity –
Task{taskId=CompositeTaskId{employeeKey=1, taskId=100}, employee=Employee{employeeId=1, name=‘Mike’, dept=‘IT’}, taskName=‘Coding’, date=2024-01-24 15:59:22.392}
四、总结
本文通过一个具体的例子,展示了如何在 JPA 中使用 @EmbeddedId 和 @ManyToOne 注解来实现复合主键。通过这种方式,我们可以灵活地处理复杂的实体关系,并利用 Hibernate 的强大功能实现数据的持久化和查询。希望这个例子能帮助你更好地理解和应用 JPA 的高级特性。