Je vais indiquer, dans ce document, comment depuis une classe Java, récupérer un objet complexe SQL. Pour y parvenir je vais utiliser l'API JDBC (Java DataBase Connectivity) qui permet aux applications Java d'accéder par le biais d'une interface commune à des bases de données. Pour être le moins théorique possible, ce didacticiel consiste à réaliser un exemple.
Voici un tableau qui indique en fonction de ce qui est retourné par le SQL l'objet Java le mieux adapté :
| Types SQL | Types Java |
|---|---|
| CHAR | String |
| VARCHAR2 | String |
| LONG | String |
| 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 |
| RAW | byte[] |
| LONGRAW | byte[] |
| DATE | java.sql.Date ou java.sql.Timestamp |
| ROWID | oracle.sql.ROWID |
| REF CURSOR | java.sql.ResultSet |
| BLOB | oracle.sql.BLOB |
| CLOB | oracle.sql.CLOB |
| BFILE | oracle.sql.BFILE |
| Oracle object | Une classe spécifié dans le « type map » sinon oracle.sql.STRUCT |
| Oracle object reference | oracle.sql.REF |
| collection (varray ou nested table) | oracle.sql.ARRAY |
Je me concentre sur les types complexes, ce qui correspond aux lignes « Oracle object » et « REF CURSOR ». Pour comprendre ce didacticiel vous devez impérativement posséder les bases de JDBC c'est-à-dire savoir récupérer des types simples (VARCHAR2, NUMBER, DATE ...).
Les « Oracle object » sont des objets SQL. Ils peuvent être stockés dans des tables ou instanciés à la volée dans du PL/SQL. Pour illustrer cette partie, vous allez créer des objets dans la base de donnée. Vous allez ensuite les récupérer par JDBC et les stocker dans des JavaBeans. C'est le sens Oracle vers Java. Pour le sens Java vers Oracle vous allez instancier des JavaBean et vous allez insérer leurs données dans Oracle via JDBC.
Créez l'objet « PERSON » ainsi :
CREATE OR REPLACE TYPE "PERSON" as object ( firstname varchar2(25), name varchar2(25), birthyear number )
Ensuite créez la table « FRIENDS » qui contiendra des « PERSON » :
create table FRIENDS ( f_id number primary key, f_pers PERSON )
Insérez dans la table « FRIENDS » quelques « PERSON » :
INSERT INTO FRIENDS VALUES (1,PERSON( 'Hyacinthe','MENIET')); INSERT INTO FRIENDS VALUES (2,PERSON( 'Larry','ELLISON')); INSERT INTO FRIENDS VALUES (3,PERSON( 'Bill','JOY'));
Créez la classe PersonBean ainsi :
/**
* @author Hyacinthe MENIET
* Created on 19 janv. 2006
*/
package net.dotmyself.j2sql;
import java.io.Serializable;
import java.sql.SQLData;
import java.sql.SQLException;
import java.sql.SQLInput;
import java.sql.SQLOutput;
/**
* Maps Oracle "PERSON" object
*/
public class PersonBean implements SQLData, Serializable {
private String firstname;
private String name;
private int birthYear;
/* (non-Javadoc)
* @see java.sql.SQLData#getSQLTypeName()
*/
public String getSQLTypeName() throws SQLException {
return "PERSON";
}
/* (non-Javadoc)
* @see java.sql.SQLData#readSQL(java.sql.SQLInput, java.lang.String)
*/
public void readSQL(SQLInput stream, String typeName) throws SQLException {
name = stream.readString();
firstname = stream.readString();
birthYear = stream.readInt();
}
/* (non-Javadoc)
* @see java.sql.SQLData#writeSQL(java.sql.SQLOutput)
*/
public void writeSQL(SQLOutput stream) throws SQLException {
stream.writeString(name);
stream.writeString(firstname);
stream.writeInt(birthYear);
}
/**
* @return Returns the birthYear.
*/
public int getBirthYear() {
return birthYear;
}
/**
* @param birthYear The birthYear to set.
*/
public void setBirthYear(int birthYear) {
this.birthYear = birthYear;
}
/**
* @return Returns the firstname.
*/
public String getFirstname() {
return firstname;
}
/**
* @param firstname The firstname to set.
*/
public void setFirstname(String firstname) {
this.firstname = firstname;
}
/**
* @return Returns the name.
*/
public String getName() {
return name;
}
/**
* @param name The name to set.
*/
public void setName(String name) {
this.name = name;
}
}
A chaque « PERSON » chez Oracle correspondra un JavaBean « PersonBean ». Pour que ça fonctionne, PersonBean doit implémenter SQLData et Serializable. La méthode getSQLTypeName() indique le type SQL correspondant au JavaBean. La méthode writeSQL() est utilisée dans le sens Java vers Oracle et La méthode readSQL() dans l'autre sens. L'ordre de lecture et d'écriture dans « stream » est très important. Il correspond à celui que vous avez utilisé pour déclarer les variables dans « PERSON ».
Créez la classe SQL2Java ainsi :
/**
* @author Hyacinthe MENIET
* Created on 19 janv. 2006
*/
package net.dotmyself.j2sql;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Map;
/**
* Creates JavaBeans which contain data from Oracle.
*/
public class SQL2Java {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
int pid;
PersonBean person;
Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@192.168.0.8.1:1520:JBD","login","password");
Map map = conn.getTypeMap();
map.put("PERSON",Class.forName("net.dotmyself.j2sql.PersonBean"));
conn.setTypeMap(map);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT f_id,f_pers FROM FRIENDS");
while(rs.next()) {
pid = rs.getInt("F_ID");
person = (PersonBean) rs.getObject("F_PERS");
System.out.println(pid+". "+person.getFirstname()+" "+person.getName()+" ( "+person.getBirthYear()+" )");
}
rs.close();
stmt.close();
conn.close();
}
}
Cette classe instancie des JavaBean à partir de données Oracle. Elle réalise le sens Oracle vers Java.
Créez la classe Java2SQL ainsi :
/**
* @author Hyacinthe MENIET
* Created on 19 janv. 2006
*/
package net.dotmyself.j2sql;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.util.Map;
/**
* Creates Oracle objects which contain data from JavaBeans.
*/
public class Java2SQL {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
PersonBean person = new PersonBean();
Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@192.168.0.8.1:1520:JBD","login","password");
Map map = conn.getTypeMap();
map.put("PERSON",Class.forName("net.dotmyself.j2sql.PersonBean"));
conn.setTypeMap(map);
person.setBirthYear(2005);
person.setFirstname("dot");
person.setName("Myself");
PreparedStatement pstmt = conn.prepareStatement ("INSERT INTO FRIENDS VALUES (?,?)");
pstmt.setInt(1, 4);
pstmt.setObject(2, person);
pstmt.executeUpdate();
pstmt.close();
conn.close();
}
}
Cette Classe créée des objets Oracle à partir de JavaBeans. Elle réalise le sens Java vers Oracle.
Il ne vous reste plus qu'à modifier les chaînes connections respectives et à les exécuter.
Les « Oracle REF CURSOR » sont des références sur des curseurs, ils s'utilisent généralement dans du code PL/SQL. Pour illustrer cette partie, vous allez créer une référence sur un curseur dans un package PL/SQL puis vous récupérerez les données référencées, dans une classe Java.
Créez une table « CONTACTS » ainsi :
create table CONTACTS ( c_id number primary key, c_firstname varchar2(25), c_name varchar2(25) )
Insérez quelques contacts dans « CONTACTS » :
INSERT INTO CONTACTS VALUES (1,'Hyacinthe','MENIET'); INSERT INTO CONTACTS VALUES (2,'Larry','ELLISON'); INSERT INTO CONTACTS VALUES (3,'Bill','JOY');
Créez le package DEMO PL/SQL. La déclaration est la suivante :
CREATE OR REPLACE PACKAGE DEMO AS TYPE ref_cursor IS REF CURSOR; PROCEDURE get_contacts (p_ref OUT ref_cursor); END DEMO;
Le body ressemble à ça :
CREATE OR REPLACE PACKAGE BODY DEMO AS
PROCEDURE get_contacts (p_ref OUT ref_cursor)
IS
BEGIN
OPEN p_ref for
SELECT c_id,c_firstname,c_name
FROM CONTACTS
ORDER BY c_name,c_firstname;
END get_contacts;
END DEMO;
Créez la classe Ref2Java qui permet d'appeler le code PL/SQL ci-dessus :
/**
* @author Hyacinthe MENIET
* Created on 17 janv. 2006
*/
package net.dotmyself.j2sql;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
/**
* Displays data from Oracle.
*/
public class Ref2Java {
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@192.168.0.8.1:1520:JBD","login","password");
CallableStatement cstmt = conn.prepareCall("{call DEMO.get_contacts(?) }");
cstmt.registerOutParameter(1,oracle.jdbc.driver.OracleTypes.CURSOR); // p_ref
cstmt.executeQuery();
ResultSet rs = (ResultSet) cstmt.getObject(1);
while (rs.next()) {
System.out.println(rs.getInt("C_ID")+". "+rs.getString("C_FIRSTNAME")+" "+rs.getString("C_NAME"));
}
rs.close();
cstmt.close();
conn.close();
}
}
Il ne vous reste plus qu'à modifier la chaîne de connexion et à exécuter la classe.