使用@EmbeddedId实现复合主键的优雅方式

在Java的JPA(Java Persistence API)中,复合主键是一个常见的需求,尤其是在处理多对多关系或需要多个字段共同作为主键的场景中。传统上,我们可以通过@IdClass来实现复合主键,但这种方式需要在实体类和主键类中重复定义相同的字段,显得有些冗余。相比之下,@EmbeddedId提供了一种更为简洁和直观的解决方案,它通过对象组合的方式,将复合主键类嵌入到实体类中,避免了字段的重复定义。本文将通过一个具体的实例,详细介绍如何使用@EmbeddedId来实现复合主键。

  1. 定义复合主键类
    首先,我们需要定义一个复合主键类,并使用@Embeddable注解标记。这个类将包含所有作为主键的字段,并且必须实现Serializable接口。以下是一个简单的复合主键类CompositeTaskId的定义:
    java复制
    import java.io.Serializable;
    import java.util.Objects;

@Embeddable
public class CompositeTaskId implements Serializable {
private int employeeId;
private int taskId;

public CompositeTaskId() {
}

public CompositeTaskId(int employeeId, int taskId) {
    this.employeeId = employeeId;
    this.taskId = taskId;
}

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    CompositeTaskId taskId1 = (CompositeTaskId) o;
    if (employeeId != taskId1.employeeId) return false;
    return taskId == taskId1.taskId;
}

@Override
public int hashCode() {
    return Objects.hash(employeeId, taskId);
}

}
在这个类中,employeeId和taskId是作为主键的两个字段。我们还重写了equals和hashCode方法,这是为了确保在使用复合主键时,能够正确地比较和哈希这些字段。
2. 定义实体类
接下来,我们定义一个实体类Task,并使用@EmbeddedId注解将复合主键类嵌入其中。以下是Task类的定义:
java复制
@Entity
public class Task {
@EmbeddedId
private CompositeTaskId taskId;
private String taskName;
private Date date;

public Task() {
}

public Task(CompositeTaskId taskId) {
    this.taskId = taskId;
}

// Getter and Setter methods
public CompositeTaskId getTaskId() {
    return taskId;
}

public void setTaskId(CompositeTaskId taskId) {
    this.taskId = taskId;
}

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;
}

@Override
public String toString() {
    return "Task{" +
            "taskId=" + taskId +
            ", taskName='" + taskName + '\'' +
            ", date=" + date +
            '}';
}

}
在这个类中,taskId字段被标记为@EmbeddedId,表示它是一个复合主键。其他字段如taskName和date则是一些普通的业务字段。
3. 数据库操作示例
为了展示如何使用Task类和CompositeTaskId类进行数据库操作,我们编写了一个简单的主类ExampleMain。以下是一个示例代码,展示了如何插入数据、执行原生SQL查询以及通过复合主键查找实体:
java复制
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.Query;
import java.util.Date;
import java.util.List;

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();
    CompositeTaskId taskId = new CompositeTaskId(5, 10);
    Task task = new Task(taskId);
    task.setTaskName("coding");
    task.setDate(new Date());
    em.getTransaction().begin();
    em.persist(task);
    em.getTransaction().commit();
    em.close();
}

private static void runNativeQuery(EntityManagerFactory emf) {
    System.out.println("-- Native query --");
    EntityManager em = emf.createEntityManager();
    Query query = em.createNativeQuery("Select * from Task");
    List list = query.getResultList();
    for (Object o : list) {
        if (o instanceof Object[]) {
            System.out.println(Arrays.toString((Object[]) o));
        } else {
            System.out.println(o);
        }
    }
    em.close();
}

private static void findEntityById(EntityManagerFactory emf) {
    System.out.println("-- Finding entity --");
    EntityManager em = emf.createEntityManager();
    CompositeTaskId taskId = new CompositeTaskId(5, 10);
    Task task = em.find(Task.class, taskId);
    System.out.println(task);
    em.close();
}

}
输出结果
运行上述代码后,我们可能会得到以下输出:
复制
– Persisting entity –
– Native query –
Select * from Task
[5, 10, 2025-01-23 12:34:56.789, coding]
– Finding entity –
Task{taskId=CompositeTaskId{employeeId=5, taskId=10}, taskName=‘coding’, date=2025-01-23 12:34:56.789}
4. 总结
通过使用@EmbeddedId,我们可以以一种更为简洁和直观的方式实现复合主键,避免了在实体类和主键类中重复定义字段的麻烦。同时,这种方式也更符合面向对象的设计思想,通过对象组合的方式将主键类嵌入到实体类中。在实际开发中,@EmbeddedId是一个非常实用的工具,可以帮助我们更好地管理复杂的数据库关系。
希望本文的介绍和示例能够帮助你更好地理解和使用@EmbeddedId来实现复合主键。

你可能感兴趣的:(java,python,开发语言,个人开发)