JPA Несколько встроенных полей

84

Возможно ли, чтобы класс сущности JPA содержал два @Embeddedполя embedded ( )? Примером может быть:

@Entity
public class Person {
    @Embedded
    public Address home;

    @Embedded
    public Address work;
}

public class Address {
    public String street;
    ...
}

В этом случае a Personможет содержать два Addressэкземпляра - домашний и рабочий. Я использую JPA с реализацией Hibernate. Когда я генерирую схему с помощью Hibernate Tools, она встраивает только одну Address. Я бы хотел два встроенных Addressэкземпляра, каждый с именами столбцов, выделенными или предваренными некоторым префиксом (например, домашний и рабочий). Я знаю @AttributeOverrides, но для этого требуется, чтобы каждый атрибут был индивидуально переопределен. Это может стать громоздким, если встроенный объект ( Address) становится большим, так как каждый столбец необходимо индивидуально переопределить.

Стив Куо
источник

Ответы:

29

Если вы хотите иметь один и тот же тип встраиваемого объекта дважды в одной и той же сущности, имя столбца по умолчанию не будет работать: по крайней мере, один из столбцов должен быть явным. Hibernate выходит за рамки спецификации EJB3 и позволяет улучшить механизм установки по умолчанию с помощью NamingStrategy. DefaultComponentSafeNamingStrategy - это небольшое улучшение по сравнению с EJB3NamingStrategy по умолчанию, которое позволяет использовать встроенные объекты по умолчанию, даже если они используются дважды в одной и той же сущности.

Из документа Hibernate Annotations: http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/#d0e714

Локи
источник
89

Общий способ JPA сделать это - @AttributeOverride. Это должно работать как в EclipseLink, так и в Hibernate.

@Entity 
public class Person {
  @AttributeOverrides({
    @AttributeOverride(name="street",column=@Column(name="homeStreet")),
    ...
  })
  @Embedded public Address home;

  @AttributeOverrides({
    @AttributeOverride(name="street",column=@Column(name="workStreet")),
    ...
  })
  @Embedded public Address work;
  }

  @Embeddable public class Address {
    @Basic public String street;
    ...
  }
}
Филихп Басби
источник
9
Обратите внимание, что это name="street"относится к имени свойства, а не к имени столбца.
Барт Свенненхейс
Считается ли это «неуклюжим», поскольку для этого требуется, чтобы разработчик Person знал интимные сведения о классе Address (например, имя поля, содержащего название улицы)?
mbmast
вау, так что я должен все это повторить, используя аннотации. веселье я мог бы просто вручную объявить весь встроенный класс, используя String homeStreet; Вместо этого String workStreeet, вероятно, более детерминированный.
ммм
6

При использовании Eclipse Link альтернативой AttributeOverrides является использование SessionCustomizer. Это решает проблему для всех сущностей за один раз:

public class EmbeddedFieldNamesSessionCustomizer implements SessionCustomizer {

@SuppressWarnings("rawtypes")
@Override
public void customize(Session session) throws Exception {
    Map<Class, ClassDescriptor> descriptors = session.getDescriptors();
    for (ClassDescriptor classDescriptor : descriptors.values()) {
        for (DatabaseMapping databaseMapping : classDescriptor.getMappings()) {
            if (databaseMapping.isAggregateObjectMapping()) {
                AggregateObjectMapping m = (AggregateObjectMapping) databaseMapping;
                Map<String, DatabaseField> mapping = m.getAggregateToSourceFields();

                ClassDescriptor refDesc = descriptors.get(m.getReferenceClass());
                for (DatabaseMapping refMapping : refDesc.getMappings()) {
                    if (refMapping.isDirectToFieldMapping()) {
                        DirectToFieldMapping refDirectMapping = (DirectToFieldMapping) refMapping;
                        String refFieldName = refDirectMapping.getField().getName();
                        if (!mapping.containsKey(refFieldName)) {
                            DatabaseField mappedField = refDirectMapping.getField().clone();
                            mappedField.setName(m.getAttributeName() + "_" + mappedField.getName());
                            mapping.put(refFieldName, mappedField);
                        }
                    }

                }
            }

        }
    }
}

}
рудист
источник
+1 Было бы неплохо иметь это как DescriptorCustomizer, чтобы иметь возможность управлять этим для каждого класса, но я не нашел способа получить доступ к ClassDescriptor встроенного класса из DescriptorCustomizer класса хоста.
oulenz
1
Альтернатива, которую я использую сейчас, - это проверить classDescriptor.getJavaClass()в SessionCustomizer список классов, на которые я хочу повлиять.
oulenz
1

Если вы используете спящий режим, вы также можете использовать другую схему именования, которая добавляет уникальные префиксы к столбцам для идентичных встроенных полей. См. Раздел Автоматическое добавление префикса к именам столбцов для классов @Embeddable.

Арьян Мэлс
источник