41
Datu bāzes objektu transformēšana Java objektos JDBC 1. Vāji tipizētās noklusētās Java klases (weakly typed default Java class) Struct (Java) vai STRUCT (Oracle) izmantošana objektu transformēšanai. Šis variants ir efektīvāks un vienkāršāks. 2. Stipri tipizētas pašveidotās Java klases (strongly typed custom Java classe) izveidošana Oracle objektiem. JDBC Literatūra Programmēšan as Java Relāciju-objektu datu bāzes Javas objek RODB objekt SQLData interfei JDBC objekta t

Vājās tipizācijas izmantošana objektu transformēšanai  · Web view2017-02-28 · STRUCT (Oracle) izmantošana objektu transformēšanai. Šis variants ir efektīvāks un vienkāršāks

  • Upload
    dothien

  • View
    215

  • Download
    0

Embed Size (px)

Citation preview

Datu bāzes objektu transformēšana Java objektos

JDBC

1. Vāji tipizētās noklusētās Java klases (weakly typed default Java class) Struct (Java) vai STRUCT (Oracle) izmantošana objektu transformēšanai. Šis variants ir efektīvāks un vienkāršāks.

2. Stipri tipizētas pašveidotās Java klases (strongly typed custom Java classe) izveidošana Oracle objektiem.

JDBC

Literatūra

1. R. M. Menon. Expert Oracle JDBC Programming. Apress, 2005, 744. Lpp.

2.Kuassi Mensah. Oracle Database programming using Java and Web Services. Elsevier Digital Press publications, 2006.

Programmēšanas Java objekti

Relāciju-objektu datu bāzes objekti

Javas objekts

RODB objekts

SQLData interfeiss

JDBC objekta tipu savienojuma karte

2

Vājās tipizācijas izmantošana objektu transformēšanai

The order of these attribute objects in the Object array is the same order in which they were specified at the time of the creation of the object type that they represent. For example, a Struct Java object that corresponds to the database person object type containing the attributes name and date_of_birth, in that order would be stored in an internal array of Object elements (i.e., an Object[] object) containing two elements:1) the first attribute would be a String object (since the database type varchar2 maps to a String object in Java, by default) that contains the value in the name attribute of the person object.2) the second attribute would be a java.sql.Timestamp object (since the database type date, by default, maps to a java.sql.Timestamp object in Java) that contains the value in the date_of_birth attribute of the person object.

Note that you may need to cast each of these attributes manually into an object of the appropriate data type in Java. For example, while manipulating the second element of the Object[] structure in the previous example (corresponding to the date_of_birth attribute), you may need to cast the second array element to the interface java.sql.Timestamp type in order to access any methods specific to the interface java.sql.Timestamp. Weakly typed Struct objects are useful in the following scenarios:1) if your program needs to work with an arbitrary object type in a generic fashion without really needing to materialize it as a custom class object. This may be true, for example, if you are building a utility that needs to deal with an object of an arbitrary object type in a generic fashion as a collection of attributes.2) if your end user application doesn’t need to do a lot of manipulation of objects in memory. If you need to do a lot of manipulation in memory, then it is much more intuitive to use strongly typed objects, where a custom class that has appropriate getter and setter methods for the object attributes represents the database object type.

Oracle implements the Struct interface as an object of type oracle.sql.STRUCT class which, in a typical fashion, implements the standard methods in the Struct interface and also adds Oracle extension methods1.

public interface Struct is the standard mapping in the Java programming language for an SQL structured type. A Struct object contains a value for each attribute of the SQL structured type that it represents. By default, an instance of Struct is valid as long as the application has a reference to it. All methods on the Struct interface must be fully implemented if the JDBC driver supports the data type.

String getSQLTypeName(); retrieves the SQL type name of the SQL structured type that this Struct object represents.

Object[] getAttributes(); produces the ordered values of the attributes of the SQL structured type that this Struct object represents. As individual attributes are processed, this method uses the type map associated with the connection for customizations of the type mappings. If there is no entry in the connection's type map that matches the structured type that an attribute represents,

1 http://docs.oracle.com/cd/B28359_01/java.111/b31224/oraoot.htm

Programmēšanas Java objekts

Relāciju-objektu datu bāzes

objekts

Java klase Struct

3

the driver uses the standard mapping. Conceptually, this method calls the method getObject on each attribute of the structured type and returns a Java array containing the result.

Object[] getAttributes(Map<String, Class<?>> map); produces the ordered values of the attributes of the SQL structured type that this Struct object represents. As individual attrbutes are proccessed, this method uses the given type map for customizations of the type mappings. If there is no entry in the given type map that matches the structured type that an attribute represents, the driver uses the standard mapping. This method never uses the type map associated with the connection. Conceptually, this method calls the method getObject on each attribute of the structured type and returns a Java array containing the result.

4

Klases java.sql.Struct izmantošana datu bāzes un Java objektu savstarpējai transformēšanai

ResultSet rs= stmt.executeQuery( "SELECT * FROM TABULA");java.sql.Struct jdbcStruct = (java.sql.Struct)rs.getObject(1);

java.sql.Struct instance and use the following standard methods:1) getAttributes(map). This method retrieves the values of the

attributes, using entries in the specified type map to determine the Java classes to use in materializing any attribute that is a structured object type. The Java types for other attribute values would be the same as for a getObject call on data of the underlying SQL type.

2) getAttributes. This method is the same as the preceding getAttributes(map) method, except it uses the default type map for the connection.

3) getSQLTypeName. This method returns a Java String that represents the fully qualified name of the Oracle object type that this Struct represents.

You can use standard JDBC functionality, such as getObject, to retrieve an Oracle object from the database as an instance of java.sql.Struct. Because getObject returns a java.lang.Object, you must cast the output of the method to Struct. For example:

Struktūra:java.sql.Struct

Datu bāzes sistēma

Struktūrasjava.sql.Struct

metodes:1) getAttributes(map)2) get Attributes3) getSQLTypeName

Izveidoša

5

Klases oracle.sql.STRUCT izmantošana datu bāzes un Java objektu savstarpējai transformēšanai

oracle.sql.Datum[] attrs = oracleSTRUCT.getOracleAttributes();

oracle.sql.Datum[] attrs = ((oracle.sql.STRUCT)jdbcStruct).getOracleAttributes();

Object[] attrs = jdbcStruct.getAttributes();

You can select data from the database into STRUCT objects and create STRUCT objects for inserting data into the database. STRUCT objects completely preserve data, because they maintain the data in SQL format. Using STRUCT objects is more efficient and more precise in situations where you do not need the information in an application specific form.If you want to take advantage of the extended functionality offered by Oracle-defined methods, then use an oracle.sql.STRUCT instance.The oracle.sql.STRUCT class implements the java.sql.Struct interface and provides extended functionality beyond the JDBC 2.0 standard.The STRUCT class includes the following methods in addition to standard Struct functionality:

1) getOracleAttributes. Retrieves the values of the values array as oracle.sql.* objects2) getDescriptor. Returns the StructDescriptor object for the SQL type that corresponds to this

STRUCT object.3) getJavaSQLConnection. Returns the current connection instance.4) toJdbc. Consults the default type map of the connection to determine what class to map to

and, then, uses toClass.5) toJdbc(map). Consults the specified type map to determine what class to map to, and then

uses toClassIf you want to retrieve Oracle object attributes from a STRUCT or Struct instance as oracle.sql types, then use the getOracleAttributes method of the oracle.sql.STRUCT class, as follows:

oracle.sql.Datum[] attrs = oracleSTRUCT.getOracleAttributes();or:oracle.sql.Datum[] attrs = ((oracle.sql.STRUCT)jdbcStruct).getOracleAttributes();

Struktūra:oracle.sql.STRUCT Datu bāzes

sistēma

Struktūrasoracle.sql.STRUCT

metodes:1) getOracleAttributes2) getDescriptor3) getJavaSQLConnection4) toJdbc5) toJdbc(map)

Izveidošana

6

If you want to retrieve Oracle object attributes as standard Java types from a STRUCT or Struct instance, use the standard getAttributes method:

Object[] attrs = jdbcStruct.getAttributes();

Note: Oracle JDBC drivers cache array and structure descriptors. This provides enormous performance benefits. However, it means that if you change the underlying type definition of a structure type in the database, the cached descriptor for that structure type will become stale and your application will receive a SQLException exception.

7

DBC standarta interfeiss java.sql.Struct

Struct objekts attēlo dažāda tipa datu bāzes objektu vispārējā veidā kā atribūtu kolekciju. Objekta atribūti tiek glabāti objekta masīvā kā Java objekti. Atribūta klase pēc noklusējuma tiek definēta šādi:

CHAR, VARCHAR, LONG oracle.sql.CHAR java.lang.String, java.sql.Date, java.sql.Time, java.sql.Timestamp, java.lang.Byte, java.lang.Short, java.lang.Integer, java.lang.Long,

java.lang.Float, java.lang.Double, java.math.BigDecimal, byte, short,int,long, float,double

DATE oracle.sql.DATE, java.sql.Date, java.sql.Time, java.sql.Timestamp, java.lang.String

NUMBER oracle.sql.NUMBER, java.lang.Byte, java.lang.Short, java.lang.Integer,

java.lang.Long, java.lang.Float, java.lang.Double, java.math.BigDecimal, byte, short,int,long, float,double

OPAQUE oracle.sql.OPAQUE RAW, LONG RAW oracle.sql.RAW, byte[] ROWID oracle.sql.CHAR, oracle.sql.ROWID, java.lang.String BFILE oracle.sql.BFILEBLOB oracle.sql.BLOB, java.sql.BlobCLOB oracle.sql.CLOB, java.sql.Clob

8

JDBC standarta interfeisa java.sql.Struct metodes

Ja izmanto savu SQL objekta attēlojumu:public Object[] getAttributes(Map map) throws SQLException;

Ja izmanto SQL objekta attēlojumu pēc noklusējuma:public Object[] getAttributes() throws SQLException;

Tiek iegūts Java String objekts ar DB objekta tipa nosaukumu (schema.sql_type_name):public String getSQLTypeName() throws SQLException;

9

Oracle klase oracle.sql.STRUCT

java.lang.Object   oracle.sql.Datum       oracle.sql.DatumWithConnection           oracle.sql.STRUCT

Atšķirībā no java.sql.Struct interfeisa, ko var izmantot tikai lai veiktu vaicājumus datu bāzei, oracle.sql.STRUCT klasi var izmantot arī, lai ievietotu un atjauninātu datus.

Tiek iegūti atribūti (atribūtu objektu masīvā) kā oracle.sql.* objekti:public oracle.sql.Datum[] getOracleAttributes() throws SQLException;

Tiek iegūts oracle.sql.StructDescriptor objekts SQL objekta tipam:public oracle.sql.StructDescriptor getDescriptor() throws SQLException;

10

Oracle RODB objekta izgūšana izmantojot klasi STRUCT un interfeisu Struct

create or replace type ADRESE_TIPS as object(NUMURS number,IELA varchar2(20),PILSETA varchar(20));

create table ADRESES of ADRESE_TIPS;

insert into DARBINIEKI values (ADRESE_TIPS(21, 'Avotu', 'Rīga'));insert into DARBINIEKI values (ADRESE_TIPS(7, 'Rīgas', 'Ogre'));insert into DARBINIEKI values(ADRESE_TIPS(5, 'Ozolu', 'Talsi'));

create table DARBINIEKI (D_NUM number,D_UZV varchar2(30),D_ADRESE ADRESE_TIPS);

insert into DARBINIEKI values (1, 'Koks', ADRESE_TIPS(21, 'Avotu', 'Rīga'));

11

create or replace JAVA source named "Objekts" asimport java.sql.*; //JDBCimport java.io.*; // Ievade, izvadeimport oracle.jdbc.*; //Oracle JDBC (aizmaiņa)

public class Objekts{ public static void ObjIzvade() throws SQLException {try {// Savienojuma objekta izveidošana (pēc noklusējuma) Connection savienojums = DriverManager.getConnection("jdbc:default:connection:");//Komandas objekta izveidošana Statement komanda = savienojums.createStatement();// Rezultātu objekta izveidošana, izpildāmās SELECT komandas // ievade un tās izpilde. ResultSet atbilde = komanda.executeQuery( "select D_NUM, D_UZV, D_ADRESE from DARBINIEKI" + " where D_NUM = 1"); atbilde.next(); //Pirmās rezultātu rindas iegūšana no atbildes masīva// Pirmo divu kolonu vērtību ieguve no rezultātu objekta. int d_num = atbilde.getInt(0); String d_uzv = atbilde.getString(1);

12

// Trešajā kolonā glabājas objekts. Tas tiek izgūts kā struktūra.// 1. Oracle objekta izgūšanas variants (oracle.sql.STRUCT izmantošana).// No struktūras STRUCT objekta atribūti (lauki) tiek izgūti Object tipa// masīvā. Notiek Oracle SQL datu tipu transformācija Java datu tipos// varchar2, char String, number BigDecimal, date Dateoracle.sql.STRUCT oracleSTRUCT = (oracle.sql.STRUCT) atbilde.getObject(2);

// 2. Oracle objekta izgūšanas variants (oracle.sql.STRUCT izmantošana).oracle.sql.STRUCT oracleSTRUCT = ((OracleResultSet)atbilde).getStruct(2);

// 3. Oracle objekta izgūšanas variants (java.sql.Struct izmantošana).java.sql.Struct jdbcStruct = (java.sql.Struct) atbilde.getObject(2);

13

// 1. Oracle objekta atribūtu izgūšana kā oracle.sql tipus.

oracle.sql.Datum[] d_adreseAttr = oracleSTRUCT.getOracleAttributes();

// 2. Oracle objekta atribūtu izgūšana kā oracle.sql tipus.

oracle.sql.Datum[] d_adreseAttr = ((oracle.sql.STRUCT) jdbcStruct).getOracleAttributes();

// 3. Oracle objekta atribūtu izgūšana kā standarta Java tipus no STRUCT vai Struct instancēm.

Object[] d_adreseAttr = jdbcStruct.getAttributes();

// Tiek izgūti atribūti pārveidoti vēlamajos Java tipos. int iel_num = ((BigDecimal) d_adreseAttr[0]).intValue(); String iela = (String) d_adreseAttr[1]; String pilseta = (String) d_adreseAttr[2];

14

// Alternatīvs variants metodes getAttributes() izmantošanai ir // getOracleAttributes() metodes lietošana. Šajā gadījumā atribūtu tipi // atbilst Oracle JDBC datu tipiem (number, char). Turpinājumā šie // tipi tiek transformēti Java tipos. Šis ir racionālāks variants.

// Object d_adreseAttr[] = d_adreseStruct.getOracleAttributes();// int iel_num = ((NUMBER) elementAttr[0]).intValue();// String iela = ((CHAR) elementAttr[1]).toString();// String pilseta = ((CHAR) elementAttr[2]).toString();

System.out.println("Darbinieka uzvārds = " + d_uzv + " Pilsēta = " + pilseta); komanda.close(); } catch (SQLException e) {System.err.println(e.getMessage());} } }

create or replace procedure OBJEKTA_IZVADE() as language JAVA name 'Objekts.ObjIzvade( )';

beginOBJEKTA_IZVADE;end;

http://docs.oracle.com/cd/B28359_01/java.111/b31224/oraoot.htm

15

Stipri tipizētas pašveidotas Java klases (strongly typed custom Java classe) izveidošana Oracle objektiem

If you want a strongly typed class of your own design that fits into your

application's design, you can do that by implementing:

1) the SQLData interface;

2) the ORAData interface in the class definition.

Once we register our SQLData or ORAData class with the JDBC driver's

type map, we can read and write objects of this class automatically, using

standard JDBC calls.

Javas objekts

RODB objekts

SQLData interfeiss

JDBC objekta tipu savienojuma karte

16

SQLData interfeisa izmantošana Javas un RODB objektu transformācijai

The interface public interface SQLData is used for the custom mapping of an SQL user-defined type (UDT) to a class in the Java programming language. The class object for a class implementing the SQLData interface will be entered in the appropriate Connection object's type map along with the SQL name of the UDT for which it is a custom mapping.

Typically, a SQLData implementation will define a field for each attribute of an SQL structured type or a single field for an SQL DISTINCT type. When the UDT is retrieved from a data source with the ResultSet.getObject method, it will be mapped as an instance of this class. A programmer can operate on this class instance just as on any other object in the Java programming language and then store any changes made to it by calling the PreparedStatement.setObject method, which will map it back to the SQL type. It is expected that the implementation of the class for a custom mapping will be done by a tool. In a typical implementation, the programmer would simply supply:

1) the name of the SQL UDT;2) the name of the class to which it is being mapped;3) the names of the fields to which each of the attributes of the UDT is to be mapped.

The tool will use this information to implement the:1) SQLData.readSQL;2) SQLData.writeSQL methods.

The readSQL method calls the appropriate SQLInput methods to read each attribute from an SQLInput object, and the writeSQL method calls SQLOutput methods to write each attribute back to the data source via an SQLOutput object. An application programmer will not normally call SQLData methods directly, and the SQLInput and SQLOutput methods are called internally by SQLData methods, not by application code.

The SQLData interface requires that we implement three methods:1) getSQLTypeName();2) readSQL(SQLInput stream, String typeName);3) writeSQL(SQLOutput stream).In addition, we need to provide a public, parameterless constructor that the JDBC driver can call:public classname() {}

17

Piemērs. Datu bāzē tiek definēts šāds objekta tips (tiek apskatīti ķīmiskie elementi):

create or replace type ELEMENTA_TIPS as object(NUMURS number,SIMBOLS char(1),NOSAUKUMS varchar2(20),SVARS number(8,3));

Each row of the table must correspond to an object in Java; to do this we need to make a table with a single column of type ELEMENTA_TIPS - an object table won't work, interestingly enough, because it appears to be a relational table with columns corresponding to the object attributes.

create table ELEMENTI(ELEMENTS ELEMENTA_TIPS);

Izveidosim Java klasi Elements, kura atbilst Oracle ELEMENTA_TIPS tipam. Klasē iekļausim arī Oracle objekta tipa nosaukumu kā privāto mainīgo typeName.

public class Elements implements SQLData { private static final String typeName="ELEMENTA_TIPS"; private int numurs; private String simbols; private String nosaukums; private float svars;

18

Pirmā SQLData interfeisa metode kura jārealizē (implement) ir getSQLTypeName(). Tai jātgriež Oracle objekta tips:

public String getSQLTypeName() { return typeName;}

Otrā realizējamā SQLData interfeisa metode readSQL() nolasa plūsmu (stream) no datu bāzes, izmantojot atbilstošās readXXX() metodes katram ELEMENTA_TIPS atribūtam:

public void readSQL(SQLInput inStream, String typeName) throws SQLException{ numurs = inStream.readInt(); simbols = inStream.readString(); nosaukums = inStream.readString(); svars = inStream.readFloat();}

Trešā SQLData interfeisa realizējamā metode writeSQL() ieraksta izejas plūsmā visas ELENTA_TIPS atribūtu vērtības, izmantojot writeXXX() metodes: public void writeSQL(SQLOutput outStream) throws SQLException{ outStream.writeInt(numurs); outStream.writeString(simbols); outStream.writeString(nosaukums); outStream.writeFloat(svars;}

Vēl jādefinē atbilstošā konstruktora metode objekta ELEMENTA_TIPS (bez parametriem): public Elements() {}

Var tikt definētas arī citas konstruktora metodes ar tikai dažiem parametriem.

19

JDBC draiverim ir jāzina, ka eksistē izveidotā klase Elements, kura atbilst Oracle ELEMENTA_TIPS objekta tipam. Pēc noklusēšanas, kad tiek iegūts objekts no ResultSet, tas tiek attēlots kā struktūra STRUCT. Lai JDBC veiktu nepieciešamo, jaunizveidoto attēlojumu, tas jāiekļauj JDBC savienojuma tipu kartē (JDBC connection's map) jeb sarakstā. Tas ir Map interfeisa realizācija. Oracle objekta tips tiek sasaistīts ar atbilstošo Java klasi konkrētajam savienojumam.Attēlojuma pievienošana JDBC savienojuma tipu kartē tiek veikta šādi:

Map typeMap = conn.getTypeMap();typeMap.put("ELEMENTA_TIPS", Class.forName("Elements");

Ir nodefinēts attēlojums: ELEMENTA_TIPS klase Elements

20

Tā kā attēlojums ir nodefinēts, var veikt Oracle objekta datu izguvi. Izmantojam arī izveidoto bezparametru konstruktora metodi Elements:

PreparedStatement ps = conn.prepareStatement( "select * from ELEMENTI E " + "where E.ELEMENTS.NUMURS = ?");ps.setInt(1, numurs);ResultSet rs = executePSQuery(ps);ResultSet rs = ps.executeQuery();Elements e = null;if(rs.next()) { e = (Elements) rs.getObject(1);}

Lai ierakstītu objektu Elements datu bāzē, vispirms tas ir jāizveido. Tiek izsaukta noklusētā konstruktora metode vai cita definēta konstruktora metode:

public Elements(String typeName, int numurs, String simbols, String nosaukums, float svars) throws SQLException{ // this.typeName = typeName; this.numurs = numurs; this.simbols = simbols; this.nosaukums = nosaukums; this.svars = svars;}

In the following example, we'll call this constructor, then insert it by using a PreparedStatement:

e = new Elements("ELEMENTA_TIPS", numurs, simbols, nosaukums, svars);getConnection();PreparedStatement ps = conn.prepareStatement( "insert into ELEMENTI values (?)");ps.setObject(1, e);ps.executeQuery();ps.close();

In this case, the JDBC driver calls our getSQLTypeName() method to determine the Oracle type, then calls our writeSQL() to get our class's attributes.

21

The examples above may suggest that the client class accesses the database to read and write Elements objects. That is one possible implementation. Below is a more complete example that encapsulates all the database access in the Elements class itself. A client application is expected to create and get an Elements object using the static factory methods getElements() and createElements(). However, there is nothing except good manners to prevent it from calling the default constructor, which must be public—though this will allow it to insert only an empty object.

22

Kopējā programma// Nepieciešamo klašu imports.import java.sql.*;import oracle.jdbc.pool.OracleDataSource;import oracle.jdbc.driver.*;import oracle.sql.*;import java.math.BigDecimal;import java.util.*;// Java objektu definēšanai nepieciešamās klases definēšana.public class Elements implements SQLData { private static Connection conn = null // Savienojuma objekta izveidošana. private static boolean typeMapSet = false; // Attēlojumu reģistrāciju nav. private static final String typeName="SYSTEM.ELEMENTA_TIPS"; private int numurs; private String simbols; private String nosaukums; private float svars;// Konstruktori public Elements() { } private Elements(int numurs, String simbols, String nosaukums, float svars) throws SQLException{ // this.typeName = typeName; this.numurs = numurs; this.simbols = simbols; this.nosaukums = nosaukums; this.svars = svars; } // Interfeisu realizācija (implementation) public String getSQLTypeName() {return typeName; } public void readSQL(SQLInput inStream, String typeName) throws SQLException{ // typeName = typeName; already set numurs = inStream.readInt(); simbols = inStream.readString(); nosaukums = inStream.readString(); svars = inStream.readFloat(); } public void writeSQL(SQLOutput outStream) throws SQLException{ outStream.writeInt(numurs); outStream.writeString(simbols); outStream.writeString(nosaukums); outStream.writeFloat(svars);}

// Pakalpojumu metodes.// Savienojuma izveidošanas metode. public static Connection getConnection() throws SQLException{ if(conn==null){ OracleDataSource ods = new OracleDataSource(); ods.setDriverType("thin"); ods.setServerName("ORACLE");

23

ods.setNetworkProtocol("tcp"); ods.setDatabaseName("BAZE1"); ods.setPortNumber(1521); ods.setUser("SYSTEM"); ods.setPassword("JANIS1946"); conn = ods.getConnection();} setTypeMap(); return conn; }

private static void setTypeMap() throws SQLException { if(!typeMapSet) { Map typeMap = conn.getTypeMap(); try { typeMap.put(typeName, Class.forName("Elements")); typeMapSet = true; } catch (ClassNotFoundException e) {System.out.println("Kļūda: " + e);}}}

// SQL metodes (INSERT, SELECT)// Jauna ELEMENTI rindas (objekta) izveidošanapublic static Elements createElements(int numurs, String simbols, String nosaukums, float svars) throws SQLException { getConnection(); Elements e = null; if((e = getElements(numurs)) == null) { e = new Elements(numurs, simbols, nosaukums, svars); PreparedStatement ps = conn.prepareStatement( "insert into ELEMENTI values (?)"); ps.setObject(1, e); ps.executeQuery(); ps.close(); } return e; }

public static Elements getElements(int numurs) throws SQLException { getConnection(); PreparedStatement ps = conn.prepareStatement( "select * from ELEMENTI E " + "WHERE E.ELEMENTS.NUMURS = ?"); ps.setInt(1, numURS); return executePSQuery(ps); }

public static ElementS getElements(String simbols) throws SQLException { getConnection(); PreparedStatement ps = conn.prepareStatement( "select * from ELEMENTI E " + "where E.ELEMENTS.SIMBOLS = ?"); ps.setString(1, simbols); return executePSQuery(ps); }

private static Elements executePSQuery(PreparedStatement ps) throws SQLException { Elements e = null; ResultSet rs = ps.executeQuery();

24

while(rs.next()) {e = (Elements) rs.getObject(1); } rs.close(); ps.close(); return e; } int getNumurs() {return numurs;}String getSimbols() {return simbols;}String getNosaukums() {return nosaukums;}float getSvars() {return svars;}int getNeitroni(){return Math.round(svars) - numurs; } }

Transformācijas pārbaudes programma

import java.sql.*;public class Molekula { public static void main(String [] args) { try { Elements.createElements(1, "H", "Ūdeņradis", 1.008F); Elements.createElements(8, "O", "Skābeklis", 15.999F); Elements udenradis = Elements.getElements("H"); Elements skabeklis = Elements.getElements("O"); System.out.println("Elements: " + skabeklis.getNosaukums()); System.out.println(" Neitroni (vidēji): " + skabeklis.getNeitroni()); System.out.println("Elements: " + udenradis.getNosaukums()); System.out.println(" Neitroni (vidēji): " + udenradis.getNeitroni()); } catch (SQLException e) {System.out.println("Kļūda: " + e); } }}

25

Java objektu transformācija RODB objektosUnfortunately, the standard Java Struct doesn't provide a way to create a new Struct. We can obtain one only by selecting an object type from the database. Fortunately, Oracle's STRUCT has additional functionality, including a means of creating a new STRUCT that we can insert into the database. Because we're working closely with Oracle-specific object-relational features anyway, there's little benefit to be gained by choosing the standard option and lots of benefit to be gained from Oracle's extensions, so we'll use only the Oracle STRUCT in these examples.When we retrieve an object type from the database into a STRUCT, the JDBC driver obtains the information it needs to create the STRUCT and the attributes array from the database, behind the scenes. If we want to create a STRUCT object from scratch and insert it into the database, we need to obtain the same information about the object type from the database ourselves—this information comes in the form of an object called a StructDescriptor, which is unique to Oracle's implementation.To use STRUCT and the StructDescriptor classes, we need to import the Oracle JDBC classes at the top of our source file.

import oracle.sql.*;To get a StructDescriptor, we call a static method, createDescriptor(), with the name of the object type and a valid connection to the database. Here is how we obtain the descriptor for our ELEMENT_TYPE:

String elemTypeName = "ELEMENT_TYPE";StructDescriptor elemDesc = StructDescriptor.createDescriptor(elemTypeName, conn);StructDescriptor has a number of interesting methods. One of these, getMetaData(), returns metadata about the object type in the form of a ResultSetMetaData object.ResultSetMetaData elemMetaData = elemDesc.getMetaData();We can obtain information about the object's attributes by calling ResultMetaData methods, such as getColumnCount(), getColumnName(), getColumnType(), and getColumnTypeName().int columns = elemMetaData.getColumnCount();System.out.println("Number of attributes: " + columns);System.out.println("Attributes: ");for(int i = 1; i <= columns; i++){ System.out.print(elemMetaData.getColumnName(i)+" "); System.out.println(elemMetaData.getColumnTypeName(i));}

Unlike the standard Java Struct, Oracle's STRUCT has a public constructor that we can call. It requires a StructDescriptor, a Connection, and an array of objects for the attributes. First, we'll create and populate an attributes array.Object [] elemAttr = new Object [4];elemAttr[0] = new BigDecimal(4); // numberelemAttr[1] = "Be"; // symbolelemAttr[2] = "Beryllium"; // nameelemAttr[3] = new BigDecimal(9.012);// massNow we have all the parts we need to call the STRUCT constructor.STRUCT elemStruct = new STRUCT(elemDesc, conn, elemAttr);We can use this STRUCT to perform an INSERT into the PERIODIC _TABLE table in two ways. We can use it with PreparedStatement.

26

PreparedStatement ps = conn.prepareStatement( "INSERT INTO PERIODIC_TABLE VALUES(?, ?, ?)");ps.setInt(1, 2); // periodps.setInt(2, 2); // groupps.setObject(3, elemStruct);int rows = ps.executeUpdate();Alternatively, if we have an updateable result set, we can use that to perform an insert. We'll add another record this way. To obtain an updateable result set, you may remember we need to set type and concurrency when we create our Statement.Statement stmt = conn.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);ResultSet rs = stmt.executeQuery( "SELECT SELECT PERIOD, COLUMN_GROUP, ELEMENT" + "FROM PERIODIC_TABLE");We'll need to create a new STRUCT but because the StructDescriptor is associated with the object type and not any particular table or object, we can reuse it. We'll keep the same attributes array, but we'll assign new values to it.elemAttr[0] = new BigDecimal(5); // Atomic numberelemAttr[1] = "B"; // symbolelemAttr[2] = "Boron"; // nameelemAttr[3] = new BigDecimal(10.81);// masselemStruct = new STRUCT(elemDesc, conn, elemAttr);To perform the insert using the ResultSet, we move to the insert row, update each of the fields using the appropriate updateXXX() method—update-Object() is the one we need for our STRUCT—then we call insertRow().rs.moveToInsertRow();rs.updateInt(1,2); // periodrs.updateInt(2,13); // grouprs.updateObject(3, elemStruct);rs.insertRow();

27

Stipri tipizētas pašveidotas Java klases (strongly typed custom Java classe) izveidošana Oracle objektiem

STRUCT is a one-size-fits-all, easy-to-use class that will accommodate

any Oracle object type. But if you want a strongly typed class of your own

design that fits into your application's design, you can do that, too, by

implementing either:

1) the SQLData;

2) the ORAData interface in the class definition.

Once we register our SQLData or ORAData class with the JDBC driver's

type map, we can read and write objects of this class automatically,

using standard JDBC calls.

28

ORAData interfeisa izmantošana Javas un RODB objektu transformācijai

Oracle's proprietary ORAData interface offers performance advantages over the SQLData interface because it does not automatically perform conversions from SQL types to Java types.Another advantage is that we do not have to update the connection's type map, because the object is passed to and from our class as an Oracle STRUCT. (The method signatures actually specify Datum, but that is a superclass of STRUCT.) Our class is responsible for conversion of the STRUCT's attributes to and from Java types. An alternative is to keep the attributes in their SQL format and let the client class perform the conversion, if the client class is database-savvy. If calculations need to be performed on the attributes, it may be more efficient and accurate to defer conversion.Creating a custom class using the ORAData interface actually requires that we implement two interfaces: ORAData and ORADataFactory. Each of these requires that we implement one method. The ORADataFactory interface requires that we implement a create() method that takes a Datum and returns a populated instance of our class.public ORAData create(Datum d, int sqlType) throws SQLException{ if(d==null) { return null; } Object [] attributes = ((STRUCT) d).getOracleAttributes(); System.out.println("Datum is" + d.getClass().getName()); return new OraDataElement( ((NUMBER) attributes[0]).intValue(), ((CHAR)attributes[1]).toString(), ((CHAR)attributes[2]).toString(), ((NUMBER) attributes[3]).floatValue());}Converting the Datum is identical to what we did when we used STRUCTs directly as weakly typed classes. After casting the Datum to a STRUCT, we obtain the attributes in an object array by calling the getAttributes() or getOracleAttributes() method, depending on whether we want Java types or Oracle JDBC datatypes.The ORAData interface requires that we implement a toDatum() method that converts the attributes of our class and returns them as a STRUCT. Creating a STRUCT, you may remember, requires us to get a StructDescriptor and stuff our attributes into an object array.public Datum toDatum(Connection conn) throws SQLException { StructDescriptor sd = StructDescriptor.createDescriptor("ELEMENT_TYPE", conn); Object [] attributes = { new NUMBER(number), new CHAR(symbol, CHAR.DEFAULT_CHARSET), new CHAR(name, CHAR.DEFAULT_CHARSET), new NUMBER(weight)}; return new STRUCT(sd, conn, attributes); }It doesn't matter in this case whether we provide Oracle JDBC types or Java types, like we did before—i.e., BigDecimal in place of NUMBER and String in place of CHAR—the driver will determine and perform the appropriate conversion in either case.In addition to these interface requirements, we also must provide a way for the client application to obtain an instance of our class that implements the ORADataFactory class. (This can be a separate class from our ORAData class, but here, our custom class implements both interfaces.) This is commonly done by having a static instance in our class and providing a method to return it.

29

static final OraDataElement oraDataElementFactory = new OraDataElement();public static ORADataFactory getFactory(){return oraDataElementFactory;}

To facilitate creating an object to insert into the database, we'll provide a constructor that a client application can use.

public OraDataElement (int number, String symbol, String name, float weight) { this.number = number; this.symbol = symbol; this.name = name; this.weight = weight; }

To use the ORAData class to insert an object, we create the object by calling the constructor, then use an OraclePreparedStatement (not just a PreparedStatement) to insert it.

OraDataElement ee = new OraDataElement(16,"S","Sulfur", 32.06F);OraclePreparedStatement ps = (OraclePreparedStatement) conn.prepareStatement("INSERT INTO ELEMENTS VALUES(?)");ps.setORAData(1, ee);ps.execute();ps.close();

To use the ORAData class to read objects, we select into an Oracle-ResultSet (not just a ResultSet) and call the getORAData() method. Notice that the getORAData() method requires the getFactory() method that we defined above.

Statement stmt = conn.createStatement();OracleResultSet rs = (OracleResultSet) stmt.executeQuery( "SELECT * FROM ELEMENTS");while(rs.next()){ OraDataElement e = (OraDataElement) rs.getORAData(1, OraDataElement.getFactory()); System.out.println("Name:" + e.name);}

Finally, just as we did with the SQLData example, we could encapsulate all the database functionality in the ORAData class to hide it from the client class.