首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

EJB 3.0 關係映射實戰:多對多與一對一關聯的註解驅動實現

     編輯:Java 相關知識

前言:註解驅動的 ORM 新時代

EJB 3.0 引入 JPA(Java Persistence API),以註解替代 XML 描述符,實現領域模型與關係數據庫的聲明式映射。本文通過「學生-老師(多對多)、學生-檔案(一對一)」經典案例,系統講解關聯映射核心註解與完整工程代碼。

領域模型設計

實體關係說明
Student多對多一學生多老師,一老師多學生,需關聯表
Teacher
Student一對一一學生一檔案,雙向導航
Dossier一對一檔案屬於單一學生

核心註解深度解析

1. @ManyToMultiple + @AssociationTable:多對多映射

@ManyToMany(
    cascade = {CascadeType.CREATE, CascadeType.MERGE},
    fetch = FetchType.EAGER,
    isInverse = true  // 標識關係擁有方,由 Teacher 維護關聯表
)
@AssociationTable(
    table = @Table(name = "STUDENT_TEACHER"),
    joinColumns = @JoinColumn(name = "TEACHER_ID"),        // 關聯表中指向 Teacher 的外鍵
    inverseJoinColumns = @JoinColumn(name = "STUDENT_ID")  // 關聯表中指向 Student 的外鍵
)
public Set<Teacher> getTeachers() { ... }

關鍵點:

  • cascade:持久化操作級聯(創建、合併)
  • fetch:EAGER 立即加載 / LAZY 延遲加載
  • isInverse:雙向關係中,僅一方為擁有方(維護關聯表),另一方設 mappedBy 或 isInverse=true
  • @AssociationTable 定義關聯表名、雙向外鍵列

2. @OneToOne + @JoinColumn:一對一映射

@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "DOSSIER_ID")  // Student 表中外鍵列,指向 Dossier 主鍵
public Dossier getDossier() { ... }

雙向導航:Dossier 端同樣定義 @OneToOne(mappedBy="dossier"),實現:

  • Student → getDossier() 獲取檔案
  • Dossier → getStudent() 獲取學生

完整工程代碼結構

com.kuaff.ejb3.relationships
├── Student.java          # 核心實體,雙向關聯樞紐
├── Teacher.java          # 實體,維護多對多擁有方
├── Dossier.java          # 實體,一對一被動端
├── EntityTest.java       # @Remote 業務接口
├── EntityTestBean.java   # @Stateless 會話 Bean 實現
├── Client.java           # 測試客戶端
├── jndi.properties       # JNDI 配置
└── build.xml             # Ant 構建腳本

關鍵代碼精講

Student.java —— 關聯匯聚點

@Entity
@Table(name = "STUDENT")
public class Student implements Serializable {
    @Id(generate = GeneratorType.AUTO)
    private int id;
    private String first, last;
    
    // 一對一:Student 為擁有方,外鍵在 STUDENT 表
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "DOSSIER_ID")
    private Dossier dossier;
    
    // 多對多:非擁有方,isInverse=true
    @ManyToMany(cascade = {CascadeType.CREATE, CascadeType.MERGE},
                fetch = FetchType.EAGER, isInverse = true)
    @AssociationTable(table = @Table(name = "STUDENT_TEACHER"),
        joinColumns = @JoinColumn(name = "TEACHER_ID"),
        inverseJoinColumns = @JoinColumn(name = "STUDENT_ID"))
    private Set<Teacher> teachers;
    // getter/setter...
}

Teacher.java —— 多對多擁有方

@Entity
public class Teacher implements Serializable {
    @Id(generate = GeneratorType.IDENTITY)
    private Long id;
    private String name, info;
    
    // 擁有方:mappedBy 指向 Student 端屬性名,或配置 @AssociationTable
    @ManyToMany(cascade = {CascadeType.CREATE, CascadeType.MERGE},
                fetch = FetchType.EAGER)
    @AssociationTable(table = @Table(name = "STUDENT_TEACHER"),
        joinColumns = @JoinColumn(name = "TEACHER_ID", referencedColumnName = "ID"),
        inverseJoinColumns = @JoinColumn(name = "STUDENT_ID", referencedColumnName = "ID"))
    private Set<Student> students;
    // getter/setter...
}

EntityTestBean.java —— 業務邏輯與測試數據構建

@Stateless
public class EntityTestBean implements EntityTest {
    @Inject EntityManager manager;  // 容器注入持久化上下文
    
    public void createData() {
        // 構建對象圖:Teacher ←→ Students、Student ←→ Dossier
        Teacher t1 = new Teacher(); t1.setName("hushisheng"); ...
        Teacher t2 = new Teacher(); t2.setName("liyongchi"); ...
        
        Student s1 = new Student(); s1.setFirst("晁"); s1.setLast("岳攀");
        Dossier d1 = new Dossier(); d1.setResume("這是晁岳攀的檔案");
        s1.setDossier(d1);
        // ... s2, s3, d2, d3
        
        t1.setStudents(new HashSet<>(Arrays.asList(s1, s2)));
        t2.setStudents(new HashSet<>(Collections.singletonList(s3)));
        
        manager.persist(t1); manager.persist(t2); // 級聯持久化所有關聯對象
    }
    
    public List<Teacher> findByName(String name) {
        return manager.createQuery(
            "FROM Teacher t WHERE t.name = :name")
            .setParameter("name", name).getResultList();
    }
}

關鍵最佳實踐總結

  1. 擁有方原則:雙向關係必須指定唯一擁有方(多對多用 mappedBy/isInverse,一對一用 @JoinColumn 所在端)
  2. 級聯策略:CascadeType.ALL 謹慎用於多對多(刪除教師會刪除所有關聯學生?通常不合理),建議 PERSIST, MERGE
  3. 抓取策略:集合關聯強烈建議 LAZY,避免載入整張關聯圖;單值關聯(@OneToOne、@ManyToOne)視業務決定
  4. 關聯表定義:@AssociationTable(JBoss/Hibernate 擴展)或標準 JPA @JoinTable,明確表名、列名、引用列
  5. 雙向同步:業務代碼中雙向設置關聯(student.setDossier(dossier); dossier.setStudent(student)),保證對象圖一致性

總結

EJB 3.0 / JPA 以註解聲明式映射徹底簡化了關係建模。掌握擁有方、級聯、抓取、關聯表四大核心概念,配合EntityManager 管理對象圖生命週期,即可優雅實現複雜領域模型持久化。代碼即文檔,模型即資產。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved