多个嵌入字段

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

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


@Embedded
public Address work;
}


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

在这种情况下,Person可以包含两个 Address实例-家庭和工作。我正在使用 JPA 和 Hibernate 的实现。当我使用 Hibernate 工具生成模式时,它只嵌入一个 Address。我想要的是两个嵌入式 Address实例,每个实例的列名可以区分,或者在前面加上一些前缀(比如 home 和 work)。我知道 @AttributeOverrides,但是这需要单独重写每个属性。如果嵌入对象(Address)变得很大,因为每个列都需要单独重写,那么这可能会变得很麻烦。

44120 次浏览

If you want to have the same embeddable object type twice in the same entity, the column name defaulting will not work: at least one of the columns will have to be explicit. Hibernate goes beyond the EJB3 spec and allows you to enhance the defaulting mechanism through the NamingStrategy. DefaultComponentSafeNamingStrategy is a small improvement over the default EJB3NamingStrategy that allows embedded objects to be defaulted even if used twice in the same entity.

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

The generic JPA way to do it is with @AttributeOverride. This should work in both EclipseLink and 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;
...
}
}

When using Eclipse Link, an alternative to using AttributeOverrides it to use a SessionCustomizer. This solves the issue for all entities in one go:

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);
}
}


}
}


}
}
}


}

In case you are using hibernate you can also use a different naming scheme which adds unique prefixes to columns for identical embedded fields. See Automatically Add a Prefix to Column Names for @Embeddable Classes