Tải bản đầy đủ (.pdf) (38 trang)

Strongly Typed Object SQL

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (113.9 KB, 38 trang )

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
names will be used in the classes themselves. The second line instructs JPublisher to generate
the PersonIdentifierType class from the database data type
PERSON_IDENTIFIER_TYPE_TYP. After executing JPublisher, you end up with five classes:
JLocation, PersonIdentifierType, PersonIdentifier, JPerson, and
PersonLocation. But what about the sixth type, PERSON_IDENTIFIER_TAB? When using the
SQLData interface, you'll use a java.sql.Array for Oracle collections, just as we did in
Chapter 15.
Now that we have the mapping, or input, file written, let's move on to the properties file.
16.2.2.3 Creating a properties file for SQLData
The properties file will allow you to list the properties you can pass on the command line in a text
file. Using a properties file ensures that you use the same properties when generating all your
classes. Here's the properties file sqldata.properties, which we'll use for generating the SQLData
classes:
jpub.user=scott/tiger
jpub.methods=false
jpub.builtintypes=jdbc
jpub.lobtypes=jdbc
jpub.numbertypes=objectjdbc
jpub.usertypes=jdbc
jpub.input=sqldata.input
This breaks down as:
user
Specifies the username and password to use when logging into the database.
methods

Set to false because JPublisher does not support the creation of wrapper methods for
the SQLData interface.
builtintypes
Set to jdbc, so java.sql.String is used instead of oracle.sql.CHAR, and so
forth.
lobtypes
Also set to jdbc to get the JDBC LOB types and not the Oracle LOB types.
numbertypes
Set to objectjdbc, so Java wrapper classes such as Integer and Double are used
instead of the Java primitives such as int and double.
usertypes
This is critical and determines whether JPublisher generates SQLData or CustomDatum
classes. In this case, we specify jdbc to generate SQLData classes.
input
Specifies the name of the input file.
As stated earlier, you can specify all these values on the command line. However, a properties
file is a tidier approach.
16.2.2.4 Executing JPublisher
Now that you have an input and a properties file, you can generate the classes by executing
JPublisher with the following command at the command prompt:
jpub -props=sqldata.properties
Given the input and properties we created earlier, you now have five new class source files:
JLocation.java
PersonIdentifierType.java
PersonIdentifier.java
JPerson.java
PersonLocation.java
You'll need to compile these generated classes before you attempt to use them in another
program.
16.2.2.5 Examining JPublisher's output

Before we move on to using the classes generated by JPublisher, let's look at the source code
that JPublisher created for the superclass JLocation.java:
import java.sql.SQLException;
import oracle.jdbc.driver.OracleConnection;
import oracle.jdbc.driver.OracleTypes;
import java.sql.SQLData;
import java.sql.SQLInput;
import java.sql.SQLOutput;
import oracle.sql.STRUCT;
import 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 java.math.BigDecimal m_locationId;
private java.math.BigDecimal m_parentLocationId;
private String m_code;
private String m_name;
private java.sql.Timestamp m_startDate;
private java.sql.Timestamp m_endDate;

/* constructor */
public JLocation( )
{
}

public void readSQL(SQLInput stream, String type)
throws SQLException

{
setLocationId(stream.readBigDecimal( ));
setParentLocationId(stream.readBigDecimal( ));
setCode(stream.readString( ));
setName(stream.readString( ));
setStartDate(stream.readTimestamp( ));
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( ) throws SQLException
{
return _SQL_NAME;
}

/* accessor methods */
public java.math.BigDecimal getLocationId( )
{ return m_locationId; }

public void setLocationId(java.math.BigDecimal locationId)

{ m_locationId = locationId; }


public java.math.BigDecimal getParentLocationId( )
{ return m_parentLocationId; }

public void setParentLocationId(java.math.BigDecimal parentLocationId)
{ m_parentLocationId = parentLocationId; }


public String getCode( )
{ return m_code; }

public void setCode(String code)
{ m_code = code; }


public String getName( )
{ return m_name; }

public void setName(String name)
{ m_name = name; }


public java.sql.Timestamp getStartDate( )
{ return m_startDate; }

public void setStartDate(java.sql.Timestamp startDate)
{ m_startDate = startDate; }


public java.sql.Timestamp getEndDate( )
{ return m_endDate; }

public void setEndDate(java.sql.Timestamp endDate)
{ m_endDate = endDate; }

}
Overall, it's pretty similar to the SQLData interface code we hand-coded for type location_typ
earlier. Nothing earth-shattering. And that's my point. Why should you write this generic code
when your computer can do it for you? However, since the SQLData interface does not define
database object method support, and therefore, JPublisher does not support the creation of
wrappers for database object methods, you'll have to write some code after all. So let's take a
look at extending a superclass.
16.2.2.6 Extending a generated superclass
Now that we have the classes generated by JPublisher, we need to create the subclasses
Location and Person for JLocation and JPerson. Since the process is similar for both, and
person_typ has more methods, I'll cover the Person class here.
Reviewing the criteria that we covered earlier for extending a JPublisher class, all we need to do
in this instance is create a class that extends JPerson and has a no argument constructor that
calls its parent class's no argument constructor. Accordingly, here's a minimal subclass:
public class Person extends JPerson {

public Person( ) {
super( );
}
}
But what good is it to subclass JPerson unless we add some functionality? In the case of
Person, we want to implement methods that call the member methods defined for person_typ
in the database. The following is our fully coded subclass with all of person_typ type's methods
implemented:

import java.math.*;
import java.sql.*;

public class Person extends JPerson {
private Connection conn = null;

public Person( ) {
super( );
}

// We've added a constructor that takes a connection so we
// have one available to make stored-procedure calls
public Person(Connection conn) {
super( );
setConnection(conn);
}

public BigDecimal getId( ) throws SQLException {
BigDecimal id = null;
if (conn!=null) {
Person thisPerson = this;
CallableStatement cstmt = conn.prepareCall(
"{? = call " + getSQLTypeName( ) + ".GET_ID( )}");
cstmt.registerOutParameter(1, Types.NUMERIC);
cstmt.execute( );
id = cstmt.getBigDecimal(1);
}
return id;
}


public Integer getAge( ) throws SQLException {
Integer age = null;
if (conn!=null) {
Person thisPerson = this;
CallableStatement cstmt = conn.prepareCall(
"{? = call " + getSQLTypeName( ) + ".GET_AGE( ? )}");
cstmt.registerOutParameter(1, Types.NUMERIC);
cstmt.setObject(2, thisPerson);
cstmt.execute( );
age = new Integer(cstmt.getInt(1));
}
return age;
}

public Integer getAgeOn(Timestamp date) throws SQLException {
Integer age = null;
if (conn!=null) {
Person thisPerson = this;
CallableStatement cstmt = conn.prepareCall(
"{? = call " + getSQLTypeName( ) + ".GET_AGE_ON( ?, ? )}");
cstmt.registerOutParameter(1, Types.NUMERIC);
cstmt.setObject(2, thisPerson);
cstmt.setTimestamp(3, date);
cstmt.execute( );
age = new Integer(cstmt.getInt(1));
}
return age;
}

// We've also added a setter method to set the connection

// so one is available for the stored-procedure calls
public void setConnection(Connection conn) {
this.conn = conn;
}

}
The first thing of importance in this definition for a Person class is that we've added a private
variable conn to hold a Connection reference. Without a connection we can't execute the
stored procedures using a callable statement. In conjunction with conn, we've added a second
constructor that takes a Connection object as a parameter and stores it in conn, and we've
also added a setConnection( ) accessor method to set the connection. Now, when creating a
new Person instance, we can use the alternate constructor to set the connection:
Person person = new Person(conn);
Or, if we have retrieved a person from the database, we can call the setConnection( )
method to initialize the Connection in the Person instance.
The first method in the Person class is getId( ). getId( ) is a wrapper method for the
person_typ type's static method GET_ID( ). The static method GET_ID( ) returns the next
sequence value for the personId attribute. It's defined as static so that it is available when
there is no instance of type person_typ. Of course, it has to be this way, because the method is
used only when creating a new instance. Since GET_ID( ) is a static method, it's called using its
type name, person_typ. In our subclass, it has been implemented as an instance method, but it
could have just as easily been a static method if we were to recode it to accept a Connection

Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Tải bản đầy đủ ngay
×