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.