JBoss.orgCommunity Documentation

Chapter 6. Controlling reverse engineering

6.1. Default reverse engineering strategy
6.2. hibernate.reveng.xml file
6.2.1. Schema Selection (<schema-selection>)
6.2.2. Type mappings (<type-mapping>)
6.2.3. Table filters (<table-filter>)
6.2.4. Specific table configuration (<table>)
6.3. Custom strategy
6.4. Custom Database Metadata

When using the <jdbcconfiguration>, the ant task will read the database metadata and thus will 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 a 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.

Thus, further in this chapter we will discuss how you can configure the process of a reverse engineering, what default reverse engineering strategy includes as well as some custom concepts.

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.

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 (it's just basic XML) or you can use the Hibernate plugins which have a specialized editor.

Note:

Many databases are 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.

Below you can see an example of a reveng.xml. Following the example gives you 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 for 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 precisely -->
 <sql-type jdbc-type="NUMERIC"  precision='1' hibernate-type="boolean" /> 
 <!-- the type-mappings are ordered. This mapping will be consulted last, 
  thus overridden 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 
     is 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>
   <key-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>
 <!-- can also control a pure (shared pk) one-to-one  -->
  <foreign-key constraint-name="ADDRESS_PERSON">
   <one-to-one exclude="false"/>
   <inverse-one-to-one exclude="true"/>
  </foreign-key>
</table>

</hibernate-reverse-engineering>

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 specified 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.

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> allows you to provide explicit configuration on how a table should be reverse engineered. Amongst other things it allows controlling over the naming of a class for the table, specifying which identifier generator should be used for the primary key etc.


<table 
 catalog="catalog_name"
 schema="schema_name"
 name="table_name"
 class="ClassName"
>
 <primary-key.../>
 <column.../>
 <foreign-key.../>
 </table>

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, one-to-one and one-to-many's).


<foreign-key
  constraint-name="foreignKeyName"
  foreign-catalog="catalogName"
  foreign-schema="schemaName"
  foreign-table="tableName"
 >
 <column-ref local-column="columnName" foreign-column="foreignColumnName"/>
 <many-to-one 
   property="aPropertyName"
   exclude="true|false"/>
 <set 
   property="aCollectionName"
   exclude="true|false"
   
 <one-to-one 
   property="aPropertyName"
   exclude="true|false"/>
 <inverse-one-to-one
   property="aPropertyName"
   exclude="true|false"/>
   </foreign-key>

Table 6.6. Foreign-key attributes

Attribute nameDefinitionAttribute use

constraint-name

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

Required

foreign-catalog

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

Optional

foreign-schema

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

Optional

foreign-table

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

Optional

column-ref

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).

Optional

many-to-one

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.

Optional

set

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.

Optional

one-to-one

Defines that a one-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.

Optional

inverse-one-to-one

Defines that an inverse one-to-one should be created based on this foreign-key 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.

Optional


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 an 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);
  }
 }
}

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.