
Intégration JAVA & RPG.
Voyez d'abord les concepts Java Native Interface (JNI)
Projet JDBCR4
Scott Klement propose un projet en Open Source sur IBM i
Il s'agit d'un programme de service qui sert de passerelle avec JDBC , écrit en RPG pour RPG : JDBCR4
Téléchargement à l'adresse: hhttps://www.scottklement.com/jdbc/RpgAndJdbc.zip
la documentation est disponible à: http://www.scottklement.com/presentations/External%20Databases%20from%20RPG.pdf
Pour utiliser un Driver JDBC celui-ci doit être de type 4 (écrit en Java) il est en général livré sous forme de fichier .jar
- MySQL: mysql-connector-java-3.1.12-bin.jar
- Oracle (thin): ojdbc14.jar
- SQL Server: sqljdbc.jar
- (version open source) : jtds-1.2.5.jar (les n° de version peuvent changer)
- DB2 for i: jt400.jar
- IBM DB2 db2jcc.jar
- Placez ce fichier dans un répertoire de l'IFS et modifiez votre CLASSPATH par
ADDENVVAR ENVVAR(CLASSPATH) VALUE('.:/java:/java/mysql-connector.jar') , par exemple.
Il vous faut impérativement connaitre la classe du driver et l'URL de connexion
SQL Server |
com.microsoft.sqlserver.jdbc.SQLServerDriver |
jdbc:sqlserver://myserver.example.com:1433 |
Jtds |
net.sourceforge.jtds.jdbc.Driver |
jdbc:jtds:sqlserver://myserver.example.com:1433 |
Oracle |
oracle.jdbc.OracleDriver |
jdbc:oracle:thin:@myserver.example.com:1521:myDataBase |
Mysql |
com.mysql.jdbc.Driver |
jdbc:mysql://myserver.example.com/myDataBase |
DB2 for i |
com.ibm.as400.access.AS400JDBCDriver |
jdbc:as400://myserver.example.com |
DB2 (autre) |
com.ibm.db2.jcc.DB2Driver |
jdbc:db2://myserver.example.com:50000/myDataBase |
Ecrivez ensuite un pgm java de test pour vérifier la validité du driver et la syntaxe de l'URL
import java.sql.*;
public class testJDBC
{
public static void main (String[] parameters)
{
String sqlurl = "jdbc:<voir le tableau ci-dessus>";
String userid = "uuuuu";
String password = "pppp";
Connection connection = null;
try {
DriverManager.registerDriver(new org.postgresql.Driver());
connection = DriverManager.getConnection ( sqlurl, userid, password );
Statement select = connection.createStatement ();
ResultSet rs = select.executeQuery("SELECT nom,cepage FROM vins");
while (rs.next ()) {
System.out.println (rs.getString(1) + "," + rs.getString(2));
}
} catch (Exception e) {
System.out.println ();
System.out.println ("Erreur: " + e.getMessage());
}
try {
if (connection != null)
connection.close ();
} catch (SQLException e) {
// Ignore.
}
}
}
|
Exemples


Compilez par javac (sous QSH)
RPG
Ceci étant fait, passons au RPG
Nous travaillerons avec une base de test sur laquelle nous aurons passé le script suivant
create database test;
CREATE TABLE clients (
nocli int(10) unsigned NOT NULL AUTO_INCREMENT,
nom varchar(45) NOT NULL,
dep decimal(2,0) DEFAULT NULL,
datcrt date DEFAULT NULL,
PRIMARY KEY (`nocli`)
);
INSERT INTO clients VALUES (1, 'premier client', 44, '2009-11-02');
INSERT INTO clients VALUES (2, 'deuxieme', 35, '2009-11-02');
INSERT INTO clients VALUES (3, 'et de trois', 22, '2009-10-31'); |
Connexion
- JDBC_connect
/copy jdbc_h
dcl-s conn like(Connection);
userid = 'root'; passwrd = 'xxxxx'; conn = JDBC_Connect('com.mysql.jdbc.Driver'
:'jdbc:mysql://myserver.example.com/test' :%trim(userid) :%trim(passwrd) );
if (conn = *NULL);
// Impossible de connecter la base MYSQL ! endif; |
- JDBC_setProp
Si vous avez besoin de propriétés particulières
dcl-s prop like(Properties);
userid = 'root';
passwrd = 'xxxxx';
prop = JDBC_Properties();
JDBC_setProp(prop: 'user' : %trim(userid) );
JDBC_setProp(prop: 'password' : %trim(passwrd));
JDBC_setProp(prop: 'connectTimeout': '60' );
conn = JDBC_ConnProp('com.mysql.jdbc.Driver'
:'jdbc:mysql://myserver.example.com/test' :prop) );
JDBC_freeProp(prop); |
Par exemple la propriété databaseName pour SQL server ou naming pour jt400
(voir la liste des propriétés pour chaque driver)
Manipulation de données
une fois connecté, vous devez distinguer deux types d'ordres
- Les ordres immédiats
- sont interprétés et exécutés dans la foulée
- Les ordres préparés
- sont interprétés une fois et exécutés, éventuellement, plusieurs fois.
- lors de la préparation, ils peuvent contenir des marqueurs (?), qui seront remplacés par des valeurs à l'exécution.
- JDBC_ExecUpd( connexion : 'ordre SQL ne retournant rien')
- Retourne
- 0 pour un ordre n'affectant aucune ligne
- n le nombre de lignes affectées
- -1 pour un ordre SQL en erreur
rc = JDBC_ExecUpd( conn : 'delete from clients' + ' where nocli = 999 '); if (rc < 0); // signaler une erreur endif; |
- JDBC_ExecQry( connexion : 'ordre SQL de type SELECT')
- Exécute la requête (comme un OPEN curseur)
- Retourne
- un objet resulSet
- *NULL pour un ordre SQL en erreur
dcl-s result like(ResultSet );
result= JDBC_ExecQry( conn : 'select nocli, raisoc, ville, tel' + ' from clients where dep = 44');
if (result = *null); // signaler une erreur
endif; |
- Lecture d'un resultSet
- JDBC_nextRow( ResultSet )
- se positionne ligne suivante (comme un FETCH)
- JDBC_getCol(ResultSet : NumCol )
- lit la valeur d'une colonne dont on fournit la position (à partir de 1)
- JDBC_getColByName(ResultSet : NomCol )
- lit la valeur d'une colonne dont on fournit le nom
- JDBC_freeResult (ResultSet)
- Ferme le ResultSet et libère la mémoire (comme CLOSE)
Exemple
dow JDBC_nextRow(Result);
Dept = JDBC_getCol(Result: 1);
EmpNo = %int(JDBC_getCol(Result: 2));
Name = JDBC_getCol(Result: 3);
// traitement des données lues. enddo; |
Les valeurs retounées sont toujours caractère, au besoin, utilisez
- %DEC(), %INT(), %UNS() pour le numérique
- %DATE() , %TIME() , %TIMESTP() pour les dates& heures
- %UCS2() pour l'Unicode
Métadonnées
une fois le resultSet créé vous pouvez retouver les métaDonnées (description des zones, comme DSPFFD)
- JDBC_getMetaData( ResultSet )
- retourne un objet MetaData décrivant le resultSet
- JDBC_getColCount( MetaData )
- retoune le nombre de colonnes
- JDBC_getColName( MetaData : NumCol))
- retoune le nom d'une colonne dont on fournit le n°
- JDBC_getColDspSize( MetaData: NumCol )
- retourne la taille (à l'affichage) d'une colonne
- JDBC_getColTypName( MetaData: NumCol )
- retourne le type d'une colonne
Instructions préparées
L'instruction est "compilée" à la volée et ses exécutions ultérieures seront plus rapides.
Elle peut contenir des marqueurs (?) représentant des valeurs fournies plus tard
- JDBC_PrepStmt( connexion : 'ordre SQL valide')
- Retourne un objet PreparedStatement pour l'instruction SQL
- JDBC_ExecPrepUpd( PreparedStatement )
- Exécute une instruction préparée qui ne retourne pas d'enregistrements
- JDBC_ExecPrepQry( PreparedStatement )
- Exécute une instruction préparée qui retourne un jeu d'enregistrements
- JDBC_FreePrepStmt( PreparedStatement )
- libère la mémoire associée à l'objet PreparedStatement
Exemple
/copy jdbc_h
dcl-s stmt like(PreparedStatement);
dcl-s result like(ResultSet );
Stmt = JDBC_PrepStmt( conn : 'Select nocli, raisoc + dep, ville + from clients order by raisoc');
if ( stmt = *null ); // signaler l'erreur
endif ;
Result = JDBC_ExecPrepQry( Stmt ); if (Result = *null); // autre erreur
endif; // lecture des enregistrements comme vu plus haut
JDBC_freeResult( result); JDBC_freePrepStmt( stmt ); |
- Si vous placez des marqueurs dans l'instruction préparée, vous devez fournir des valeurs avant l'exécution par :
- JDBC_setString( stmt : numéro paramètre : 'chaîne');
- JDBC_setInt( stmt : numéro paramètre : zone binaire);
- JDBC_setDouble( stmt : numéro paramètre : zone virg. flottante );
- JDBC_setDecimal( stmt : numéro paramètre : zone décimale);
- JDBC_setDate( stmt : numéro paramètre : zone date );
- JDBC_setTime( stmt : numéro paramètre : zone heure);
- JDBC_setTimestamp( stmt : numéro paramètre : zone horodatage);
Exemple
nocli = 1234; JDBC_SetInt( stmt: 1: nocli ); |
Les fonctions JDBC_getCol et JDBC_Setxxx (int, date, etc...) possèdent un paramètre supplémentaire, facultatif, de type indicateur.
- sur JDBC_getCol il sera positionné à *ON si la colonne est nulle,*OFF dans le cas contraire.
- sur JDBC_SetInt et les autres fonctions du même genre, vous l'envoyez à *ON pour signaler une valeur nulle, *OFF dans le cas contraire.
Exemple complet (Full free RPG)
Déclarations

Corps du programme
Ajout d'une ligne (instruction préparée)
Boucle de lecture
Delete
Testons
Résultat
Procédures cataloguées
Il existe aussi des instructions pour appeler des procédures cataloguées :
- JDBC_PrepCall( Connection : 'instruction CALL')
- JDBC_RegisterOutParameter( CallableStatement: ParmNum: DataType )
- prévient JDBC que ce paramètre est en sortie (valeur retour)
- JDBC_ExecCall( CallableStatement )
- Exécution
- retourne
- *ON si un jeu d'enregistrement est retourné par la procédure (SET RESULT SETS)
- *OFF dans le cas contraire
- JDBC_FreeCallStmt( CallableStatement )
- JDBC_getUpdateCount( CallableStatement )
- Quand une procédure ne retourne pas de jeu d'enregistrements, nombre de lignes modifiées par la procédure.
- JDBC_getResultSet( CallableStatement )
- retourne un jeu d'enregistrements comme ExecuteQuery
- JDBC_getMoreResults( CallableStatement )
- passe au jeu d'enregistrements suivant, retourne *OFF s'il n'y en a pas
JDBC_getString(), JDBC_getInt(), JDBC_getShort(), JDBC_getBoolean() permettent de récupérer les valeurs des paramètres en sortie
Contrôle de validation
Enfin, il y a deux instructions pour gérer les transactions
- JDBC_Commit()
- JDBC_Rollback
© Volubis