Chapter 5. Controlling reverse engineering

When using the <jdbcconfiguration> the ant task will read the database metadata and from that perform a reverse engineering of the database schema into a normal Hibernate Configuration. It is from this object e.g. >hbm2java< can generate other artifacts such as .java, .hbm.xml etc.

To govern this process Hibernate uses a reverse engineering strategy. A reverse engineering strategy is mainly called to provide more java like names for tables, column and foreignkeys into classes, properties and associations. It also used to provide mappings from SQL types to Hibernate types. The strategy can be customized by the user. The user can even provide its own custom reverse engineering strategy if the provided strategy is not enough, or simply just provide a small part of the strategy and delegate the rest to the default strategy.

5.1. Default reverse engineering strategy

The default strategy uses some rules for mapping JDBC artifact names to java artifact names. It also provide basic typemappings from JDBC types to Hibernate types. It is the default strategy that uses the packagename attribute to convert a table name to a fully qualified classname.

5.2. hibernate.reveng.xml file

To have fine control over the process a hibernate.reveng.xml file can be provided. In this file you can specify type mappings and table filtering. This file can be created by hand (its just basic XML) or you can use the Hibernate plugins which have a specialized editor.

Note: many databases is case-sensitive with their names and thus if you cannot make some table match and you are sure it is not excluded by a <table-filter> then check if the case matches; most databases stores table names in uppercase.

The following is an example of a reveng.xml. Following the example is more details about the format.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-reverse-engineering 
  SYSTEM "http://hibernate.sourceforge.net/hibernate-reverse-engineering-3.0.dtd" >

<hibernate-reverse-engineering>

<type-mapping>
 <!-- jdbc-type is name fom java.sql.Types -->
 <sql-type jdbc-type="VARCHAR" length='20' hibernate-type="SomeUserType" /> 
 <sql-type jdbc-type="VARCHAR" length='1' hibernate-type="yes_no" />
 <!-- length, scale and precision can be used to specify the mapping precisly -->
 <sql-type jdbc-type="NUMERIC"  precision='1' hibernate-type="boolean" /> 
 <!-- the type-mappings are ordered. This mapping will be consulted last, 
  thus overriden by the previous one if precision=1 for the column -->
 <sql-type jdbc-type="NUMERIC"  hibernate-type="long" /> 
</type-mapping>

<!-- BIN$ is recycle bin tables in Oracle -->
<table-filter match-name="BIN$.*" exclude="true" /> 

<!-- Exclude DoNotWantIt from all catalogs/schemas -->
<table-filter match-name="DoNotWantIt" exclude="true" /> 

<!-- exclude all tables from the schema SCHEMA in catalog BAD. -->
<table-filter match-catalog="BAD" match-schema="SCHEMA" match-name=".*" exclude="true" /> 

<!-- table allows you to override/define how reverse engineering 
     are done for a specific table -->
<table name="ORDERS"> 
 <primary-key>
   <!-- setting up a specific id generator for a table -->
  <generator class="sequence">
    <param name="table">seq_table</param>
  </generator>
   <column name="CUSTID"/>
 </primary-key>
 <column name="NAME" property="orderName" type="string" />
 <!-- control many-to-one and set names for a specific named foreign key constraint -->
 <foreign-key constraint-name="ORDER_CUST">
   <many-to-one property="customer"/>
   <set property="orders"/>
 </foreign-key>
</table>

</hibernate-reverse-engineering>

5.2.1. Schema Selection (<schema-selection>)

<schema-selection> is used to drive which schema's the reverse engineering will try and process.

By default the reverse engineering will read all schemas and then use <table-filter> to decide which tables get reverse engineered and which do not; this makes it easy to get started but can be inefficient on databases with many schemas.

With <schema-selection> it is thus possible to limit the actual processed schemas and thus significantly speed-up the reverse engineering. <table-filter> is still used to then decide which tables will be included/excluded.

Note: If no <schema-selection> is specified, the reverse engineering works as if all schemas should be processed. This is equal to:

<schema-selection/>

which in turn is equal to:

<schema-selection match-catalog=".*" match-schema=".*" match-table=".*"/>

5.2.1.1. Examples

The following will process all tables from MY_SCHEMA.

<schema-selection match-schema="MY_SCHEMA"/>

It is possible to have multiple schema-selection's to support multi-schema reading or simply to limit the processing to very specific tables. The following example process all tables in MY_SCHEMA, a specific CITY table plus all tables that starts with CODES_ in COMMON_SCHEMA.

<schema-selection match-schema="MY_SCHEMA"/>
<schema-selection match-schema="COMMON_SCHEMA" match-table="CITY"/>
<schema-selection match-schema="COMMON_SCHEMA" match-table="CODES_.*"/>

5.2.2. Type mappings (<type-mapping>)

The <type-mapping> section specifies how the JDBC types found in the database should be mapped to Hibernate types. e.g. java.sql.Types.VARCHAR with a length of 1 should be mapped to the Hibernate type yes_no or java.sql.Types.NUMERIC should generally just be converted to the Hibernate type long.

<type-mapping>
 <sql-type
  jdbc-type="integer value or name from java.sql.Types"
  length="a numeric value"
  precision="a numeric value"
  scale="a numeric value"
  not-null="true|false"  
  hibernate-type="hibernate type name"  
 />
</type-mapping>

The number of attributes specificed and the sequence of the sql-type's is important. Meaning that Hibernate will search for the most specific first, and if no specific match is found it will seek from top to bottom when trying to resolve a type mapping.

5.2.2.1. Example

The following is an example of a type-mapping which shows the flexibility and the importance of ordering of the type mappings.

<type-mapping>
 <sql-type jdbc-type="NUMERIC" precision="15" hibernate-type="big_decimal"/>
 <sql-type jdbc-type="NUMERIC" not-null="true" hibernate-type="long" />
 <sql-type jdbc-type="NUMERIC" not-null="false" hibernate-type="java.lang.Long" />
 <sql-type jdbc-type="VARCHAR" length="1" not-null="true" hibernate-type="java.lang.Character"/>
 <sql-type jdbc-type="VARCHAR" hibernate-type="your.package.TrimStringUserType"/>
 <sql-type jdbc-type="VARCHAR" length="1" hibernate-type="char"/>
 <sql-type jdbc-type="VARCHAR" hibernate-type="string"/>
</type-mapping>

The following table shows how this affects an example table named CUSTOMER:

Table 5.1. sql-type examples

Columnjdbc-typelengthprecisionnot-nullResulting hibernate-typeRationale
IDINTEGER 10trueintNothing defined for INTEGER. Falling back to default behavior.
NAMEVARCHAR30 falseyour.package.TrimStringUserTypeNo type-mapping matches length=30 and not-null=false, but type-mapping matches the 2 mappings which only specifies VARCHAR. The type-mapping that comes first is chosen.
INITIALVARCHAR1 falsecharEven though there is a generic match for VARCHAR, the more specifc type-mapping for VARCHAR with not-null="false" is chosen. The first VARCHAR sql-type matches in length but has no value for not-null and thus is not considered.
CODEVARCHAR1 truejava.lang.CharacterThe most specific VARCHAR with not-null="true" is selected.
SALARYNUMERIC 15falsebig_decimalThere is a precise match for NUMERIC with precision 15
AGENUMERIC 3falsejava.lang.Longtype-mapping for NUMERIC with not-null="false"

5.2.3. Table filters (<table-filter>)

The <table-filter> let you specifcy matching rules for performing general filtering/setup for tables, e.g. let you include or exclude specific tables based on the schema or even a specifc prefix.

<table-filter
 match-catalog="catalog_matching_rule"                (1)
 match-schema="schema_matching_rule"                  (2)
 match-name="table_matching_rule"                     (3)
 exclude="true|false"                                 (4)
 package="package.name"                               (5)
/>
(1)

match-catalog (default: .*): Pattern for matching catalog part of the table

(2)

match-schema (default: .*): Pattern for matching schema part of the table

(3)

match-table (default: .*): Pattern for matching table part of the table

(4)

exclude (default: false): if true the table will not be part of the reverse engineering

(5)

package (default: ""): The default package name to use for classes based on tables matched by this table-filter

5.2.4. Specific table configuration (<table>)

<table> allows you to provide explicit configuration on how a table should be reverse engineered. Amongst other things it allow control over the naming of a class for the table, specify which identifier generator should be used for the primary key etc.

<table 
 catalog="catalog_name"                               (1)
 schema="schema_name"                                 (2)
 name="table_name"                                    (3)
 class="ClassName"                                    (4)
>
 <primary-key...>
 <column...>
 <foreign-key...>
</table>
(1)

catalog (Optional): Catalog name for table. Has to be specified if you are reverse engineering multiple catalogs or if it is not equal to hiberante.default_catalog

(2)

schema (Optional): Schema name for table. Has to be specified if you are reverse engineering multiple schemas or if it is not equal to hiberante.default_schema

(3)

name (Required): Name for table

(4)

clase (Optional): The class name for table. Default name is camelcase version of the table name.

5.2.4.1. <primary-key>

A <primary-key> allows you to define a primary-key for tables that does not have such defined in the database, and probably more importantly it allows you to define which identifier strategy that should be used (even for already existing primary-key's).

<primary-key
 <generator class="generatorname">                    (1)
   <param name="param_name">parameter value</param>   (2)
 </generator>
 <key-column...>                                      (3)
</primary-key>
(1)

generator/class (Optional): defines which identifier generator should be used. The class name is any hibernate short hand name or fully quailfied class name for an identifier strategy.

(2)

generator/param (Optional): Allows to specify which parameter with name and value should be passed to the identifier generator

(3)

key-column (Optional): Specifies which column(s ) the primary-key consists of. A key-column is same as column, but does not have the exclude property.

5.2.4.2. <column>

With a <column> it is possible to explicitly name the resulting property for a column. It is also possible to redefine what jdbc and/or hibernate type a column should be processed and finally it is possible to completely exclude a column from processing.

<column
 name="column_name"                                   (1)
 jdbc-type="java.sql.Types type"                      (2)
 type="hibernate_type"                                (3)
 property="propertyName"                              (4)
 exclude="true|false"                                 (5)
/>
(1)

name (Required): Column name

(2)

jdbc-type (Optional): Which jdbc-type this column should be processed as. A value from java.sql.Types, either numerical (93) or the constant name (TIMESTAMP).

(3)

type (Optional): Which hibernate-type to use for this specific column.

(4)

property (Optional): What property name will be generated for this column.

(5)

exclude (default: false): set to true if this column should be ignored.

5.2.4.3. <foreign-key>

The <foreign-key> has two purposes. One for allowing to define foreign-keys in databases that does not support them or does not have them defined in their schema. Secondly, to allow defining the name of the resulting properties (many-to-one and one-to-many's).

Note

<foreign-key
  constraint-name="foreignKeyName"                    (1)
  foreign-catalog="catalogName"                       (2)
  foreign-schema="schemaName"                         (3)
  foreign-table="tableName"                           (4)
 >
 <column-ref local-column="columnName" foreign-column=(5)"foreignColumnName"/>
 <many-to-one                                         (6)
   property="aPropertyName"
   exclude="true|false"/>                             (7)
 <set 
   property="aCollectionName"
   exclude="true|false"/>
</foreign-key>
(1)

constraint-name (Required): Name of the foreign key constraint. Important when naming many-to-one and set. It is the constraint-name that is used to link the processed foreign-keys with the resulting property names.

(2)

foreign-catalog (Optional): Name of the foreign table's catalog. (Only relevant if you want to explicitly define a foreign key)

(3)

foreign-schema (Optional): Name of the foreign table's schema. (Only relevant if you want to explicitly define a foreign key)

(4)

foreign-table (Optional): Name of the foreign table. (Only relevant if you want to explicitly define a foreign key)

(5)

column-ref (Optional): Defines that the foreign-key constraint between a local-column and foreign-column name. (Only relevant if you want to explicitly define a foreign key)

(6)

many-to-one (Optional): Defines that a many-to-one should be created and the property attribute specifies the name of the resulting property. Exclude can be used to explicitly define that it should be created or not.

(7)

set (Optional): Defines that a set should be created based on this foreign-key and the property attribute specifies the name of the resulting (set) property. Exclude can be used to explicitly define that it should be created or not.

5.3. Custom strategy

It is possible to implement a user strategy. Such strategy must implement org.hibernate.cfg.reveng.ReverseEngineeringStrategy. It is recommended that one uses the DelegatingReverseEngineeringStrategy and provide a public constructor which takes another ReverseEngineeringStrategy as argument. This will allow you to only implement the relevant methods and provide a fallback strategy. Example of custom delegating strategy which converts all column names that ends with "PK" into a property named "id".

public class ExampleStrategy extends DelegatingReverseEngineeringStrategy {

 public ExampleStrategy(ReverseEngineeringStrategy delegate) {
  super(delegate);
 }

 public String columnToPropertyName(TableIdentifier table, String column) {
  if(column.endsWith("PK")) {
   return "id";
  } else {
   return super.columnToPropertyName(table, column);
  }
 }
}

5.4. Custom Database Metadata

By default the reverse engineering is performed by reading using the JDBC database metadata API. This is done via the class org.hibernate.cfg.reveng.dialect.JDBCMetaDataDialect which is an implementation of org.hibernate.cfg.reveng.dialect.MetaDataDialect.

The default implementation can be replaced with an alternative implementation by setting the property hibernatetool.metadatadialect to a fully qualified classname for a class that implements JDBCMetaDataDialect.

This can be used to provide database specific optimized metadata reading. If you create an optimized/better metadata reading for your database it will be a very welcome contribution.