8

Hibernate に関連する次の問題についての指針はほとんどありません。これは、親子関係を持つ単一のデータベース テーブルを使用して継承を実装することに関連しています。例えば:

CREATE TABLE Employee (
  empId BIGINT NOT NULL AUTO_INCREMENT,
  empName VARCHAR(100) NOT NULL,
  managerId BIGINT,
  CONSTRAINT pk_employee PRIMARY KEY (empId)
)

ここで、managerId列は null であるか、Employeeテーブルの別の行を指している可能性があります。ビジネス ルールでは、従業員はすべての報告先について知り、上司について知る必要があります。ビジネス ルールでは、行が null managerIdを持つことも許可されます(組織の CEO にはマネージャーがいません)。

この関係を Hibernate でどのようにマッピングすればよいでしょうか。標準の多対 1 の関係はここでは機能しません。特に、対応する「従業員」エンティティ クラスとしてだけでなく、「マネージャー」、「アシスタント マネージャー」、「エンジニア」などの複数のクラスとしてエンティティを実装したい場合は、それぞれ「従業員」スーパー エンティティ クラスから継承します。 、実際にはすべてに適用されない属性を持つエンティティもあります。たとえば、「マネージャー」は特典を取得し、他のエンティティは取得しません (対応するテーブル列はもちろん null を受け入れます)。

サンプルコードをいただければ幸いです (Hibernate 3 アノテーションを使用するつもりです)。

4

3 に答える 3

9

ここでは 2 つの概念を表現しています。

  1. 継承し、継承階層を 1 つのテーブルにマップする必要があります。
  2. 親子関係。

1. を実装するには、クラス階層ごとに Hibernate の単一テーブル戦略を使用する必要があります。

@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(
    name="emptype",
    discriminatorType=DiscriminatorType.STRING
)
public abstract class Employee { ... }

@Entity
@DiscriminatorValue("MGR")
public class Manager extends Employee { ... }

2. を実装するには、 に 2 つの自己参照関連付けを追加する必要がありますEmployee

  • 多くの従業員には 0 人または 1 人のマネージャーがいます (これもEmployee)
  • 1 人の従業員に 0 人または多数の報告先がいる

結果Employeeは次のようになります。

@Entity
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(
    name="emptype",
    discriminatorType=DiscriminatorType.STRING
)
public abstract class Employee { 

    ... 

    private Employee manager;
    private Set<Employee> reportees = new HashSet<Employee>();

    @ManyToOne(optional = true)
    public Employee getManager() {
        return manager;
    }

    @OneToMany
    public Set<Employee> getReportees() {
        return reportees;
    }

    ...
}

これにより、次の表が得られます。

CREATE TABLE EMPLOYEE_EMPLOYEE (
        EMPLOYEE_ID BIGINT NOT NULL,
        REPORTEES_ID BIGINT NOT NULL
    );

CREATE TABLE EMPLOYEE (
        EMPTYPE VARCHAR(31) NOT NULL,
        ID BIGINT NOT NULL,
        NAME VARCHAR(255),
        MANAGER_ID BIGINT
    );

ALTER TABLE EMPLOYEE ADD CONSTRAINT SQL100311183749050 PRIMARY KEY (ID);

ALTER TABLE EMPLOYEE_EMPLOYEE ADD CONSTRAINT SQL100311183356150 PRIMARY KEY (EMPLOYEE_ID, REPORTEES_ID);

ALTER TABLE EMPLOYEE ADD CONSTRAINT FK4AFD4ACE7887BF92 FOREIGN KEY (MANAGER_ID)
    REFERENCES EMPLOYEE (ID);

ALTER TABLE EMPLOYEE_EMPLOYEE ADD CONSTRAINT FKDFD1791F25AA2BE0 FOREIGN KEY (REPORTEES_ID)
    REFERENCES EMPLOYEE (ID);

ALTER TABLE EMPLOYEE_EMPLOYEE ADD CONSTRAINT FKDFD1791F1A4AFCF1 FOREIGN KEY (EMPLOYEE_ID)
    REFERENCES EMPLOYEE (ID);
于 2010-03-11T17:46:34.240 に答える
3

たくさんの人に感謝します。次のように従業員エンティティを作成しました。

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(
    name="EMPLOYEE_TYPE", 
    discriminatorType = DiscriminatorType.STRING
)
@DiscriminatorValue("Employee")
public abstract class Employee {

    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="EMPLOYEE_ID") 
    private Integer empId = null;

    @Column(name="EMPLOYEE_NAME") 
    private String empName = null;

    @Column(name="EMPLOYEE_SECRETARY")
    private String secretary;

    @Column(name="EMPLOYEE_PERKS")
    private int perks;       

    @ManyToOne(targetEntity = Employee.class, optional=true)
@JoinColumn(name="MANAGER_ID", nullable=true)
private Employee manager = null;

    @OneToMany  
    private Set<Employee> reportees = new HashSet<Employee>();

    ...        

    public Set<Employee> getReportees() {
        return reportees;
    } 
}

次に、Manager、CEO、AsstManager など、ボディがなく、Discriminator 列の値だけを持つ他のエンティティ クラスを追加しました。Hibernate にテーブルを作成させることにしました。主なプログラムは次のとおりです。

SessionFactory sessionFactory = HibernateUtil.sessionFactory;
Session session = sessionFactory.openSession();
Transaction newTrans = session.beginTransaction();

CEO empCeo = new CEO();
empCeo.setEmpName("Mr CEO");
empCeo.setSecretary("Ms Lily");

Manager empMgr = new Manager();
empMgr.setEmpName("Mr Manager1");
empMgr.setPerks(1000);
empMgr.setManager(empCeo);

Manager empMgr1 = new Manager();
empMgr1.setEmpName("Mr Manager2");
empMgr1.setPerks(2000);
empMgr1.setManager(empCeo);

AsstManager asstMgr = new AsstManager();
asstMgr.setEmpName("Mr Asst Manager");
asstMgr.setManager(empMgr);

session.save(empCeo);
session.save(empMgr);
session.save(empMgr1);
session.save(asstMgr);
newTrans.commit();

System.out.println("Mr Manager1's manager is : "
        + empMgr.getManager().getEmpName());
System.out.println("CEO's manager is : " + empCeo.getManager());
System.out.println("Asst Manager's manager is : " + asstMgr.getManager());
System.out.println("Persons Reporting to CEO: " + empCeo.getReportees());

session.clear();
session.close();

コードは正常に実行され、Hibernate は FK を格納する列「MANAGER_EMPLOYEE_ID」を独自に作成していました。JoinColumn名を指定して「MANAGER_ID」にしました。Hibernate はテーブル EMPLOYEE_EMPLOYED も作成しますが、データはそこに永続化されていません。

メソッド getReportees() メソッドは null を返しますが、getManager() は期待どおりに正常に動作します。

于 2010-03-12T14:51:27.350 に答える
1

本当に欲しいかどうかはわかりませんが、クラス階層ごとにテーブルが必要だと思います

その場合、各エンティティは次のように DISCRIMINATOR_COLUMN でソートされます

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(
    name="EMPLOYEE_TYPE", 
    discriminatorType = DiscriminatorType.STRING
)
@DiscriminatorValue("EMPLOYEE")
public class Employee {

    @Id @GeneratedValue
    @Column(name="EMPLOYEE_ID") 
    private Integer id = null;

}

そして、その子は次のようにマッピングされます

@Entity
@DiscriminatorValue("MANAGER")
public class Manager extends Employee {

    // Manager properties goes here        
     ...
}

テストするために、次のことをしましょう

SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
Session session = sessionFactory.openSession();

/*
insert 
into
    Employee
    (EMPLOYEE_TYPE) 
values
    ('EMPLOYEE')
*/
session.save(new Employee());

/*
insert 
into
    Employee
    (EMPLOYEE_TYPE) 
values
    ('MANAGER')
*/
session.save(new Manager());

session.clear();
session.close();

ただし、継承の代わりに (InheritanceType.SINGLE_TABLE 戦略を使用する場合、複数のエンティティが同じテーブルを共有しているため、多くの NULL 列が表示されます)、モデルは次のように優れています。

@Entity
public class Employee { 

    private Employee manager;
    private List<Employee> reporteeList = new ArrayList<Employee>();

    /**
    * optional=true
    * because of an Employee could not have a Manager
    * CEO, for instance, do not have a Manager
    */  
    @ManyToOne(optional=true)
    public Employee getManager() {
        return manager;
    }

    @OneToMany
    public List<Employee> getReporteeList() {
        return reporteeList;
    }

}

ニーズを満たす最適なアプローチを自由に選択してください。

よろしく、

于 2010-03-11T16:42:47.290 に答える