Кілька вбудованих полів 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.