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

EJB 3.0 實體 Bean 多表映射實戰:@SecondaryTable 與 @SecondaryTables 詳解

     編輯:Java 程式設計入門

場景動機

實體類常需映射多張資料表:主表儲存核心屬性,擴展表/字典表儲存分類、代碼等共享資料。EJB 3.0 (JPA 1.0) 提供 `@SecondaryTable` / `@SecondaryTables` 實現單一實體跨表映射。

註解定義速查

@Target(TYPE) @Retention(RUNTIME)
public @interface SecondaryTable {
    String name();                    // 表名
    String catalog() default "";      // 目錄
    String schema() default "";       // 模式
    JoinColumn[] join() default {};   // 聯合主鍵欄位映射
    UniqueConstraint[] uniqueConstraints() default {};
}

@Target(TYPE) @Retention(RUNTIME)
public @interface SecondaryTables {
    SecondaryTable[] value() default {};
}

完整實體範例:Student (學生主表 + 性別字典表)

package com.kuaff.ejb3.secondary;

import javax.persistence.*;
import java.io.Serializable;

@Entity
@Table(name = "STUDENT")
@SecondaryTables({
    @SecondaryTable(name = "GENDER",
        join = @JoinColumn(name = "GENDER_ID", referencedColumnName = "ID"))
})
public class Student implements Serializable {
    @Id @GeneratedValue(strategy = GenerationType.AUTO)
    private int id;

    @Embedded
    private Name name; // 嵌入式值物件

    @Column(name = "GRADE")
    private String grade;

    @Column(name = "EMAIL")
    private String email;

    // 映射至 GENDER 表的 gender 欄位
    @Column(name = "GENDER", table = "GENDER")
    private String gender;

    // Getter/Setter 省略
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }

    public Name getName() { return name; }
    public void setName(Name name) { this.name = name; }

    public String getGrade() { return grade; }
    public void setGrade(String grade) { this.grade = grade; }

    public String getEmail() { return email; }
    public void setEmail(String email) { this.email = email; }

    public String getGender() { return gender; }
    public void setGender(String gender) { this.gender = gender; }
}

嵌入式值物件

@Embeddable
public class Name implements Serializable {
    @Column(name = "FIRST")
    private String first;
    @Column(name = "LAST")
    private String last;
    // Getter/Setter 省略
}

Session Facade (DAO) 介面與實現

@Local
public interface StudentDAO {
    Student create(Student s);
    Student findById(int id);
    void update(Student s);
    void delete(int id);
}

@Stateless
public class StudentDAOBean implements StudentDAO {
    @PersistenceContext(unitName = "ejb3PU")
    private EntityManager em;

    public Student create(Student s) { em.persist(s); return s; }
    public Student findById(int id) { return em.find(Student.class, id); }
    public void update(Student s) { em.merge(s); }
    public void delete(int id) { Student s = findById(id); if (s != null) em.remove(s); }
}

關鍵映射細節解析

屬性映射規則
主鍵共享`@SecondaryTable.join` 指定 `GENDER_ID` 對應主表 `ID`,JPA 自動內連結
欄位指定表`@Column(table = "GENDER")` 明確宣告該欄位寫入副表
嵌入物件`@Embedded` + `@AttributeOverrides` 可將 `Name` 的 `first/last` 映射至主表 `FIRST/LAST`
多副表`@SecondaryTables({ @SecondaryTable(...), @SecondaryTable(...) })` 支援任意數量

資料庫結構 (MySQL)

CREATE TABLE STUDENT (
  ID INT AUTO_INCREMENT PRIMARY KEY,
  FIRST VARCHAR(50),
  LAST VARCHAR(50),
  GRADE VARCHAR(20),
  EMAIL VARCHAR(100),
  GENDER_ID INT
) ENGINE=InnoDB;

CREATE TABLE GENDER (
  ID INT PRIMARY KEY,
  GENDER VARCHAR(10) -- 'M', 'F', 'U'
) ENGINE=InnoDB;

ALTER TABLE STUDENT ADD FOREIGN KEY (GENDER_ID) REFERENCES GENDER(ID);

客戶端測試代碼

Properties props = new Properties();
props.put("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory");
props.put("java.naming.provider.url", "jnp://localhost:1099");
InitialContext ctx = new InitialContext(props);

StudentDAO dao = (StudentDAO) ctx.lookup("StudentDAO/local");

Student s = new Student();
s.setName(new Name("John", "Doe"));
s.setGrade("Senior");
s.setEmail("john@example.com");
s.setGender("M"); // 對應 GENDER 表 ID=1
dao.create(s);

Student found = dao.findById(s.getId());
System.out.println("Gender from GENDER table: " + found.getGender());

遷移至現代 JPA (Hibernate/EclipseLink) 注意點

  • 註解包已從 `javax.persistence` 遷移至 `jakarta.persistence` (Jakarta EE 9+)。
  • `@GeneratedValue` 策略建議改用 `IDENTITY` (MySQL) 或 `SEQUENCE` (PostgreSQL/Oracle)。
  • 嵌入式物件推薦使用 `@Embedded` + `@AttributeOverride` 而非舊版 `@Dependent`/`@DependentAttribute` (已廢棄)。
  • 多表映射在極高併發下可能產生鎖爭用,考慮將熱點欄位留在主表,冷門擴展欄位放副表。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved