Search This Blog

Thursday 8 September 2011

Enforcing the Rules for naming conventions

There is often a need to enforce strict naming conventions for the database just as we have naming conventions in java. As multiple developers work on different tables in the schema, there is a possibility of each developer following his own naming conventions. To avoid this Hibernate provides us with the NamingStrategy interface, that can be used to enforce naming standards automatically.
The solution requires us to implement our own naming strategy or use several of the provided options.

In this example I will extend the ImprovedNamingStrategy class.

public class DbNamingStrategy extends ImprovedNamingStrategy {

    /**
     * Called only if a <class> element does not specify a table name
     */
    @Override
    public String classToTableName(String className) {
        //this will transform it to the name to use.
        return splitCamelCase(StringHelper.unqualify(className));
    }
    
    /**
     * Called when the table name is specified. 
     */
    @Override
    public String tableName(String tableName) {
        //No action taken.provided table name is used.
        //If necessary validations can be performed here to ensure that the table name is valid.
        //Or better still simply throw an exception to prevent any developer providing table names
        return tableName;
    }
    
    /**
     * Called only if a column name is provided
     */
    @Override
    public String columnName(String columnName) {
        throw new RuntimeException("You are not allowed to set the column property!!!");        
    }
    
    /**
     * Called when a column name is not provided
     */
    @Override
    public String propertyToColumnName(String propertyName) {       
        return splitCamelCase(propertyName).toUpperCase();
    }
    
    /**
     * Method will convert a Camel Case word into a word where the 
     * individual fields are separated by _
     * @param cameCaseWord
     * @return {@link String}
     * code used from :http://stackoverflow.com/questions/2559759/
     *     how-do-i-convert-camelcase-into-human-readable-names-in-java
     */
    private String splitCamelCase(String cameCaseWord) {
        return cameCaseWord.replaceAll(String.format(
                "%s|%s|%s",
                "(?<=[A-Z])(?=[A-Z][a-z])",
                "(?<=[^A-Z])(?=[A-Z])",
                "(?<=[A-Za-z])(?=[^A-Za-z])")
            , "_");
    }
}

Java Code to use the above class: 

 
public static void main(String[] args) {
    final Configuration configuration = new Configuration()
            .setNamingStrategy(new DbNamingStrategy());
    configuration.configure();
    sessionFactory = configuration.buildSessionFactory();
}

The naming strategy has been associated with the Configuration class as seen above. This strategy will be used for creating all the queries, the
DDLs(in case SchemaExporter is used) etc.
I created a sample Model class User and its mapping document for testing. I set the hbm2ddl to "create" to verify if the rules are applied to the generated
DDL code; 
User.java
public class User {
        public Long id;//using field access to reduce the test code. 
        //Not the right way to do things
    public String name,userCode, TXRNumber;        
}

The User mapping document:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping package="com.test.data.model" default-access ="field">
    <!-- all fields of this class are set to direct field access. 
    The attribute can be set at individual property level too. -->
    <class name="User" > 
        
        <id name="id" type="long">
            <generator class="native" />
        </id>
        <property name="name" />
        <property name="userCode" />
        <property name="TXRNumber" />        
    </class>
</hibernate-mapping>

As can be seen from the above, no column or table mappings have been provided. So the
rules specified in the Naming Strategy will be used.On executing the code the generated DDL is as follows:
create table User (
    ID bigint not null auto_increment,
    NAME varchar(255),
    USER_CODE varchar(255),
    TXR_NUMBER varchar(255),
    primary key (ID)
)

If some user adds a column mapping to the hbm file then :
<property name="name" >
    <column name="NAME"></column>
</property>

then on executing the application, an exception would be thrown
Exception in thread "main" java.lang.RuntimeException: You are not allowed to set the column property!!!
at com.naming.startegy.DbNamingStrategy.columnName(DbNamingStrategy.java:36)
at org.hibernate.cfg.HbmBinder.bindColumns(HbmBinder.java:1029)
at org.hibernate.cfg.HbmBinder.bindColumnsOrFormula(HbmBinder.java:1528)
at org.hibernate.cfg.HbmBinder.bindSimpleValue(HbmBinder.java:1124)
at...

No comments:

Post a Comment