Hibernate has tools to create mapping files(hbm files) and domain model classes from database schemas(reverse engineering). <jdbcconfguration> can be used as part of ant task to do this. Hibernate creates the property names using its default reverse engineering strategy. Hibernate also, provides a way through which the user can specify his own custom reverse engineering strategy through which he can follow his own naming conventions etc…
There is a reversestrategy attribute which can be set to some custom class, which implements org.hibernate.cfg.reveng.ReverseEngineeringStrategy, in the <jdbcconfiguration>. Here’s an example:
<jdbcconfiguration configurationfile="hibernate.cfg.xml"
packagename="${package.name}"
revengfile="hibernate.reveng.xml"
reversestrategy="org.myapp.hibernate.tool.SampleReverseEngineeringStrategy"/>
The org.myapp.hibernate.tool.SampleReverseEngineeringStrategy is our own custom class which implements org.hibernate.cfg.reveng.ReverseEngineeringStrategy. In this example, our SampleReverseEngineeringStrategy, overrides the columnToPropertyName(TableIdentifier table, String column) method to provide a custom implementation for generating property names out of a column name. Here’s the SampleReverseEngineeringStrategy code:
package org.myapp.hibernate.tool;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.hibernate.cfg.reveng.DelegatingReverseEngineeringStrategy;
import org.hibernate.cfg.reveng.ReverseEngineeringStrategy;
import org.hibernate.cfg.reveng.TableIdentifier;
/**
*
* @author Jaikiran Pai
*
*/
public class SampleReverseEngineeringStrategy extends DelegatingReverseEngineeringStrategy {
/**
* Constructor
*
* @param delegate {@link org.hibernate.cfg.reveng.ReverseEngineeringStrategy}
*/
public SampleReverseEngineeringStrategy(ReverseEngineeringStrategy delegate) {
super(delegate);
}
/**
* Changes the default behaviour of naming the property. <br>
* Does the following replacements(not neccessarily in the order) and returns the resulting
* {@link String} as property name
*
* <ul>
* <li>Converts the first letter of the <code>column</code> to uppercase</li>
* <li>Converts the letters following a '_' character to uppercase in the <code>column</code></li>
* <li>Removes any underscores present from <code>column</code></li>
* </ul>
*
*
* @see org.hibernate.cfg.reveng.DelegatingReverseEngineeringStrategy
* @see org.hibernate.cfg.reveng.ReverseEngineeringStrategy
*
* @param table {@link TableIdentifier}
* @param column
* @return Returns the propert name after converting it appropriately
*/
public String columnToPropertyName(TableIdentifier table, String column) {
String replacedColumn = replaceFirstLetterToUpperCase(column);
replacedColumn = removeUnderScoresAndConvertNextLetterToUpperCase(replacedColumn);
if (anyReplacementsMadeToOriginalColumnName(column,replacedColumn)) {
return replacedColumn;
}
/*
* Let DelegatingReverseEngineeringStrategy handle this
*/
return super.columnToPropertyName(table, column);
}
/**
*
* Returns true if the <code>originalString</code> and <code>replacedString</code> are NOT equal
* (meaning there was some replacement done to the original column name). Else returns false. <br/>
*
* @param originalString The original column name
* @param replacedString The column name after doing necessary replacements
* @return Returns true if the <code>originalString</code> and <code>replacedString</code> are NOT equal
* (meaning there was some replacement done to the original column name). Else returns false.
*
* @throws {@link NullPointerException} if <code>originalString</code> is null.
*/
protected boolean anyReplacementsMadeToOriginalColumnName(String originalString, String replacedString) {
if (originalString.equals(replacedString)) {
return false;
}
return true;
}
/**
* Converts the first letter of the <code>input</code> to uppercase and
* returns the resultant {@link String}.<br/>
* <p>
* Ex: If the <code>input</code> is startDate then the resulting {@link String}
* after replacement will be StartDate
* </p>
*
* @param input The {@link String} whose contents have to be replaced
* @return Returns a {@link String} after doing the appropriate replacements
*/
protected String replaceFirstLetterToUpperCase(String input) {
/*
* The pattern to match a String starting with lower case
*/
final String startsWithLowerCasePattern = "^[a-z]";
Pattern patternForReplacingLowerCase = Pattern.compile(startsWithLowerCasePattern);
Matcher regexMatcher = patternForReplacingLowerCase.matcher(input);
/*
* This will hold the replaced contents
*/
StringBuffer replacedContents = new StringBuffer();
/*
* Check whether the first letter starts with lowercase.
* If yes, change it to uppercase, else pass on the control to
* DelegatingReverseEngineeringStrategy
*
*/
if (regexMatcher.find()) {
String firstCharacter = regexMatcher.group();
/*
* Convert it to uppercase
*/
regexMatcher.appendReplacement(replacedContents,firstCharacter.toUpperCase());
regexMatcher.appendTail(replacedContents);
regexMatcher.reset();
/*
* Return the replaced contents
*/
return replacedContents.toString();
}
//no replacements to do, just return the original input
return input;
}
/**
* Converts the letters following a '_' character to uppercase and also removes
* the '_' character from the <code>input</code> and returns the resulting {@link String}. <br/>
* Carries out a 2 pass strategy to do the replacements. During the first pass,
* replaces all the letters that immidiately follow a '_' to uppercase.
* <p>
* Ex: If the <code>input</code> is _start_Date__today_ then after the first pass of replacement, the
* resultant string will be _Start_Date__Today_
* </p>
* <p>
* This replaced {@link String} is then passed ahead for second pass (if no replacements were
* done during first pass, then the original {@link String} is passed). During the second pass
* the underscores are removed.
* </p>
* <p>
* Ex: If the <code>input</code> is _start_Date__today_ then after BOTH the passes the
* resultant string will be StartDateToday
* </p>
*
* @param input The {@link String} whose contents have to be replaced
* @return Returns a {@link String} after doing the appropriate replacements
*/
protected String removeUnderScoresAndConvertNextLetterToUpperCase(String input) {
/*
* The pattern which matches a String that starts with a letter immidiately after
* a '_' character
*/
final String stringFollowingUnderScore = "[.]*_[a-zA-Z]+";
Pattern patternForReplacingLowerCase = Pattern.compile(stringFollowingUnderScore);
Matcher regexMatcher = patternForReplacingLowerCase.matcher(input);
/*
* This will hold the replaced contents
*/
StringBuffer replacedContents = new StringBuffer();
boolean foundAnyMatch = false;
while (regexMatcher.find()) {
foundAnyMatch = true;
String matchedString = regexMatcher.group();
/*
* The character immidiately following the underscore
* Example:
* If matchedString is _tMn then originalCharAfterUnderScore will be the
* character t
*/
char originalCharAfterUnderScore = matchedString.charAt(1);
/*
* Convert the character to uppercase
*/
String replacedCharAfterUnderScore = String.valueOf(originalCharAfterUnderScore).toUpperCase();
/*
* Now place this replaced character back into the matchedString
*/
String replacement = matchedString.replace(originalCharAfterUnderScore,replacedCharAfterUnderScore.charAt(0));
/*
* Append this to the replacedColumn, which will be returned back to the user
*/
regexMatcher.appendReplacement(replacedContents,replacement);
}
regexMatcher.appendTail(replacedContents);
regexMatcher.reset();
/*
* Now the input string has been replaced to contain uppercase letters after the underscore.
* Ex: If input string was "_start_Date_today" then at this point after the above processing,
* the replaced string will be "_Start_Date_Today"
* The only thing that remains now is to remove the underscores from the input string.
* The following statements do this part.
*
*/
if (foundAnyMatch) {
return removeUnderScores(replacedContents.toString());
} else {
return removeUnderScores(input);
}
}
/**
* Removes any underscores present from <code>input</code> and returns the
* resulting {@link String}
* <p>
* Ex: If the <code>input</code> is _start_Date__today_ then the resulting {@link String}
* after replacement will be startDatetoday
* </p>
*
* @param input The {@link String} whose contents have to be replaced
* @return Returns a {@link String} after doing the appropriate replacements
*/
protected String removeUnderScores(String input) {
/*
* Pattern for matching underscores
*/
Pattern patternForUnderScore = Pattern.compile("[.]*_[.]*");
Matcher regexMatcher = patternForUnderScore.matcher(input);
/*
* This will hold the return value
*/
StringBuffer returnVal = new StringBuffer();
boolean foundAnyMatch = false;
while (regexMatcher.find()) {
foundAnyMatch = true;
String matchedString = regexMatcher.group();
/*
* Remove the underscore
*/
regexMatcher.appendReplacement(returnVal,"");
}
regexMatcher.appendTail(returnVal);
regexMatcher.reset();
/*
* If any match was found(and replaced) then return the replaced string.
* Else return the original input.
*/
if (foundAnyMatch) {
return returnVal.toString();
}
return input;
}
}
In the example above, the columnToPropertyName method is overridden to do the following:
- Creates property names that start with a Capital case(By default, Hibernate creates property names in camel-case)
- Converts the letter, that follows a ‘_’ (underscore character) to uppercase in the property name
- Removes any underscores in the property name
Ex: If the column name is start_Date_today, then the resulting property name after using the SampleReverseEngineeringStrategy would be StartDateToday.
Here’s a documentation from Hibernate about Controlling Reverse Engineering