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

时间:2013-11-10 22:59:42

标签: java java-ee jpa eclipselink

由于历史原因,我需要在当前项目中使用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<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 ) )实际上并没有改变字段名称映射,这真的让我感到困惑。

编辑11/11/13 我忘了清楚地说明eclipselink.session.customizer启用了persistence.xml。也就是说,我在persistence.xml

中有如下所示的行
<property name="eclipselink.session.customizer" value="pkg.JpaNameMappingCustomizer"/>

4 个答案:

答案 0 :(得分:6)

而不是

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

尝试:

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

这在EclipseLink 2.5.0

下为我提供了诀窍

答案 1 :(得分:4)

也许您可以使用我的会话自定义程序。它将表名和字段名转换为Camel案例。它支持继承和嵌入实体。 我测试了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<DatabaseMapping> mappings = descriptor.getMappings();
                camelCaseToUnderscore(mappings);
            } else if (descriptor.isAggregateDescriptor() || descriptor.isChildDescriptor()) {
                camelCaseToUnderscore(descriptor.getMappings());
            }
        }
    }

    private void camelCaseToUnderscore(Vector<DatabaseMapping> 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);
    }
}

答案 2 :(得分:1)

我认为您错过了在persistence.xml中添加以下行以说EclipseLink来使用您自己的SessionCustomizer

<property name="eclipselink.session.customizer" value="your.company.package.name.JpaNameMappingCustomizer"/>

参考

http://www.eclipse.org/eclipselink/documentation/2.5/dbws/creating_dbws_services002.htm

http://eclipse.org/eclipselink/documentation/2.4/jpa/extensions/p_session_customizer.htm#CHDFBIEI

答案 3 :(得分:1)

如果你想使用@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