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.

Steve Kuo
quelle

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

Loki
quelle
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;
    ...
  }
}
Philihp Busby
quelle
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);
                        }
                    }

                }
            }

        }
    }
}

}
ruediste
quelle
+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