4.3. Class mapping options

If you check the <hibernate-mapping> and <class> elements in the DTD (or the reference documentation), you'll find a few options we haven't discussed so far:

  • Dynamic generation of CRUD SQL statements

  • Entity mutability control

  • Naming of entities for querying

  • Mapping package names

  • Quoting keywords and reserved database identifiers

  • Implementing database naming conventions

4.3.1. Dynamic SQL generation

By default, Hibernate creates SQL statements for each persistent class on startup. These statements are simple create, read, update, and delete operations for reading a single row, deleting a row, and so on.

How can Hibernate create an UPDATE statement on startup? After all, the columns to be updated aren't known at this time. The answer is that the generated SQL statement updates all columns, and if the value of a particular column isn't modified, the statement sets it to its old value.

In some situations, such as a legacy table with hundreds of columns where the SQL statements will be large for even the simplest operations (say, only one column needs updating), you have to turn off this startup SQL generation and switch to dynamic statements generated at runtime. An extremely large number of entities can also impact startup time, because Hibernate has to generate all SQL statements for CRUD upfront. Memory consumption for this query statement cache will also be high if a dozen statements must be cached for thousands of entities (this isn't an issue, usually).

Two attributes for disabling CRUD SQL generation on startup are available on the <class> mapping element:

<class name="Item"
   dynamic-insert="true"
   dynamic-update="true">
...
</class>

The dynamic-insert attribute tells Hibernate whether to include null property values in an SQL INSERT, and the dynamic-update attribute tells Hibernate whether to include unmodified properties in the SQL UPDATE.

If you're using JDK 5.0 annotation mappings, you need a native Hibernate annotation to enable dynamic SQL generation:

@Entity
@org.hibernate.annotations.Entity(
    dynamicInsert = true, dynamicUpdate = true
)
public class Item { ...

The second @Entity annotation from the Hibernate package extends the JPA annotation with additional options, including dynamicInsert and dynamicUpdate.

Sometimes you can avoid generating any UPDATE statement, if the persistent class is mapped immutable.

4.3.2. Making an entity immutable

Instances of a particular class may be immutable. For example, in CaveatEmptor, a Bid made for an item is immutable. Hence, no UPDATE statement ever needs to be executed on the BID table. Hibernate can also make a few other optimizations, such as avoiding dirty checking, if you map an immutable class with the mutable attribute set to false:

<hibernate-mapping default-access="field">
    <class name="Bid" mutable="false">
    ...
    </class>
</hibernate-mapping>

A POJO is immutable if no public setter methods for any properties of the class are exposed—all values are set in the constructor. Instead of private setter methods, you often prefer direct field access by Hibernate for immutable persistent classes, so you don't have to write useless accessor methods. You can map an immutable entity using annotations:

@Entity
@org.hibernate.annotations.Entity(mutable = false)
@org.hibernate.annotations.AccessType("field")
public class Bid { ...

Again, the native Hibernate @Entity annotation extends the JPA annotation with additional options. We have also shown the Hibernate extension annotation @AccessType here—this is an annotation you'll rarely use. As explained earlier, the default access strategy for a particular entity class is implicit from the position of the mandatory @Id property. However, you can use @AccessType to force a more fine-grained strategy; it can be placed on class declarations (as in the preceding example) or even on particular fields or accessor methods.

Let's have a quick look at another issue, the naming of entities for queries.

4.3.3. Naming entities for querying

By default, all class names are automatically "imported" into the namespace of the Hibernate query language, HQL. In other words, you can use the short class names without a package prefix in HQL, which is convenient. However, this auto-import can be turned off if two classes with the same name exist for a given SessionFactory, maybe in different packages of the domain model.

If such a conflict exists, and you don't change the default settings, Hibernate won't know which class you're referring to in HQL. You can turn off auto-import of names into the HQL namespace for particular mapping files with the auto-import="false" setting on the <hibernate-mapping> root element.

Entity names can also be imported explicitly into the HQL namespace. You can even import classes and interfaces that aren't explicitly mapped, so a short name can be used in polymorphic HQL queries:

<hibernate-mapping>
    <import class="auction.model.Auditable" rename="IAuditable"/>
</hibernate-mapping>

You can now use an HQL query such as from IAuditable to retrieve all persistent instances of classes that implement the auction.model.Auditable interface. (Don't worry if you don't know whether this feature is relevant to you at this point; we'll get back to queries later in the book.) Note that the <import> element, like all other immediate child elements of <hibernate-mapping>, is an application-wide declaration, so you don't have to (and can't) duplicate this in other mapping files.

With annotations, you can give an entity an explicit name, if the short name would result in a collision in the JPA QL or HQL namespace:

@Entity(name="AuctionItem")
public class Item { ... }

Now let's consider another aspect of naming: the declaration of packages.

4.3.4. Declaring a package name

All the persistent classes of the CaveatEmptor application are declared in the Java package auction.model. However, you don't want to repeat the full package name whenever this or any other class is named in an association, subclass, or component mapping. Instead, specify a package attribute:

 <hibernate-mapping package="auction.model">
     <classname="Item" table="ITEM">
       ...
     </class>
 </hibernate-mapping>

Now all unqualified class names that appear in this mapping document will be prefixed with the declared package name. We assume this setting in all mapping examples in this book and use unqualified names for CaveatEmptor model classes.

Names of classes and tables must be selected carefully. However, a name you've chosen may be reserved by the SQL database system, so the name has to be quoted.

4.3.5. Quoting SQL identifiers

By default, Hibernate doesn't quote table and column names in the generated SQL. This makes the SQL slightly more readable, and it also allows you to take advantage of the fact that most SQL databases are case insensitive when comparing unquoted identifiers. From time to time, especially in legacy databases, you encounter identifiers with strange characters or whitespace, or you wish to force case sensitivity. Or, if you rely on Hibernate's defaults, a class or property name in Java may be automatically translated to a table or column name that isn't allowed in your database management system. For example, the User class is mapped to a USER table, which is usually a reserved keyword in SQL databases. Hibernate doesn't know the SQL keywords of any DBMS product, so the database system throws an exception at startup or runtime.

If you quote a table or column name with backticks in the mapping document, Hibernate always quotes this identifier in the generated SQL. The following property declaration forces Hibernate to generate SQL with the quoted column name "DESCRIPTION". Hibernate also knows that Microsoft SQL Server needs the variation [DESCRIPTION] and that MySQL requires 'DESCRIPTION'.

<property name="description"
          column="'DESCRIPTION'"/>

There is no way, apart from quoting all table and column names in backticks, to force Hibernate to use quoted identifiers everywhere. You should consider renaming tables or columns with reserved keyword names whenever possible. Quoting with backticks works with annotation mappings, but it's an implementation detail of Hibernate and not part of the JPA specification.

4.3.6. Implementing naming conventions

We often encounter organizations with strict conventions for database table and column names. Hibernate provides a feature that allows you to enforce naming standards automatically.

Suppose that all table names in CaveatEmptor should follow the pattern CE_<table name>. One solution is to manually specify a table attribute on all <class> and collection elements in the mapping files. However, this approach is time-consuming and easily forgotten. Instead, you can implement Hibernate's NamingStrategy interface, as in listing 4.1.

Listing 4-1. NamingStrategy implementation
public class CENamingStrategy extends ImprovedNamingStrategy {

      public String classToTableName(String className) {
            return StringHelper.unqualify(className);
      }

      public String propertyToColumnName(String propertyName) {
          return propertyName;
    }

      public String tableName(String tableName) {
            return "CE_" + tableName;
      }

      public String columnName(String columnName) {
          return columnName;
    }

      public String propertyToTableName(String className,
                                      String propertyName) {
            return "CE_"
                        + classToTableName(className)
                        + '_'
                        + propertyToColumnName(propertyName);
      }
}

You extend the ImprovedNamingStrategy, which provides default implementations for all methods of NamingStrategy you don't want to implement from scratch (look at the API documentation and source). The classToTableName() method is called only if a <class> mapping doesn't specify an explicit table name. The propertyToColumnName() method is called if a property has no explicit column name. The tableName() and columnName() methods are called when an explicit name is declared.

If you enable this CENamingStrategy, the class mapping declaration

<class name="BankAccount">

results in CE_BANKACCOUNT as the name of the table.

However, if a table name is specified, like this,

<class name="BankAccount" table="BANK_ACCOUNT">

then CE_BANK_ACCOUNT is the name of the table. In this case, BANK_ACCOUNT is passed to the tableName() method.

The best feature of the NamingStrategy interface is the potential for dynamic behavior. To activate a specific naming strategy, you can pass an instance to the Hibernate Configuration at startup:

Configuration cfg = new Configuration();
cfg.setNamingStrategy( new CENamingStrategy() );
SessionFactory sessionFactory sf =
                    cfg.configure().buildSessionFactory();

This allows you to have multiple SessionFactory instances based on the same mapping documents, each using a different NamingStrategy. This is extremely useful in a multiclient installation, where unique table names (but the same data model) are required for each client. However, a better way to handle this kind of requirement is to use an SQL schema (a kind of namespace), as already discussed in chapter 3, section 3.3.4, "Handling global metadata."

You can set a naming strategy implementation in Java Persistence in your persistence.xml file with the hibernate.ejb.naming_strategy option.

Now that we have covered the concepts and most important mappings for entities, let's map value types.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.12.74.141