Strongly Typed Object SQL

38 309 0
Strongly Typed Object SQL

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

ResultSetMetaData getMetaData( ) String getName( ) 15.7.4 STRUCT Implements Struct The oracle.sql.STRUCT class implements the java.sql.Struct interface. A STRUCT not only implements a Struct, but is also used along with a StructDescriptor object to create a new Struct object in your Java program. Beyond the functionality defined by the java.sql.Struct interface, the Struct class also has the following proprietary constructor and methods, all of which can throw a SQLException: STRUCT STRUCT(StructDescriptor type, Connection conn, Object attributes[]) OracleConnection getConnection( ) StructDescriptor getDescriptor( ) Datum[] getOracleAttributes( ) boolean isConvertibleTo(Class jClass) Object toJdbc( ) 15.7.5 REF Implements Ref The oracle.sql.REF class implements the java.sql.Ref interface. A Ref is used as a pointer to an object row in the database. Besides implementing the java.sql.Ref interface, REF also has the following proprietary methods, all of which can throw a SQLException: OracleConnection getConnection( ) StructDescriptor getDescriptor( ) STRUCT getSTRUCT( ) Object getValue( ) Object getValue(Dictionary map) boolean isConvertibleTo(Class jClass) void setValue(Object value) Object toJdbc( ) Now you know how to use a Struct, Array, and Ref to insert objects into a database, update objects, delete objects, and select objects from a database. So let's move on to Chapter 16, where you'll learn to do the same with the strongly typed SQLData and CustomDatum interfaces. Chapter 16. Strongly Typed Object SQL Strongly typed object SQL refers to the use of client-side custom Java classes to manipulate database-side SQL objects. The classes themselves are referred to as custom because a Java class is created to mirror its database counterpart. To mirror database objects you can use one of two approaches: the JDBC API's standard SQLData interface or Oracle's CustomDatum interface. With the SQLData interface, a database object is represented as a custom Java class that implements the SQLData interface; however, a collection is still represented by an Array object, and a reference is still represented by a Ref object. With the Oracle CustomDatum interface, a database object is represented as an Oracle custom class file that implements the CustomDatum and CustomDatumFactory interfaces. Unlike the SQLData interface, The CustomDatum interface supports all database object types, including references and collections. For example, in Chapter 15 we used a Struct object to manipulate a database object, an Array object for collections, and a Ref object to hold a database reference. With strongly typed object SQL, you'll use a custom Java class to manipulate a database object, an Array object or another custom Java class for a collection, and a Ref object or yet another custom Java class to hold a database reference. If you're concerned with portability, then you should use the SQLData interface. Otherwise, since the SQLData interface currently doesn't provide support for collections and references, or if you're performing a data-processing task, I'd use Oracle's CustomDatum interface. In this chapter, we'll cover both the standard java.sql.SQLData and the Oracle oracle.sql.CustomDatum interfaces. Before we do, we'll spend some time in the next section covering how to use Oracle's JPublisher utility. JPublisher can be used to automatically generate the custom Java classes for both the SQLData and CustomDatum interfaces. It's important to take the time to read the next section because we use JPublisher throughout this chapter to generate the custom Java classes for the examples. 16.1 JPublisher Oracle's JPublisher utility queries the database for the database object types you specify, and using the mapping options you specify, creates either a SQLData or a CustomDatum implementation of a Java class for each SQL object. JPublisher itself is a Java program that has a command-line interface. You specify its runtime parameters on the command line when you execute the program, but all the command-line options must be listed on one line, and this is an invitation for errors. Alternatively, instead of typing a long list of parameters on the command line, you can execute JPublisher using properties and input files. We'll start by covering all the command-line options, then discuss how most of them can be entered into a properties file, continue with input file syntax, and finish up with an outline of how to use JPublisher to generate a custom Java class. 16.1.1 Command-Line Options Execute JPublisher by executing the jpub program at a host command prompt. Specify any command-line options by using the following syntax: -option_name=value which breaks down as: option_name Refers to one of the valid command-line options value A valid value for the corresponding option_name There should be no spaces following the switch character (-), nor around the equal sign (=). Following are the options available for use with JPublisher along with descriptions of their possible values. Default values are underlined. -builtintypes={jdbc|oracle} Controls type mappings, such as the choice between standard Java classes such as String and Oracle Java classes such as CHAR, for nonnumeric, non-LOB, nonuser- defined SQL or PL/SQL data types -case={lower|mixed|same|upper} Controls how JPublisher translates database type names to Java class and attribute names. lower, same, and upper are self-explanatory. For mixed, JPublisher uses the Java naming convention, removing any underscore ( _ ) or dollar sign ($) characters but using their placement in the database type name to denote the beginning of different words to support capitalization. -dir= directory_name Controls where JPublisher writes the class files it generates. The default is the current directory. -driver= driver_name Controls which JDBC driver to use to access the database. The default is oracle.jdbc.driver.OracleDriver. -encoding= encoding_character_set Controls the character set encoding used when writing the class files. The default is the value in the system property file.encoding. -input= input_filename Specifies the name of a mapping file. A mapping file allows you to specify the data type mapping between SQL and Java in a file rather than on the command line. -lobtypes={jdbc|oracle} Controls the data type mapping between SQL and Java for the BLOB and CLOB SQL types. -methods={true|false|named} Controls whether JPublisher creates wrappers for a database type's static and member methods. When true or named, JPublisher creates .sqlj files as part of a CustomDatum interface class. CustomDatum classes are created because the SQLData interface does not provide a Connection object, which is required to make a stored procedure call, while the CustomDatum classes using SQLJ provide the required Connection object. When false, JPublisher creates .java files. Regardless, JPublisher always generates .java files for a reference, varying array, or nested table type. If the value is named, then only those methods listed in the input file are wrapped. -numbertypes={bigdecimal|jdbc|objectjdbc|oracle} Controls type mappings for the numeric types. The mapping types listed affect numeric data types differently, so they require some additional explanation: jdbc Maps most numeric types to primitive Java types such as short, int, long, float, double, etc. Choosing jdbc means you can't properly handle database NULL values in your program! objectjdbc Maps the numeric types to corresponding Java wrapper classes such as Short, Integer, Long, Float, Double, etc. This makes detecting database NULL values feasible. bigdecimal Maps all numeric types to BigDecimal. Not too efficient, but it can handle any number Oracle throws its way. oracle Maps all data types to their corresponding oracle.sql.* types and maps user-defined types to CustomDatum. Very efficient, but not portable. -omit_schema_names Controls whether the schema name is used in the generated classes. The default is to include the schema name. -package= java_package_name Specifies a Java package name to be included in the generated classes. -props= properties_filename Specifies the name of a properties file. A properties file allows you to specify the command-line options covered in this section in a file that in turn is read by JPublisher. -sql= type_name: super_class_name: map_class_name -sql= type_name: map_class_name Specifies the name of a database object, an optional Java superclass name, and a Java class name for which to generate class files. You can use this option multiple times to specify multiple object types for which to generate classes: type_name Identifies the name of the database type. If you're going to extend a superclass, then use the first format and specify a super_class_name that you will extend with a subclass: the map_class_name. super_class_name The name of an intermediate class file that you will then extend. map_class_name The name that will be used in the type map. Note that the case you specify overrides any other case settings. -url= database_url Specifies the database URL. The default value is jdbc:oracle:oci8:@. -user= username/ password A username and password that have access to the database types for which you want to generate classes. This information must be specified in order to use JPublisher. -usertypes={jdbc|oracle} Controls mappings for user-defined types and determines whether the SQLData or CustomDatum interface is implemented by the generated classes. Selecting jdbc results in the use of the SQLData interface, while a value of oracle results in the use of the CustomDatum interface. All of the properties in the previous list, except for -props, can be specified in a properties file. And for your sanity's sake, I hope you use one. To show you why I feel the way I do, I'll provide an example of a JPublisher command where I specify the properties on the command line. If you wish to create classes for the five object types introduced in Chapter 14 and wish for those classes to use SQLData interface implementations, use the following command at the host's command prompt: jpub.exe -user=scott/tiger -methods=false -builtintypes=jdbc - lobtypes=jdbc - numbertypes=objectjdbc -usertypes=jdbc - sql=LOCATION_TYP:JLocation:Location -sql= PERSON_IDENTIFIER_TYPE_TYP:PersonIdentifierType -sql= PERSON_IDENTIFIER_TYP: PersonIdentifier -sql=PERSON_TYP:JPerson:Person - sql=PERSON_LOCATION_TYP: PersonLocation Rather confusing, isn't it? There's more than ample opportunity to make a mistake when typing, isn't there? To better organize the process of generating custom classes using JPublisher, use a properties file to hold all the command-line options except -props and -sql. For the -sql property, use an input file. We'll examine both of these file types in the next two sections. 16.1.2 Property File Syntax Instead of listing all the desired command-line options on the command line when you run JPublisher, you can put them in a properties file and specify the properties filename on the command line with the -props option. To enter options in a properties file, prefix them with jpub For example, to specify the -user option, type the following into a text file: jpub.user=scott/tiger For our earlier example, the contents of a properties file might look like this: jpub.user=scott/tiger jpub.methods=false jpub.builtintypes=jdbc jpub.lobtypes=jdbc jpub.numbertypes=objectjdbc jpub.usertypes=jdbc jpub.input=sqldata.input Be warned that trailing spaces on your property values will make them invalid -- for example, "jdbc " with a trailing space character, is not recognized, but jdbc is recognized. If you make the mistake of leaving a trailing space character, you'll get an error message similar to this: ERROR: Option -builtintypes=jdbc is invalid This error will drive you crazy trying to figure out what's wrong when your option setting looks right. 16.1.3 Input File Syntax Instead of specifying the database types to generate classes on the command line, as we did in the earlier example, you can specify your class file generation options (those specified with the - sql option) in an input file that you in turn specify on the command line with the -input option. Alternatively, you can specify the input file in the properties file with the jpub.input property. An input file is a text file with the following syntax (items in brackets are optional): SQL [schema.]{type_name | package_name} [GENERATE [java_package_name.]java_super_class_name] [AS [java_package_name.] java_map_class_name] [TRANSLATE member_name AS java_name [,member_name AS java_name .]] which breaks down as: schema The database object type's schema name. type_name The database object type's name. package_name The name of a database package. java_package_name The name of the Java package to include in a generated class. GENERATE A clause that determines the name of the class file that will be generated. java_super_class_name The name of the class generated with the expectation that the class will be extended by java_map_class_name, which in turn will be manually coded by a programmer to extend java_super_class_name. If the GENERATE clause is omitted, then the AS clause's java_map_class_name is generated. AS A clause that determines the name of a subclass if the GENERATE clause is used or the name of the generated class if the GENERATE clause is omitted. java_map_class_name The name of the class that will subclass the generated class file if the GENERATE clause is used or the name of the class file that is generated if the GENERATE clause is omitted. It's also the class name that is used when modifying the class map in your Java program (more on this later). TRANSLATE A clause that renames a type's static or member methods. member_name The name of a type method. java_name The name you wish to use for the method in the generated class. 16.1.4 Writing a Class That Extends a Generated Class If you use the GENERATE clause, you need to write the subclass that will extend the superclass. When you do, your subclass must: • Have a no argument constructor that calls the no argument constructor for the superclass. For a CustomDatum class, you must also have a constructor that takes a Connection object and passes it to the superclass, and you must have another constructor that takes a ConnectionContext object and passes it to the superclass. • Implement the CustomDatum or SQLData interface. Your subclass activity does this automatically by inheriting from its parent class. • Implement CustomDatumFactory if it's implementing the CustomDatum interface. Now that you have some background on how JPublisher works, let's actually use it to generate a SQLData class for the type person_typ. 16.2 The SQLData Interface The java.sql.SQLData interface allows you to create custom Java classes that mirror your user-defined database types. But, as my mother-in-law would say, "What do you get for that?" If you haven't used an object database before, using a database to store objects, that is, both data and methods, requires a shift in your thinking. Instead of just modeling the data around, and establishing relationships between, different things, you can complete the puzzle by including a thing's behavior. When you create a user-defined data type in the database, you can also include methods for its behaviors. You can continue to use relational SQL and retrieve the object data as though it were in tables, and execute object methods as though they were separate stored procedures, but with the SQLData interface, you don't have to. Instead, you can create a Java object that will mimic your database object and retrieve an object directly from the database into your Java program as an object. There is no longer any need to do any relational-to-object mapping in your Java program. Now you can use objects. When you use SQLData, follow these steps: 1. Create custom Java classes to represent database user-defined data types. 2. Add the custom Java classes to the Connection object's type map. 3. For insert and update operations, use a PreparedStatement object with an appropriately formulated SQL statement. 4. Use the getObject( ) or setObject( ) accessor methods to get and set the object values as needed. Since we will use JPublisher to write our custom Java classes, I will not go into any great detail about hand-coding them. However, I will briefly talk about the process for doing that in the next section. 16.2.1 Hand-Coding a SQLData Implementation Writing your own SQLData classes is really not that difficult. The SQLData interface requires you to implement three methods: String getSQLTypeName( ) void readSQL(SQLInput stream, String typeName) void writeSQL(SQLOutput stream) The getSQLTypeName( ) method returns the database type name. The readSQL( ) method uses the SQLInput stream that is passed to it from the JDBC driver to populate the attributes in the custom Java class. For each attribute in the database type, the appropriate SQLInput object readXXX( ) method is called in the same order as the attributes in the database type. For example, let's take location_typ. It's defined as: create type LOCATION_typ as object ( location_id number, parent_location_id number, code varchar2(30), name varchar2(80), start_date date, end_date date, map member function get_map return varchar2, static function get_id return number ); / Assuming that the class's variables are defined elsewhere in the class, a readSQL( ) method for this type would look something like this: public void readSQL(SQLInput stream, String type) throws SQLException { locationId = stream.readBigDecimal( ); parentLocationId = stream.readBigDecimal( ); code = stream.readString( ); name = stream.readString( ); startDate = stream.readTimestamp( ); endDate = stream.readTimestamp( ); } The writeSQL( ) method for the type, which writes the data back to the database, would look something like this: public void writeSQL(SQLOutput stream) throws SQLException { stream.writeBigDecimal(locationId); stream.writeBigDecimal(parentLocationId); stream.writeString(code); stream.writeString(name); stream.writeTimestamp(startDate); stream.writeTimestamp(endDate); } If you want the custom Java class to be useful, give it a set of applicable accessor methods so it follows the JavaBeans standard. Accordingly, for each attribute, create corresponding get and set methods. Putting it all together, you have the following class definition: import java.sql.*; public class SQLDataLocation implements SQLData, Serializable { private java.math.BigDecimal locationId; private java.math.BigDecimal parentLocationId; private String code; private String name; private java.sql.Timestamp startDate; private java.sql.Timestamp endDate; // A no argument constructor public SQLDataLocation( ) { } public void readSQL(SQLInput stream, String type) throws SQLException { locationId = stream.readBigDecimal( ); parentLocationId = stream.readBigDecimal( ); code = stream.readString( ); name = stream.readString( ); startDate = stream.readTimestamp( ); endDate = stream.readTimestamp( ); } public void writeSQL(SQLOutput stream) throws SQLException { stream.writeBigDecimal(locationId); stream.writeBigDecimal(parentLocationId); stream.writeString(code); stream.writeString(name); stream.writeTimestamp(startDate); stream.writeTimestamp(endDate); } public String getSQLTypeName( ) throws SQLException { return "SCOTT.LOCATION_TYP"; } public java.math.BigDecimal getLocationId( ) { return locationId; } public java.math.BigDecimal getParentLocationId( ) { return parentLocationId; } public String getCode( ) { return code; } public String getName( ) { return name; } public java.sql.Timestamp getStartDate( ) { return startDate; } public java.sql.Timestamp getEndDate( ) { return endDate; } public void setLocationId(java.math.BigDecimal locationId) { this.locationId = locationId; } public void setParentLocationId(java.math.BigDecimal parentLocationId) { this.parentLocationId = parentLocationId; } public void setCode(String code) { this.code = code; } public void setName(String name) { this.name = name; } public void setStartDate(java.sql.Timestamp startDate) { this.startDate = startDate; } public void setEndDate(java.sql.Timestamp endDate) { this.endDate = endDate; } } But what if you have 100, or 500, or maybe even 1,000 types for which you need to create Java classes? Manually coding the classes can be an onerous and unproductive task, especially when you stop to consider that JPublisher can do the job for you. 16.2.2 Using JPublisher to Generate SQLData Classes The process of creating custom Java classes for your database types with JPublisher is: 1. Create database object types. 2. Create a JPublisher mapping file, referred to as the input file. 3. Create a JPublisher properties file that points to the mapping file. 4. Execute JPublisher using the -props option. 5. Compile any .sqlj files created by JPublisher in order of dependence. 6. Compile any .java files created by JPublisher in order of dependence. 16.2.2.1 Creating database objects We covered step 1, creating database objects, in Chapter 14. In that chapter, we created several types, so we won't repeat that step here. We ended up creating six types for our examples: location_typ person_identifier_type_typ person_identifier_typ person_identifier_tab person_typ person_location_typ Let's proceed to step 2 and create a mapping file. 16.2.2.2 Creating a mapping file for SQLData Of the six types mentioned previously, two, location_typ and person_typ, have methods. Since the SQLData interface does not support database object methods, we need to create a superclass using JPublisher and then later hand-code a subclass that implements their methods. So for these two types, we use the GENERATE clause to create a superclass. Then later, we create a subclass that implements JPublisher's generated class, which adds wrapper methods to call the database type's methods. For the other four types, we simply use the AS clause. Here's our mapping file, sqldata.input: SQL LOCATION_TYP GENERATE JLocation AS Location SQL PERSON_IDENTIFIER_TYPE_TYP AS PersonIdentifierType SQL PERSON_IDENTIFIER_TYP AS PersonIdentifier SQL PERSON_TYP GENERATE JPerson AS Person SQL PERSON_LOCATION_TYP AS PersonLocation The first line instructs JPublisher to generate a superclass JLocation that will be extended by the subclass Location from the database type LOCATION_TYP. Remember that although the case of the database data type is not important, the case of the GENERATE and AS clause's class [...]... import import import import import java .sql. SQLException; oracle.jdbc.driver.OracleConnection; oracle.jdbc.driver.OracleTypes; java .sql. SQLData; java .sql. SQLInput; java .sql. SQLOutput; oracle .sql. STRUCT; oracle.jpub.runtime.MutableStruct; public class JLocation implements SQLData { public static final String _SQL_ NAME = "SCOTT.LOCATION_TYP"; public static final int _SQL_ TYPECODE = OracleTypes.STRUCT; private... a Location object into LOCATION_OT 5 Inserts a PersonLocation object into PERSON_LOCATION_OT 6 Updates a Person object 7 Updates a Person object using a Ref object 8 Retrieves a Person object Example 16-1 The TestSQLData interface import import import import import java.io.*; java.math.*; java .sql. *; java.text.*; oracle.jdbc.driver.*; public class TestSQLData { Connection conn; public TestSQLData( )... use SQLJ, you need to make sure its class file is on the ClassPath In the Windows environment, you should have something like the following as part of your ClassPath: C:\Oracle\Ora81\sqlj\lib\translator.zip; C:\Oracle\Ora81\sqlj\lib\runtime.zip; Assuming you do have the correct ClassPath setting, compile the sqlj files using SQLJ by typing the following commands at the command prompt: sqlj sqlj sqlj sqlj... " + "and first_name = 'Tim' "); 16.2.6.5 Getting an object value from a result set The last step in the object- retrieval process is to get the object value from the result set and assign it to your local object variable Since the type map has already been updated for the object, when you call a ResultSet object' s getObject( ) method to retrieve the object, JDBC will automatically instantiate the appropriate... available in the object instance Now we are retrieving objects from the database! Since we've covered the processes for inserting and selecting an object, we're ready to cover the process for updating an object 16.2.7 Updating an Object The process for updating an object is very similar to selecting and then inserting an object, with two exceptions First, you retrieve an existing object and update... SQL* Plus to verify the existence of the object in the PERSON_OT object table 16.2.6 Retrieving an Object Now that you know how to insert an object, let's take a look at how to retrieve one Once again, the process for retrieving a database object is very similar to the process used in Chapter 15, except this time, you use your custom Java class instead of a Struct object Again, assuming that you have updated... setEndDate(stream.readTimestamp( )); } public void writeSQL(SQLOutput stream) throws SQLException { stream.writeBigDecimal(getLocationId( )); stream.writeBigDecimal(getParentLocationId( stream.writeString(getCode( )); stream.writeString(getName( )); stream.writeTimestamp(getStartDate( )); stream.writeTimestamp(getEndDate( )); } public String getSQLTypeName( { return _SQL_ NAME; } )); ) throws SQLException /* accessor methods... instance of an object of the Java type you specified, and a call to setObject( ) expects an instance of the Java type you specified If you don't update the type map, a call to the getObject( ) method gives you its default object, a Struct, while a call to a the setObject( ) methods expects a Struct To add entries to a type map, follow these steps: 1 Get the existing type map from a Connection object 2 Add... getObject( ) with a Type Map If all you do in your program is retrieve objects from a database, you have another option at your disposal Instead of updating your connection's type map, you can create a new type map and pass it to one of the overloaded forms of the getObject( ) method Here are the signatures for the two forms of the getObject( ) method that allow you to specify a type map: Object getObject(int... rows updated"); conn.commit( ); } catch (SQLException e) { System.err.println( "SQL Error: " } finally { if (rslt != null) try { rslt.close( ); } catch if (stmt != null) try { stmt.close( ); } catch if (pstmt != null) try { pstmt.close( ); } catch } + e.getMessage( )); (SQLException ignore) { } (SQLException ignore) { } (SQLException ignore) { } // Update an object using REF get/setValue( ) try { Ref . do the same with the strongly typed SQLData and CustomDatum interfaces. Chapter 16. Strongly Typed Object SQL Strongly typed object SQL refers to the use. Ref object to hold a database reference. With strongly typed object SQL, you'll use a custom Java class to manipulate a database object, an Array object

Ngày đăng: 29/09/2013, 09:20

Từ khóa liên quan

Tài liệu cùng người dùng

  • Đang cập nhật ...

Tài liệu liên quan