I need to map camel-case names to underscore-separated names in my current project, which uses EclipseLink, due to historical reasons. I know we can customize name mapping individually in JPA, but we have a long list of camel-case names to change, so we want to avoid that kind of boilerplate codes if all possible.
What I want to achieve is as follows. Suppose we have an entity class as below:
@Entity
public class FooBar {
@Id @GeneratedValue
private Long id;
@Temporal( TemporalType.TIMESTAMP )
private Date dateCreated;
}
I want this class maps to a table with name "foo_bar" and columns "id" and "date_created". Note that all names in database are in lower case.
I googled around, and found a solution for changing table names. However, I can't figure out how to change field names in an entity class.
Below is my name-mapping customizer, where the method updateFieldNameMappings()
is not mapping fieldName
to field_name
, which is what I want to achieve. The problem boils down to how to get the field name as in the class definition. So, how do I do that in 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();
}
}
edit 11/10/13
I did some hacking and changed the customizer. The updateFieldNameMapping()
still doesn't do the trick. It seems to me that this statement directMapping.setFieldName( nameMap.get( oldFieldName ) )
inside the method doesn't actually change the field name mapping, which really confuses me.
edit 11/11/13
I forgot to make it clear that I had the eclipselink.session.customizer
enabled in the persistence.xml
. Namely, I have a line like below in the persistence.xml
:
<property name="eclipselink.session.customizer" value="pkg.JpaNameMappingCustomizer"/>
Instead of
directMapping.setFieldName(nameMap.get(oldFieldName ));
try:
directMapping.getField().resetQualifiedName(nameMap.get(oldFieldName));
This does the trick for me under EclipseLink 2.5.0
Perhaps you could use my session customizer. It converts table names, and field names to Camel case. It supports inheritance and embeded entities. I tested for 2 years, and I got no issues or side effects. Enjoy it!
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);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With