Поддерживает ли Hibernate индивидуальные ассоциации как pkeys? - PullRequest
4 голосов
/ 14 апреля 2010

Может кто-нибудь сказать мне, поддерживает ли Hibernate ассоциации как pkey объекта? Я думал, что это будет поддерживаться, но у меня много проблем с получением любого вида картирования, которое представляет это для работы. В частности, с прямым отображением ниже:

@Entity
public class EntityBar
{
    @Id
    @OneToOne(optional = false, mappedBy = "bar")
    EntityFoo foo

    // other stuff
}

Я получаю исключение org.hibernate.MappingException: «Не удалось определить тип для: EntityFoo, для таблицы: ENTITY_BAR, для столбцов: [org.hibernate.mapping.Column (foo)]»

Погружаясь в код, кажется, что ID всегда считается типом Value; то есть «все, что сохраняется по значению, а не по ссылке. По сути, это Hibernate-тип вместе с нулем или большим количеством столбцов». Я мог бы сделать мой EntityFoo типом значения, объявив его сериализуемым, но я не ожидал, что это также приведет к правильному результату.

Я бы подумал, что Hibernate будет считать тип столбца целочисленным (или каким бы ни был фактический тип идентификатора родителя), так же, как это было бы с обычной ссылкой один на один, но это не так. Похоже, что он начинает действовать, когда я также объявляю его идентификатором. Я иду за пределы того, что возможно, пытаясь объединить @OneToOne с @Id? И если да, то как можно разумно смоделировать эти отношения?

Ответы [ 4 ]

2 голосов
/ 25 апреля 2010

Если цель состоит в том, чтобы иметь общий первичный ключ , что по этому поводу (вдохновленный примером Java Persistence With Hibernate и протестированный в базе данных домашних животных):

@Entity
public class User {
    @Id
    @GeneratedValue
    private Long id;

    @OneToOne(cascade = CascadeType.ALL)
    @PrimaryKeyJoinColumn
    private Address shippingAddress;

    //...
}

Это родительский класс, который вставляется первым и получает сгенерированный идентификатор. Address выглядит так:

@Entity
public class Address implements Serializable {
    @Id @GeneratedValue(generator = "myForeignGenerator")
    @org.hibernate.annotations.GenericGenerator(
        name = "myForeignGenerator",
        strategy = "foreign",
        parameters = @Parameter(name = "property", value = "user")
    )
    @Column(name = "ADDRESS_ID")
    private Long id;

    @OneToOne(mappedBy="shippingAddress")
    @PrimaryKeyJoinColumn
    User user;

    //...
}

С вышеуказанными объектами, кажется, следующее ведет себя как ожидалось:

User newUser = new User();
Address shippingAddress = new Address();
newUser.setShippingAddress(shippingAddress);
shippingAddress.setUser(newUser);            // Bidirectional
session.save(newUser);

При сохранении Address вставляемое значение первичного ключа совпадает со значением первичного ключа экземпляра User, на который ссылается свойство user.

Загрузка User или Address также просто работает.

Дайте мне знать, если я что-то пропустил.


PS: Чтобы строго ответить на вопрос, согласно Первичные ключи через отношения OneToOne :

JPA 1.0 не допускает @Id для OneToOne или ManyToOne, но JPA 2.0 разрешает.

Но , совместимая с JPA 1.0 версия Hibernate

позволяет использовать аннотацию @Id при отображении OneToOne или ManyToOne *.

Я не смог заставить это работать с Hibernate EM 3.4 (он работал с Hibernate EM 3.5.1, то есть реализацией JPA 2.0). Может я что то не так сделал.

В любом случае, использование общего первичного ключа, кажется, обеспечивает правильное решение.

1 голос
/ 22 апреля 2010

Да, это возможно.

Посмотрите на следующий пример, используя класс Driver и DriverId в качестве идентификатора для Driver.

@Entity
public class Drivers {

private DriversId id; //The ID which is located in another class

public Drivers() {
}

@EmbeddedId
@AttributeOverrides({
        @AttributeOverride(name = "personId", column = @Column(name = "person_id", nullable = false))})
@NotNull
public DriversId getId() {
    return this.id;
}
   //rest of class
}

Здесь мы используем personId в качестве идентификатора для драйвера

И класс DriversId:

//composite-id class must implement Serializable
@Embeddable
public class DriversId implements java.io.Serializable {
private static final long serialVersionUID = 462977040679573718L;

private int personId;

public DriversId() {
}

public DriversId(int personId) {
    this.personId = personId;
}

@Column(name = "person_id", nullable = false)
public int getPersonId() {
    return this.personId;
}

public void setPersonId(int personId) {
    this.personId = personId;
}

public boolean equals(Object other) {
    if ((this == other))
        return true;
    if ((other == null))
        return false;
    if (!(other instanceof DriversId))
        return false;
    DriversId castOther = (DriversId) other;

    return (this.getPersonId() == castOther.getPersonId());
}

public int hashCode() {
    int result = 17;

    result = 37 * result + this.getPersonId();
    return result;
}
}
0 голосов
/ 21 апреля 2010

Вы должны использовать @EmbeddedId вместо @Id здесь. И EntityFoo должно быть Embeddable.

Другой способ - это поставить целое число, а OneToOne с updateble и instertable, установленными на false.

0 голосов
/ 19 апреля 2010

Вы можете сделать это, поделив первичный ключ между EntityFoo и EntityBar:

@Entity
public class EntityBar
{
    @Id @OneToOne
    @JoinColumn(name = "foo_id")
    EntityFoo foo;

    // other stuff
}

@Entity
public class EntityFoo
{
    @Id @GeneratedValue
    Integer id;

    // other stuff
}
...