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 Tools生成模式时,它只嵌入一个Address
.我想要的是两个嵌入式Address
实例,每个实例的列名都有区别或预先设置了一些前缀(例如家庭和工作).我知道@AttributeOverrides
,但这要求每个属性都被单独覆盖.如果嵌入的object(Address
)变大,因为每个列需要单独重写,这会很麻烦.
通用的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; ... } }
如果要在同一实体中两次使用相同的可嵌入对象类型,则列名默认将不起作用:至少有一列必须是显式的.Hibernate超越了EJB3规范,允许您通过NamingStrategy增强默认机制.DefaultComponentSafeNamingStrategy是对默认EJB3NamingStrategy的一个小改进,即使在同一实体中使用两次,也允许默认嵌入对象.
来自Hibernate Annotations Doc:http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/#d0e714
使用Eclipse Link时,使用AttributeOverrides的替代方法是使用SessionCustomizer.这一次解决了所有实体的问题:
public class EmbeddedFieldNamesSessionCustomizer implements SessionCustomizer { @SuppressWarnings("rawtypes") @Override public void customize(Session session) throws Exception { Mapdescriptors = session.getDescriptors(); for (ClassDescriptor classDescriptor : descriptors.values()) { for (DatabaseMapping databaseMapping : classDescriptor.getMappings()) { if (databaseMapping.isAggregateObjectMapping()) { AggregateObjectMapping m = (AggregateObjectMapping) databaseMapping; Map 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); } } } } } } } }