23

Click here to load reader

datubaze.files.wordpress.com · Web viewcustom object classes for your Oracle objects, then you must define entries in the type map that specify the custom object classes that the

  • Upload
    lyduong

  • View
    214

  • Download
    1

Embed Size (px)

Citation preview

Page 1: datubaze.files.wordpress.com · Web viewcustom object classes for your Oracle objects, then you must define entries in the type map that specify the custom object classes that the

Javas objekts

SQL objekts

JDBC interfeiss

Reģistrētās objekta tipu transformācijas

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.

If you want to create custom object classes for your Oracle objects, then you must define entries in the type map that specify the custom object classes that the drivers will instantiate for the corresponding Oracle objects.You must also provide a way to create and populate instances of the custom object class from the Oracle object and its attribute data. The driver must be able to read from a custom object class and write to it. In addition, the custom object class can provide getXXX and setXXX methods corresponding to the attributes of the Oracle object, although this is not necessary. To create and populate the custom classes and provide these read/write capabilities, you can choose between the following interfaces:

1) the JDBC standard SQLData interface;2) the ORAData and ORADataFactory interfaces provided by Oracle.

The custom object class you create must implement one of these interfaces. The ORAData interface can also be used to implement the custom reference class corresponding to the custom object class. However, if you are using the SQLData interface, then you can use only weak reference types in Java, such as java.sql.Ref or oracle.sql.REF. The SQLData interface is for mapping SQL objects only.You can create custom object classes yourself, but the most convenient way to create them is to use the Oracle JPublisher utility to create them for you. JPublisher supports the standard SQLData interface as well as the Oracle-specific ORAData interface, and is able to generate classes that implement either one.

Page 2: datubaze.files.wordpress.com · Web viewcustom object classes for your Oracle objects, then you must define entries in the type map that specify the custom object classes that the

Javas objekts

SQL objekts

SQLData interfeiss

JDBC objekta tipu transformācijas

2

Interface SQLData

The 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.

Page 3: datubaze.files.wordpress.com · Web viewcustom object classes for your Oracle objects, then you must define entries in the type map that specify the custom object classes that the

3

The SQLData interface requires that we implement three methods:

1) public String getSQLTypeName() throws SQLException

2) public void readSQL(SQLInput stream, String typeName) throws SQLException

3) public void writeSQL(SQLOutput stream) throws SQLException

4) in addition, we need to provide a public, parameterless constructor that the JDBC driver can call:public classname() {}

Page 4: datubaze.files.wordpress.com · Web viewcustom object classes for your Oracle objects, then you must define entries in the type map that specify the custom object classes that the

4

Piemērs. Datu bāzē tiek definēts objekta tips - ķīmiskais elements

create or replace type ELEMENTA_T as object(NUM number,SIMB char(1),NOS 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_T - 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_T);

Izveidosim Java klasi Elements, kura atbilst Oracle ELEMENTA_T 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_T"; private int num; private String simb; private String nos; private float svars;

Page 5: datubaze.files.wordpress.com · Web viewcustom object classes for your Oracle objects, then you must define entries in the type map that specify the custom object classes that the

5

public class Elements implements SQLData{

// Konstruktori public Elements() { } private Elements(int num, String simb, String nos, float svars)

// Interfeisu realizācija (implementation) public String getSQLTypeName() {return typeName; } public void readSQL(SQLInput inStream, String typeName) public void writeSQL(SQLOutput outStream) private static void setTypeMap()

// Savienojuma izveidošanas metode. public static Connection getConnection()

// SQL metodes (INSERT, SELECT)// Jauna ELEMENTI rindas objekta izveidošana (INSERT...)public static Elements createElements(int num, String simb, String nos, float svars)

// ELEMENTI rindas objekta nolasīšana (SELECT...)public static Elements getElements(int num)

// ELEMENTI rindas objekta nolasīšana (SELECT...)public static Elements getElements(String simb)

//execute metode SELECT darbībaiprivate static Elements executePSQuery(PreparedStatement ps)

int getNumurs() {return num;}String getSimbols() {return simb;}String getNosaukums() {return nos;}float getSvars() {return svars;}int getNeitroni(){return Math.round(svars) - num; } }

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");

Page 6: datubaze.files.wordpress.com · Web viewcustom object classes for your Oracle objects, then you must define entries in the type map that specify the custom object classes that the

6

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{ num = inStream.readInt(); simb = inStream.readString(); nos = inStream.readString(); svars = inStream.readFloat();}

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

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

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

Page 7: datubaze.files.wordpress.com · Web viewcustom object classes for your Oracle objects, then you must define entries in the type map that specify the custom object classes that the

7

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_T", Class.forName("Elements");

Ir nodefinēts attēlojums: ELEMENTA_TIPS klase Elements

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.NUM = ?");ps.setInt(1, num);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 num, String simb, String nos, float svars) throws SQLException{ // this.typeName = typeName; this.num = num; this.simb = simb; this.nos = nos; this.svars = svars;}

Page 8: datubaze.files.wordpress.com · Web viewcustom object classes for your Oracle objects, then you must define entries in the type map that specify the custom object classes that the

8

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

e = new Elements("ELEMENTA_T", num, simb, nos, 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.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.

Page 9: datubaze.files.wordpress.com · Web viewcustom object classes for your Oracle objects, then you must define entries in the type map that specify the custom object classes that the

9

Kopējā programma

create or replace Java source named "Elements" as// 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.util.Map

// Java objektu definēšanai nepieciešamās klases definēšana.public class Elements implements SQLData { private static Connection conn = null // Savien. objekta izveidošana. private static boolean typeMapSet = false; // Attēlojumu reģistr. nav. private static final String typeName="SYSTEM.ELEMENTA_T"; private int num; private String simb; private String nos; private float svars;// Konstruktori public Elements() { } private Elements(int num, String simb, String nos, float svars) throws SQLException{ // this.typeName = typeName; this.num = num; this.simb = simb; this.nos = nos; 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 num = inStream.readInt(); simb = inStream.readString(); nos = inStream.readString(); svars = inStream.readFloat(); }

Page 10: datubaze.files.wordpress.com · Web viewcustom object classes for your Oracle objects, then you must define entries in the type map that specify the custom object classes that the

10

public void writeSQL(SQLOutput outStream) throws SQLException{ outStream.writeInt(num); outStream.writeString(simb); outStream.writeString(nos); outStream.writeFloat(svars);}

// Savienojuma izveidošanas metode. public static Connection getConnection() throws SQLException{ if(conn==null){ OracleDataSource ods = new OracleDataSource(); ods.setURL("jdbc:default:connection"); //ods.setDriverType("thin"); //ods.setServerName("ORACLE"); //ods.setNetworkProtocol("tcp"); //ods.setDatabaseName("BAZE"); //ods.setPortNumber(1521); //ods.setUser("SYSTEM"); //ods.setPassword("Janis2018"); conn = ods.getConnection();} setTypeMap(); return conn; }

//Transformācijas reģistrēšana 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);}}}

Page 11: datubaze.files.wordpress.com · Web viewcustom object classes for your Oracle objects, then you must define entries in the type map that specify the custom object classes that the

11

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

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

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

private static Elements executePSQuery(PreparedStatement ps) throws SQLException { Elements e = null; ResultSet rs = ps.executeQuery(); while(rs.next()) {e = (Elements) rs.getObject(1); } rs.close(); ps.close();

Page 12: datubaze.files.wordpress.com · Web viewcustom object classes for your Oracle objects, then you must define entries in the type map that specify the custom object classes that the

12

return e; } int getNumurs() {return num;}String getSimbols() {return simb;}String getNosaukums() {return nos;}float getSvars() {return svars;}int getNeitroni(){return Math.round(svars) - num; } }

//Transformācijas pārbaudes programmacreate or replace Java source named "Molekula" asimport 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); } }}

create or replace procedure MOLEKULA as language JAVA NAME 'Molekula.main(java.lang.String[])';

Page 13: datubaze.files.wordpress.com · Web viewcustom object classes for your Oracle objects, then you must define entries in the type map that specify the custom object classes that the

13

Dokumentācija. 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);

Page 14: datubaze.files.wordpress.com · Web viewcustom object classes for your Oracle objects, then you must define entries in the type map that specify the custom object classes that the

14

We can use this STRUCT to perform an INSERT into the PERIODIC _TABLE table in two ways. We can use it with PreparedStatement.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();

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());

Page 15: datubaze.files.wordpress.com · Web viewcustom object classes for your Oracle objects, then you must define entries in the type map that specify the custom object classes that the

15

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.

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();

Page 16: datubaze.files.wordpress.com · Web viewcustom object classes for your Oracle objects, then you must define entries in the type map that specify the custom object classes that the

16

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.