使用EclipseLink自定义JPA字段名称映射

由于历史原因,我需要在当前项目中使用EclipseLink将camel-case名称映射到以下划线分隔的名称。 我知道我们可以在JPA中单独定制名称映射,但我们有一长串的驼峰名称要更改,所以我们想要尽可能避免使用那种样板代码。

我想要达到的目的如下。 假设我们有一个实体类,如下所示:

@Entity public class FooBar { @Id @GeneratedValue private Long id; @Temporal( TemporalType.TIMESTAMP ) private Date dateCreated; } 

我希望这个类映射到名为“foo_bar”和列“id”和“date_created”的表。 请注意,数据库中的所有名称都是小写的。

我用Google搜索,找到了更改表名的解决方案。 但是,我无法弄清楚如何在实体类中更改字段名称。

下面是我的名称映射定制器,其中方法updateFieldNameMappings()没有将fieldName映射到field_name ,这是我想要实现的。 问题归结为如何在类定义中获取字段名称。 那么,我如何在EclipseLink中做到这一点?

 public class JpaNameMappingCustomizer implements SessionCustomizer { @Override public void customize( Session session ) throws Exception { Map descs = session.getDescriptors(); Collection 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 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("(?<!^)[AZ](?!$)", "_$0").toLowerCase(); } } 

编辑11/10/13我做了一些黑客攻击并更改了自定义程序。 updateFieldNameMapping()仍然updateFieldNameMapping() 。 在我看来,这个语句中的directMapping.setFieldName( nameMap.get( oldFieldName ) )实际上并没有改变字段名称映射,这真让我感到困惑。

编辑11/11/13我忘了说清楚我在persistence.xml启用了eclipselink.session.customizer 。 也就是说,我在persistence.xml有如下所示的行:

  

代替

 directMapping.setFieldName(nameMap.get(oldFieldName )); 

尝试:

 directMapping.getField().resetQualifiedName(nameMap.get(oldFieldName)); 

这在EclipseLink 2.5.0下可以解决这个问题

也许你可以使用我的会话定制器。 它将表名和字段名转换为Camel案例。 它支持inheritance和嵌入实体。 我测试了2年,没有任何问题或副作用。 好好享受!

 import java.sql.SQLException; import java.util.Locale; import java.util.Vector; import org.eclipse.persistence.config.SessionCustomizer; import org.eclipse.persistence.descriptors.ClassDescriptor; import org.eclipse.persistence.internal.helper.DatabaseField; import org.eclipse.persistence.mappings.DatabaseMapping; import org.eclipse.persistence.sessions.Session; import org.eclipse.persistence.tools.schemaframework.IndexDefinition; public class CamelNamingStrategy implements SessionCustomizer { public static String unqualify(final String qualifiedName) { int loc = qualifiedName.lastIndexOf("."); return loc < 0 ? qualifiedName : qualifiedName.substring(qualifiedName.lastIndexOf(".") + 1); } @Override public void customize(final Session session) throws SQLException { for (ClassDescriptor descriptor : session.getDescriptors().values()) { if (!descriptor.getTables().isEmpty()) { // Take table name from @Table if exists String tableName = null; if (descriptor.getAlias().equalsIgnoreCase(descriptor.getTableName())) { tableName = unqualify(descriptor.getJavaClassName()); } else { tableName = descriptor.getTableName(); } tableName = camelCaseToUnderscore(tableName); descriptor.setTableName(tableName); for (IndexDefinition index : descriptor.getTables().get(0).getIndexes()) { index.setTargetTable(tableName); } Vector mappings = descriptor.getMappings(); camelCaseToUnderscore(mappings); } else if (descriptor.isAggregateDescriptor() || descriptor.isChildDescriptor()) { camelCaseToUnderscore(descriptor.getMappings()); } } } private void camelCaseToUnderscore(Vector mappings) { for (DatabaseMapping mapping : mappings) { DatabaseField field = mapping.getField(); if (mapping.isDirectToFieldMapping() && !mapping.isPrimaryKeyMapping()) { String attributeName = mapping.getAttributeName(); String underScoredFieldName = camelCaseToUnderscore(attributeName); field.setName(underScoredFieldName); } } } private String camelCaseToUnderscore(final String name) { StringBuffer buf = new StringBuffer(name.replace('.', '_')); for (int i = 1; i < buf.length() - 1; i++) { if (Character.isLowerCase(buf.charAt(i - 1)) && Character.isUpperCase(buf.charAt(i)) && Character.isLowerCase(buf.charAt(i + 1))) { buf.insert(i++, '_'); } } return buf.toString().toLowerCase(Locale.ENGLISH); } } 

如果你想使用@Column注释来区分非注释元素和带注释的元素,那么我发现了一个非常漂亮的例子:

 public class CamelCaseSessionCustomizer implements SessionCustomizer { @Override public void customize(Session session) throws SQLException { for (ClassDescriptor descriptor : session.getDescriptors().values()) { // Only change the table name for non-embedable entities with no // @Table already if (!descriptor.getTables().isEmpty() && descriptor.getAlias().equalsIgnoreCase(descriptor.getTableName())) { String tableName = addUnderscores(descriptor.getTableName()); descriptor.setTableName(tableName); for (IndexDefinition index : descriptor.getTables().get(0).getIndexes()) { index.setTargetTable(tableName); } } for (DatabaseMapping mapping : descriptor.getMappings()) { // Only change the column name for non-embedable entities with // no @Column already if (mapping instanceof AggregateObjectMapping) { for (Association association : ((AggregateObjectMapping) mapping).getAggregateToSourceFieldAssociations()) { DatabaseField field = (DatabaseField) association.getValue(); field.setName(addUnderscores(field.getName())); for (DatabaseMapping attrMapping : session.getDescriptor(((AggregateObjectMapping) mapping).getReferenceClass()).getMappings()) { if (attrMapping.getAttributeName().equalsIgnoreCase((String) association.getKey())) { ((AggregateObjectMapping) mapping).addFieldTranslation(field, addUnderscores(attrMapping.getAttributeName())); ((AggregateObjectMapping) mapping).getAggregateToSourceFields().remove(association.getKey()); break; } } } } else if (mapping instanceof ObjectReferenceMapping) { for (DatabaseField foreignKey : ((ObjectReferenceMapping) mapping).getForeignKeyFields()) { foreignKey.setName(addUnderscores(foreignKey.getName())); } } else if (mapping instanceof DirectMapMapping) { for (DatabaseField referenceKey : ((DirectMapMapping) mapping).getReferenceKeyFields()) { referenceKey.setName(addUnderscores(referenceKey.getName())); } for (DatabaseField sourceKey : ((DirectMapMapping) mapping).getSourceKeyFields()) { sourceKey.setName(addUnderscores(sourceKey.getName())); } } else { DatabaseField field = mapping.getField(); if (field != null && !mapping.getAttributeName().isEmpty() && field.getName().equalsIgnoreCase(mapping.getAttributeName())) { field.setName(addUnderscores(mapping.getAttributeName())); } } } } } private static String addUnderscores(String name) { if (name.equalsIgnoreCase("begintime")) { System.err.println(); } StringBuffer buf = new StringBuffer(name.replace('.', '_')); for (int i = 1; i < buf.length() - 1; i++) { if (Character.isLowerCase(buf.charAt(i - 1)) && Character.isUpperCase(buf.charAt(i)) && Character.isLowerCase(buf.charAt(i + 1))) { buf.insert(i++, '_'); } } return buf.toString().toLowerCase(); } } 

来自https://gist.github.com/ganeshs/c0deb77ffae33dee4555