Monday, January 19, 2009

2.1.11 Using Type Maps

The two new SQL99 data types that are user-defined types (UDTs), SQL structured types and DISTINCT types, can be custom mapped to a class in the Java programming language. Like all the SQL99 data types, they have standard mappings, but a programmer may create a custom mapping as well. The fact that there is a custom mapping for a particular UDT is declared in a java.util.Map object. This Map object may be the one that is associated with a connection, or it may be one that is passed to a method.
A programmer declares a custom mapping by adding an entry to a Map object. This entry must contain two things: (1) the name of the UDT to be mapped and (2) the Class object for the class in the Java programming language to which the UDT is to be mapped. The class itself, which must implement the SQLData interface, will contain the specific mappings.
Each Connection object created using a driver that supports custom mapping will have an empty type map to which custom mappings may be added. This type map is an instance of the interface java.util.Map, which was new in the Java 2 platform and replaced java.util.Dictionary. Until custom map entries are added to this type map, all operations for STRUCT and DISTINCT values will use the standard mappings (the Struct interface for STRUCT values and the underlying type for DISTINCT values).

The following code fragment, in which con is a Connection object and ADDRESSES is an SQL structured type, demonstrates retrieving the type map associated with con and adding a new entry to it. After the type map is modified, it is set as the new type map for con.

java.util.Map map = con.getTypeMap();
map.put("SchemaName.ADDRESSES", Class.forName("Addresses"));
con.setTypeMap();

The Map object map, the type map associated with con, now contains at least one custom mapping (or more if any mappings have already been added). The programmer will have previously created the class Addresses, probably using a tool to generate it. Note that it is an error to supply a class that does not implement the interface SQLData. The class Addresses, which does implement SQLData, will have a field for each attribute in ADDRESSES, and whenever a value of type ADDRESSES is operated on by a method in the Java programming language, the default will be to map it to an instance of the class Addresses. The type map associated with a connection is the default type map in the sense that a method will use it if no other type map is explicitly passed to it.
Note that the name of the UDT should be the fully-qualified name. For some DBMSs, this will be of the form catalogName.schemaName.UDTName. Many DBMSs, however, do not use this form and, for example, use a schema name but no catalog name. The important thing is to use the form appropriate for a particular DBMS. The DatabaseMetaData methods getCatalogs, getCatalogTerm, getCatalogSeparator, getSchemas and getSchemaTerm give information about a DBMS's catalogs, schemas, preferred terms, and the separator it uses.

Instead of modifying the existing type map, an application can replace it with a completely different type map. This is done with the Connection method setTypeMap, as shown in the following code fragment. It creates a new type map, gives it two entries (each with an SQL UDT name and the class to which values of that type should be mapped), and then installs the new type map as the one associated with the Connection con.

java.util.Map newConnectionMap = new java.util.HashTable();
newConnectionMap.put(
"SchemaName.UDTName1", Class.forName("className1"));
newConnectionMap.put(
"SchemaName.UDTName2", Class.forName("className2"));
con.setTypeMap(newConnectionMap);

The Map object newConnectionMap now replaces the type map originally associated with the Connection con, and it will be used for custom type mappings unless it is itself replaced. Note that the example uses the default constructor for the class HashTable to create the new type map. This class is one of many implementations of java.util.Map provided in the Java 2 platform API, and one of the others could have been used as well.


In the previous examples, the type map associated with a connection was modified to contain additional mappings or set to be a different type map altogether. In either case, though, the connection's type map is the default for custom mapping JDBC types to types in the Java programming language. The next example will show how to supersede the connection's type map by supplying a method with a different type map.

Methods whose implementations may involve a custom mapping for UDTs have two versions, one that takes a type map and one that does not. If a type map is passed to one of these methods, the given type map will be used instead of the one associated with the connection. For example, the Array methods getArray and getResultSet have versions that take a type map and versions that do not. If a type map is passed to a method, it will map the array elements using the given type map. If no type map is specified, the method will use the type map associated with the connection.

The capability for supplying a type map to a method makes it possible for values of the same user-defined type to have different mappings. For example, if two applications are using the same connection and operating on the same column value, one could use the type map associated with the connection, and the other could use a different type map by supplying it as an argument to the appropriate method.

The following code fragment creates a new type map and provides it as a parameter to the Array method getArray.

java.util.Map arrayMap = new java.util.HashTable();
arrayMap.put("SchemaName.DIMENSIONS", Class.forName("Dimensions"));
Dimensions [] d = (Dimensions [])array.getArray(arrayMap);

In the second line, the new type map arrayMap is given an entry with the fully-qualified name of an SQL structured type (SchemaName.DIMENSIONS) and the Java class object (Class.forName("Dimensions")). This establishes the mapping between the Java type Dimensions and the SQL type DIMENSIONS. In the third line, arrayMap is specified as the type map to use for mapping the contents of this Array object, whose base type is SchemaName.DIMENSIONS.

The method getArray will materialize the elements of the SQL99 ARRAY value designated by array, with each element being mapped according to the mapping specified in arrayMap. In other words, each element, which is a value of type Schema.DIMENSIONS, will be translated to an instance of the class Dimensions by mapping the attributes of each DIMENSIONS value to the fields of a Dimensions object. If the base type of the array does not match the UDT named in arrayMap, the driver will convert the array's elements according to the standard mapping. If no type map is specified to the method getArray, the driver uses the mapping indicated in the connection's type map. If that type map has no entry for Schema.DIMENSIONS, the driver will instead use the standard mapping.

0 Comments: