【发布时间】:2013-11-22 15:56:43
【问题描述】:
由于历史原因,我需要在我当前使用 EclipseLink 的项目中将驼峰式名称映射到下划线分隔的名称。我知道我们可以在 JPA 中单独自定义名称映射,但是我们有一长串要更改的驼峰式名称,因此我们希望尽可能避免使用那种样板代码。
我想要实现的目标如下。假设我们有一个实体类如下:
@Entity
public class FooBar {
@Id @GeneratedValue
private Long id;
@Temporal( TemporalType.TIMESTAMP )
private Date dateCreated;
}
我希望这个类映射到一个名为“foo_bar”和列“id”和“date_created”的表。请注意,数据库中的所有名称都是小写的。
我四处搜索,找到了更改表名的解决方案。但是,我不知道如何更改实体类中的字段名称。
下面是我的名称映射定制器,其中updateFieldNameMappings() 方法没有将fieldName 映射到field_name,这是我想要实现的。问题归结为如何获取类定义中的字段名称。那么,我该如何在 EclipseLink 中做到这一点?
public class JpaNameMappingCustomizer implements SessionCustomizer {
@Override
public void customize( Session session ) throws Exception {
Map<Class, ClassDescriptor> descs = session.getDescriptors();
Collection<ClassDescriptor> descriptors = descs.values();
// This code assumes single table per descriptor!
for (ClassDescriptor desc : descriptors) {
updateTableNameMapping( desc );
updateFieldNameMapping( desc );
}
}
private void updateTableNameMapping ( ClassDescriptor desc ) {
Class clazz = desc.getJavaClass();
String tableName = camelCaseToUnderscore( clazz.getSimpleName() );
desc.setTableName( tableName );
}
private void updateFieldNameMapping ( ClassDescriptor desc ) {
// build name maps
Field[] fields = desc.getJavaClass().getDeclaredFields();
String tableName = desc.getTableName();
Map<String,String> nameMap = new HashMap<>();
String prefix = tableName + ".";
for( Field field : fields ) {
String name = field.getName();
String key = prefix + name.toUpperCase();
String value = prefix + camelCaseToUnderscore( name );
nameMap.put( key, value );
}
for (DatabaseMapping mapping : desc.getMappings()) {
if (mapping.isDirectToFieldMapping()) {
DirectToFieldMapping directMapping = (DirectToFieldMapping) mapping;
String oldFieldName = directMapping.getFieldName(); // format: table_name.FIELD
directMapping.setFieldName( nameMap.get( oldFieldName ) );
}
}
}
private String camelCaseToUnderscore( String camelCase ) {
return camelCase.trim().replaceAll("(?<!^)[A-Z](?!$)", "_$0").toLowerCase();
}
}
编辑 11/10/13
我做了一些黑客攻击并更改了定制器。 updateFieldNameMapping() 仍然不能解决问题。在我看来,方法内部的这个声明directMapping.setFieldName( nameMap.get( oldFieldName ) )实际上并没有改变字段名称映射,这让我很困惑。
编辑 2013 年 11 月 11 日
我忘了说明我在persistence.xml 中启用了eclipselink.session.customizer。也就是说,我在persistence.xml 中有如下一行:
<property name="eclipselink.session.customizer" value="pkg.JpaNameMappingCustomizer"/>
【问题讨论】:
-
当你说它没有给你想要的东西时,你到底是什么意思?
-
@JamesB:感谢您的提问。我更新了我的帖子。希望现在更清楚了。
-
setFieldName 应该从传入的字符串创建一个 DatabaseField 对象,因此请检查 getFieldName 在调用后返回您期望的内容。你能看到你的定制器什么时候被调用吗?由于它仅在直接到字段映射(基本 JPA 映射)上运行,因此将排除在其他映射中定义的任何其他字段。
-
@Chris:感谢您的评论。根据您的建议,我在调用
setFieldName()后仔细检查了字段名称。它显示了我想要的字段名称,但 Eclipselink 不知何故仍使用旧字段进行映射。例如,假设我在调用setFieldName()后从getFieldName()得到table_name.foo_bar。但是,我仍然收到类似There should be one non-read-only mapping defined for the primary key field [table_name.FOOBAR]的错误。 -
你能看看这个问题吗-stackoverflow.com/questions/37226244/…
标签: java jakarta-ee jpa eclipselink