JPA Multiple Embedded-Felder


83

Kann eine JPA-Entitätsklasse zwei embedded ( @Embedded) -Felder enthalten? Ein Beispiel wäre:

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

    @Embedded
    public Address work;
}

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

In diesem Fall Personkann a zwei AddressInstanzen enthalten - Heim und Arbeit. Ich verwende JPA mit der Implementierung von Hibernate. Wenn ich das Schema mit den Tools für den Ruhezustand generiere, wird nur eines eingebettet Address. Was ich möchte, sind zwei eingebettete AddressInstanzen, deren Spaltennamen unterschieden oder mit einem Präfix versehen sind (z. B. zu Hause und bei der Arbeit). Ich weiß @AttributeOverrides, aber dies erfordert, dass jedes Attribut einzeln überschrieben wird. Dies kann umständlich werden, wenn das eingebettete Objekt ( Address) groß wird, da jede Spalte einzeln überschrieben werden muss.

Antworten:


28

Wenn Sie denselben einbettbaren Objekttyp zweimal in derselben Entität haben möchten, funktioniert die Standardeinstellung für den Spaltennamen nicht: Mindestens eine der Spalten muss explizit sein. Der Ruhezustand geht über die EJB3-Spezifikation hinaus und ermöglicht es Ihnen, den Standardmechanismus durch die NamingStrategy zu verbessern. DefaultComponentSafeNamingStrategy ist eine kleine Verbesserung gegenüber der Standard-EJB3NamingStrategy, mit der eingebettete Objekte standardmäßig verwendet werden können, selbst wenn sie zweimal in derselben Entität verwendet werden.

Aus Hibernate Annotations Doc: http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/#d0e714


89

Die generische JPA-Methode ist @AttributeOverride. Dies sollte sowohl in EclipseLink als auch in Hibernate funktionieren.

@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
Beachten Sie, dass name="street"sich dies auf den Namen der Eigenschaft bezieht, nicht auf den Spaltennamen.
Bart Swennenhuis

Wird dies als "klobig" angesehen, da der Entwickler von Person vertraute Dinge über die Klassenadresse wissen muss (wie den Namen des Feldes, das den Straßennamen enthält)?
mbmast

6

Bei Verwendung von Eclipse Link ist eine Alternative zur Verwendung von AttributeOverrides die Verwendung eines SessionCustomizers. Dies löst das Problem für alle Entitäten auf einmal:

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 Es wäre schön, dies als DescriptorCustomizer zu haben, um dies pro Klasse steuern zu können, aber ich habe keine Möglichkeit gefunden, über den DescriptorCustomizer der Host-Klasse auf den ClassDescriptor der eingebetteten Klasse zuzugreifen.
Oulenz

1
Die Alternative, die ich jetzt verwende, besteht darin, classDescriptor.getJavaClass()den SessionCustomizer anhand einer Liste von Klassen zu überprüfen, die davon betroffen sein sollen.
Oulenz

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.