使用 @EmbeddedId 和 @ManyToOne 实现复合主键的 JPA 实践

在实际的软件开发中,我们常常需要处理复杂的实体关系,尤其是在数据库设计中,复合主键的使用场景非常常见。本文将通过一个具体的例子,展示如何在 Java Persistence API (JPA) 中使用 @EmbeddedId 和 @ManyToOne 注解来实现复合主键,并通过 Hibernate 进行数据持久化和查询。
一、背景与需求
假设我们有一个员工任务管理系统,其中员工(Employee)和任务(Task)是一对多的关系。每个任务都属于一个员工,但一个员工可以有多个任务。为了唯一标识一个任务,我们需要一个复合主键,它由员工的 ID 和任务的 ID 组成。
二、代码实现

  1. 定义嵌入式主键类
    在 JPA 中,@Embeddable 注解用于定义一个可以嵌入到其他实体中的类。我们定义一个 CompositeTaskId 类,它包含员工 ID 和任务 ID 作为复合主键。
    java复制
    import java.io.Serializable;

@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 的高级特性。

你可能感兴趣的:(个人开发)