Campi multipli JPA incorporati


84

È possibile che una classe di entità JPA contenga due @Embeddedcampi embedded ( )? Un esempio potrebbe essere:

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

    @Embedded
    public Address work;
}

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

In questo caso a Personpuò contenere due Addressistanze: casa e lavoro. Sto usando JPA con l'implementazione di Hibernate. Quando genero lo schema usando Hibernate Tools, ne incorpora solo uno Address. Quello che vorrei sono due Addressistanze incorporate , ciascuna con i nomi delle colonne distinti o preceduti da un prefisso (come casa e lavoro). Lo so @AttributeOverrides, ma questo richiede che ogni attributo venga ignorato individualmente. Questo può diventare complicato se l'oggetto incorporato ( Address) diventa grande poiché ogni colonna deve essere sovrascritta individualmente.

Risposte:


29

Se vuoi avere lo stesso tipo di oggetto incorporabile due volte nella stessa entità, l'impostazione predefinita del nome della colonna non funzionerà: almeno una delle colonne dovrà essere esplicita. Hibernate va oltre le specifiche EJB3 e consente di migliorare il meccanismo di default tramite NamingStrategy. DefaultComponentSafeNamingStrategy è un piccolo miglioramento rispetto a EJB3NamingStrategy predefinito che consente di impostare come predefinito gli oggetti incorporati anche se usati due volte nella stessa entità.

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


89

Il modo generico di JPA per farlo è con @AttributeOverride. Questo dovrebbe funzionare sia in EclipseLink che in 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
Notare che si name="street"riferisce al nome della proprietà, non al nome della colonna.
Bart Swennenhuis

Questo è considerato "goffo", poiché richiede allo sviluppatore di Person di conoscere cose intime sull'indirizzo della classe (come il nome del campo contenente il nome della strada)?
mbmast

wow, quindi devo ripetere tutto usando le annotazioni. wtf. potrei altrettanto bene dichiarare manualmente l'intera classe incorporata usando String homeStreet; Stringa workStreeet invece, probabilmente più deterministica.
mmm

6

Quando si utilizza Eclipse Link, un'alternativa all'utilizzo di AttributeOverride per utilizzare un SessionCustomizer. Questo risolve il problema per tutte le entità in una volta sola:

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 Sarebbe bello averlo come DescriptorCustomizer, per poterlo controllare per classe, ma non ho trovato un modo per accedere al ClassDescriptor della classe incorporata dall'interno del DescriptorCustomizer della classe host.
oulenz

1
L'alternativa che sto usando ora è controllare classDescriptor.getJavaClass()in SessionCustomizer un elenco di classi che voglio che influenzi.
oulenz

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.