Кілька вбудованих полів JPA


84

Чи можливо, щоб клас сутності JPA містив два вбудовані ( @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) стає великим, оскільки кожен стовпець потрібно індивідуально замінювати.

Відповіді:


28

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

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


89

Загальний спосіб JPA це зробити за допомогою @AttributeOverride. Це має працювати як в EclipseLink, так і в режимі глибокого сну.

@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 знання близьких речей про Адресу класу (наприклад, назви поля, що містить назву вулиці)?
mbmast

нічого собі, тому мені доводиться все це повторювати, використовуючи анотації. wtf. я міг би так само добре оголосити весь вбудований клас самостійно вручну, використовуючи String homeStreet; Натомість рядок 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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.