JPAの複数の埋め込みフィールド


84

JPAエンティティクラスに2つの埋め込み(@Embedded)フィールドを含めることは可能ですか?例は次のとおりです。

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

    @Embedded
    public Address work;
}

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

この場合、aにPersonAddress自宅と職場の2つのインスタンスを含めることができます。Hibernateの実装でJPAを使用しています。Hibernate Toolsを使用してスキーマを生成すると、1つしか埋め込まれませんAddress。私が欲しいのは、2つの埋め込みAddressインスタンスで、それぞれの列名が区別されるか、接頭辞(自宅や職場など)が前に付けられます。私は知っていますが@AttributeOverrides、これには各属性を個別にオーバーライドする必要があります。Address各列を個別にオーバーライドする必要があるため、埋め込みオブジェクト()が大きくなると、これは面倒になる可能性があります。

回答:


28

同じエンティティに同じ埋め込み可能オブジェクトタイプを2回持つ場合、列名のデフォルトは機能しません。少なくとも1つの列が明示的である必要があります。HibernateはEJB3仕様を超えており、NamingStrategyを介してデフォルトメカニズムを拡張できます。DefaultComponentSafeNamingStrategyは、デフォルトのEJB3NamingStrategyを少し改善したもので、同じエンティティで2回使用された場合でも、埋め込みオブジェクトをデフォルトにすることができます。

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


89

これを行う一般的なJPAの方法は、@ AttributeOverrideを使用することです。これは、EclipseLinkと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
name="street"これは、列名ではなく、プロパティの名前を参照していることに注意してください。
Bart Swennenhuis 2015

これは、Personの開発者がクラスのアドレス(通りの名前を含むフィールドの名前など)について詳しく知る必要があるため、「不格好」と見なされますか?
mbmast

うわー、だから私は注釈を使用してそれをすべて繰り返す必要があります。wtf。String homeStreetを使用して、埋め込みクラス全体を手動で宣言することもできます。代わりに文字列workStreeet、おそらくより決定論的。
mmm

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として使用して、クラスごとに制御できるようにすると便利ですが、ホストクラスのDescriptorCustomizer内から埋め込みクラスのClassDescriptorにアクセスする方法が見つかりませんでした。
oulenz 2016

1
私が現在使用している代替手段はclassDescriptor.getJavaClass()、影響を与えたいクラスのリストに対してSessionCustomizerをチェックインすることです。
oulenz 2016

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.