Как правильно реализовать доменный объект с составным идентификатором в Hibernate? - PullRequest
1 голос
/ 11 сентября 2010

У меня есть следующие доменные объекты:

public class Department  {
     private long departmentId;
}
public class Manager {
     private long managerId;
}    
public class Project  {
     private ProjectId compositeId;
     @ManyToOne
     private Department department;
     @ManyToOne
     private Manager manager;
}
public class ProjectId  {
     private long departmentId;
     private long managerId;
}

Проект идентифицируется составным ключом (DepartmentId, managerId). Вопрос в том, как реализовать Project.setManager (..) или Project.setDepartment (..)? Является ли реализация, перечисленная ниже, лучшей практикой?

public void setManager( Manager manager ) {
     this.manager = manager;
     this.compositeId.setManagerId( manager.getId() );
}

Насколько я понимаю, составной идентификатор должен обновляться всякий раз, когда устанавливается свойство.

Более сложный и связанный с этим вопрос: как реализовать Project.setCompositeId (..)? Project не сможет обновить менеджера свойств или отдел на основе составного идентификатора (long). Перезапись композитного идентификатора без обновления свойств приведет к тому, что Project окажется в несоответствующем состоянии.

1 Ответ

2 голосов
/ 11 сентября 2010

Я предлагаю следующее:

@Entity
@IdClass(ProjectId.class)
public class Project  {
     @Id @Column(name="DEPARTMENT_ID")
     private long departmentId;
     @Id @Column(name="MANAGER_ID")
     private long managerId;

     @ManyToOne
     @PrimaryKeyJoinColumn(name="DEPARTMENT_ID", referencedColumnName="DPT_ID")
     private Department department;
     @ManyToOne
     @PrimaryKeyJoinColumn(name="MANAGER_ID", referencedColumnName="MGR_ID")
     private Manager manager;

     ...
}

Это отображение очень хорошо объяснено в JPA Wikibook :

JPA 1.0 требует, чтобы все @Id mappings be Basic mappings, поэтому, если ваш Id приходит из столбца внешнего ключа через OneToOne или ManyToOne, вы также должны определить отображение Basic @Id для столбца внешнего ключа.Это частично объясняется тем, что Id должен быть простым объектом для идентификации и кэширования, а также для использования в IdClass или EntityManager find() API.

Поскольку теперь у вас есть два сопоставления для одного и того же столбца внешнего ключа, вы должны определить, какое из них будет записано в базу данных (оно должно быть базовым), поэтому внешний ключ OneToOne или ManyToOne должен быть определен для чтения.только.Это можно сделать, установив для атрибутов JoinColumn insertable и updatable значение false или используя @PrimaryKeyJoinColumn вместо @JoinColumn.

Побочный эффект наличия двух сопоставленийдля того же столбца в том, что теперь вы должны синхронизировать два.Обычно это делается с помощью того, что метод set для атрибута OneToOne также устанавливает значение атрибута Basic в качестве идентификатора целевого объекта.Это может стать очень сложным, если первичный ключ целевого объекта - GeneratedValue, в этом случае вы должны убедиться, что идентификатор целевого объекта был назначен до , связывающего два объекта.

(...)

Пример аннотации ManyToOne id

...
@Entity
@IdClass(PhonePK.class)
public class Phone {
    @Id
    @Column(name="OWNER_ID")
    private long ownerId;

    @Id
    private String type;

    @ManyToOne
    @PrimaryKeyJoinColumn(name="OWNER_ID", referencedColumnName="EMP_ID")
    private Employee owner;
    ...

    public void setOwner(Employee owner) {
        this.owner = owner;
        this.ownerId = owner.getId();
    }
    ...
}

Ссылка

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...