My Wiki

July 31, 2006

Custom reverse engineering strategy in Hibernate

Filed under: Hibernate — jaikiran @ 10:12 am

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

Advertisements

6 Comments »

  1. Hi,
    nice article. is it possible to extract the table/column comments from the database and add the meta tags to the hbm files. I am not sure whether Reverse Engineering strategy will be able to do it.

    Kathir.

    Comment by kathir — February 21, 2007 @ 3:58 am

  2. Hi,
    I have to create a Strategy to change the suffix “HOME” for the generation of DaoObject and replace it with the suffix “DAO” es.”MyClass.DAO” and place it in a custom directory in my project.
    Anyone can help me?

    Comment by Fabio — July 10, 2009 @ 10:48 am

  3. Superb post but I was wanting to know if you could write
    a litte more on this topic? I’d be very thankful if you could elaborate a little bit more. Many thanks!

    Comment by right hotmail support — April 19, 2013 @ 8:06 pm

  4. Hey there terrific website! Does running a blog similar to this take a lot of work?
    I’ve virtually no expertise in programming however I had been hoping to start my own blog soon. Anyway, if you have any ideas or tips for new blog owners please share. I understand this is off topic nevertheless I simply needed to ask. Thanks!

    Comment by Cynthia — May 12, 2013 @ 4:28 pm

  5. You are so awesome! I don’t think I have read a single thing like that before. So wonderful to discover another person with a few original thoughts on this subject. Really.. thanks for starting this up. This web site is one thing that is required on the internet, someone with a little originality!

    Comment by www.jeremiesamson-portfolio.com — May 31, 2013 @ 4:00 pm

  6. […] Autre exemple de reveng strategy […]

    Pingback by Génération de classes modèles avec Hibernate Tools | Blog Xebia France — May 22, 2014 @ 12:47 pm


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Blog at WordPress.com.

%d bloggers like this: