JPA多个嵌入式字段


84

JPA实体类是否可以包含两个嵌入式(@Embedded)字段?一个例子是:

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

    @Embedded
    public Address work;
}

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

在这种情况下,aPerson可以包含两个Address实例-家庭和工作。我在Hibernate的实现中使用JPA。当我使用Hibernate Tools生成架构时,它仅嵌入一个Address。我想要的是两个嵌入式Address实例,每个实例的列名都经过区分或以某些前缀(例如home和work)为前缀。我知道@AttributeOverrides,但这要求每个属性都应单独覆盖。如果嵌入式对象(Address)变大,则可能会很麻烦,因为每个列都需要单独覆盖。

Answers:


28

如果要在同一实体中具有两次相同的可嵌入对象类型,则默认使用列名将无效:至少其中一列必须是显式的。Hibernate超越了EJB3规范,并允许您通过NamingStrategy增强默认机制。DefaultComponentSafeNamingStrategy是对默认EJB3NamingStrategy的一个很小的改进,该默认EJB3NamingStrategy允许即使在同一实体中使用两次嵌入式对象也默认为默认值。

从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的开发人员了解有关Address类的内在知识(例如包含街道名称的字段名称)?
mbmast

哇,所以我必须重复使用注解。wtf。我也可以使用String homeStreet手动声明整个嵌入式类。相反,使用字符串workStreeet可能更具确定性。
mmm

6

使用Eclipse Link时,使用AttributeOverride的替代方法是使用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

1
我现在使用的替代方法是根据classDescriptor.getJavaClass()我希望它影响的类的列表检入SessionCustomizer。
oulenz

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.