forked from gleu/pgdocs_fr
/
jdbc.xml
3218 lines (2603 loc) · 98.8 KB
/
jdbc.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="ISO-8859-15"?>
<!-- Dernière modification
le $Date$
par $Author$
révision $Revision$ -->
<chapter id="jdbc">
<title>Interface <acronym>JDBC</acronym></title>
<indexterm zone="jdbc">
<primary>JDBC</primary>
</indexterm>
<indexterm zone="jdbc">
<primary>Java</primary>
</indexterm>
<para>
<acronym>JDBC</acronym> est une <acronym>API</acronym> au cœur de
Java 1.1 et ultérieur. Elle fournit un ensemble standard d'interfaces vers
les bases de données compatibles <acronym>SQL</acronym>.
</para>
<para>
<productname>PostgreSQL</productname> fournit un pilote <acronym>JDBC</acronym> de
<firstterm>type 4</firstterm>. Le type 4 indique que le pilote est écrit en
pur Java et communique dans le propre protocole réseau du système des bases
de données. De ce fait, le pilote est indépendant de la plate-forme.
Une fois compilé, le pilote peut être utilisé sur tout système.
</para>
<para>
Ce chapitre n'a pas pour but d'être un guide complet de programmation
<acronym>JDBC</acronym> mais devrait vous aider à commencer. Pour plus
d'informations, référez-vous à la documentation standard de
l'<acronym>API</acronym> <acronym>JDBC</acronym>. De même, jetez un œil
sur les exemples inclus dans les sources.
</para>
<sect1 id="jdbc-setup">
<title>Configurer le pilote <acronym>JDBC</acronym></title>
<para>
Cette section décrit les étapes nécessaires avant de commencer à écrire ou
exécuter des programmes utilisant l'interface <acronym>JDBC</acronym>.
</para>
<sect2 id="jdbc-build">
<title>Obtenir le pilote</title>
<para>
Les versions pré compilées du pilote sont téléchargeables à partir du <ulink
url="http://jdbc.postgresql.org">site web <acronym>JDBC</acronym> pour
<productname>PostgreSQL</productname></ulink>.
</para>
<para>
Vous pouvez aussi construire le pilote à partir des sources mais
vous n'avez réellement besoin de le faire que si vous modifiez le code
source. Pour plus de détails, référez-vous aux <link
linkend="installation">instructions d'installation</link> de
<productname>PostgreSQL</productname>. Après installation, le pilote devrait se
trouver dans
<filename><replaceable>PREFIX</replaceable>/share/java/postgresql.jar</filename>. Le
pilote résultant sera construit pour la version de Java que vous utilisez.
Si vous construisez avec un <acronym>JDK</acronym>, version 1.1, vous construirez
une version qui supporte la spécification <acronym>JDBC</acronym> 1,
si vous construisez avec un <acronym>JDK</acronym> 1.2 ou 1.3, vous construirez une
version supportant la spécification <acronym>JDBC</acronym> 2, et enfin si vous
construisez avec un <acronym>JDK</acronym> 1.4, vous construirez une version
supportant la spécification <acronym>JDBC</acronym> 3.
</para>
</sect2>
<sect2 id="jdbc-classpath">
<title>Configurer le chemin des classes</title>
<indexterm zone="jdbc-classpath">
<primary>chemin des classes</primary>
</indexterm>
<indexterm zone="jdbc-classpath">
<primary>CLASSPATH</primary>
</indexterm>
<para>
Pour utiliser le pilote, l'archive JAR (nommée
<filename>postgresql.jar</filename> si vous l'avez construite à partir des
sources, sinon elle sera probablement nommée
<filename>pg&majorversion;jdbc1.jar</filename>,
<filename>pg&majorversion;jdbc2.jar</filename> ou
<filename>pg&majorversion;jdbc3.jar</filename> respectivement pour
les versions <acronym>JDBC</acronym> 1, <acronym>JDBC</acronym> 2 et <acronym>JDBC</acronym>
3) a besoin d'être inclue dans le chemin des classes, soit en le plaçant
dans la variable d'environnement <envar>CLASSPATH</envar> soit en utilisant
des options sur la ligne de commande <command>java</command>.
</para>
<para>
Pour l'instant, supposons que nous avons une application qui utilise le
pilote <acronym>JDBC</acronym> pour accéder à une base de données et que
l'application est installée en tant que
<filename>/usr/local/lib/myapp.jar</filename>. Le pilote <acronym>JDBC</acronym> de
PostgreSQL est installé comme
<filename>/usr/local/pgsql/share/java/postgresql.jar</filename>. Pour lancer
l'application, nous pourrions utiliser :
<programlisting>export CLASSPATH=/usr/local/lib/myapp.jar:/usr/local/pgsql/share/java/postgresql.jar:.
java MyApp
</programlisting>
</para>
<para>
Charger le pilote dans l'application est traité dans
<xref linkend="jdbc-use"/>.
</para>
</sect2>
<sect2 id="jdbc-prepare">
<title>Préparer le serveur de bases de données pour
<acronym>JDBC</acronym></title>
<para>
Comme Java utilise uniquement les connexions TCP/IP, le serveur
<application>PostgreSQL</application> doit être configuré pour accepter les
connexions TCP/IP. Ceci se fait en configurant <literal>tcpip_socket =
true</literal> dans le fichier <filename>postgresql.conf</filename> ou en
fournissant l'option <option>-i</option> au lancement de
<command>postmaster</command>.
</para>
<para>
De plus, la méthode d'authentification du client dans le fichier
<filename>pg_hba.conf</filename> pourrait avoir besoin d'être configurée.
Référez-vous à <xref linkend="client-authentication"/> pour plus de détails.
Le pilote <acronym>JDBC</acronym> supporte les méthodes d'accès
<literal>trust</literal>, <literal>ident</literal>, <literal>password</literal>, <literal>md5</literal>,
et <literal>crypt</literal>.
</para>
</sect2>
</sect1>
<sect1 id="jdbc-use">
<title>Initialiser le pilote</title>
<para>
Cette section décrit comment charger et initialiser le pilote
<acronym>JDBC</acronym> dans vos programmes.
</para>
<sect2 id="jdbc-import">
<title>Importer <acronym>JDBC</acronym></title>
<para>
Toute source utilisant <acronym>JDBC</acronym> a besoin d'importer le paquet
<literal>java.sql</literal> en utilisant :
<programlisting>import java.sql.*;
</programlisting>
</para>
<note>
<para>
N'importez pas le paquet <literal>org.postgresql</literal>. Si vous le
faites, vos sources ne compileront pas car <command>javac</command> sera
en pleine confusion.
</para>
</note>
</sect2>
<sect2 id="jdbc-load">
<title>Charger le pilote</title>
<para>
Avant de vous connecter à une base de données, vous avez besoin de charger
le pilote. Deux méthodes sont disponibles et celui à utiliser dépend de
votre code.
</para>
<para>
Dans la première méthode, votre code charge implicitement le pilote en
utilisant la méthode <function>Class.forName()</function>. Pour
<productname>PostgreSQL</productname>, vous devriez utiliser :
<programlisting>Class.forName("org.postgresql.Driver");
</programlisting>
Ceci charge le pilote et, pendant le chargement, le pilote s'enregistre
lui-même avec <acronym>JDBC</acronym>.
</para>
<note>
<para>
La méthode <function>forName()</function> peut renvoyer
<classname>ClassNotFoundException</classname> si le pilote n'est pas
disponible.
</para>
</note>
<para>
C'est la méthode la plus communément utilisée mais elle oblige votre
code à utiliser uniquement <productname>PostgreSQL</productname>. Si votre
code peut accéder à d'autres systèmes de bases de données dans le futur
et que vous ne souhaitez pas utiliser d'extensions spécifiques à
<productname>PostgreSQL</productname>, alors la deuxième méthode est
conseillée.
</para>
<para>
La seconde méthode passe le pilote en paramètre à la <acronym>JVM</acronym>
lors de son lancement en utilisant l'argument <option>-D</option>.
Exemple :
<programlisting>java -Djdbc.drivers=org.postgresql.Driver example.ImageViewer
</programlisting>
Dans cet exemple, la <acronym>JVM</acronym> tentera de charger le pilote
pendant son initialisation. Ceci fait, <classname>ImageViewer</classname>
est lancée.
</para>
<para>
Cette méthode est la meilleure car elle permet à
votre code d'être utilisé avec d'autres paquets de bases de données sans
recompiler le code. Le seul élément qui pourrait changer est
l'<acronym>URL</acronym> de connexion. Ce point est traité tout de suite après.
</para>
<para>
Une dernière chose : Quand votre code essaie d'ouvrir une
<classname>Connection</classname> et que vous obtenez une
<classname>SQLException</classname> de type <errorname>No driver
available</errorname> , ceci est probablement dû au fait que pilote ne fait
pas partie du chemin de classe ou que la valeur du paramètre n'est pas
correcte.
</para>
</sect2>
<sect2 id="jdbc-connect">
<title>Se connecter à la base de données</title>
<para>
Avec <acronym>JDBC</acronym>, une base de données est représentée par une
<acronym>URL</acronym> (Uniform Resource Locator). Avec
<application>PostgreSQL</application>, elle peut prendre l'une des formes
suivantes :
<itemizedlist>
<listitem>
<synopsis>jdbc:postgresql:<replaceable class="parameter">base_de_données</replaceable>
</synopsis>
</listitem>
<listitem>
<synopsis>jdbc:postgresql://<replaceable class="parameter">hôte</replaceable>/<replaceable
class="parameter">base_de_données</replaceable>
</synopsis>
</listitem>
<listitem>
<synopsis>jdbc:postgresql://<replaceable class="parameter">hôte</replaceable>:<replaceable
class="parameter">port</replaceable>/<replaceable
class="parameter">base_de_données</replaceable>
</synopsis>
</listitem>
</itemizedlist>
Les paramètres ont les significations suivantes :
<variablelist>
<varlistentry>
<term>
<replaceable class="parameter">hôte</replaceable>
</term>
<listitem>
<para>
Le nom d'hôte du serveur. Par défaut, <literal>localhost</literal>.
Pour spécifier une adresse IPv6, vous devez englober le paramètre
<replaceable class="parameter">hôte</replaceable> avec des crochets, par
exemple :
<programlisting>jdbc:postgresql://[::1]:5740/accounting
</programlisting>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<replaceable class="parameter">port</replaceable>
</term>
<listitem>
<para>
Le numéro de port sur lequel le serveur est en écoute. Par défaut, il
s'agit du numéro de port standard de
<productname>PostgreSQL</productname> (5432).
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
<replaceable class="parameter">base_de_données</replaceable>
</term>
<listitem>
<para>
Le nom de la base de données.
</para>
</listitem>
</varlistentry>
</variablelist>
</para>
<para>
Pour vous connecter, vous avez besoin d'une instance de
<classname>Connection</classname> provenant de <acronym>JDBC</acronym>.
Pour cela, vous utilisez la méthode
<function>DriverManager.getConnection()</function> :
<programlisting>Connection db = DriverManager.getConnection(url, username, password);
</programlisting>
</para>
</sect2>
<sect2 id="jdbc-disconnect">
<title>Fermer la connexion</title>
<para>
Pour fermer la connexion de la base de données, appelez simplement la
méthode <function>close()</function> pour la
<classname>Connection</classname> :
<programlisting>db.close();
</programlisting>
</para>
</sect2>
</sect1>
<sect1 id="jdbc-query">
<title>Lancer une requête et traiter les résultats</title>
<indexterm zone="jdbc-query">
<primary>Statement</primary>
</indexterm>
<indexterm zone="jdbc-query">
<primary>PreparedStatement</primary>
</indexterm>
<indexterm zone="jdbc-query">
<primary>ResultSet</primary>
</indexterm>
<para>
À chaque fois que vous voulez exécuter des instructions
<acronym>SQL</acronym> sur la base de données, vous avez besoin d'une
instance <classname>Statement</classname> ou
<classname>PreparedStatement</classname>. Une fois que vous avez un
<classname>Statement</classname> ou un
<classname>PreparedStatement</classname>, vous pouvez lancer une requête.
Elle renvoie une instance <classname>ResultSet</classname> contenant le
résultat complet (voir <xref linkend="jdbc-query-with-cursor"/> pour savoir
comment modifier ce comportement). <xref linkend="jdbc-query-example"/>
illustre ce processus.
</para>
<example id="jdbc-query-example">
<title>Traiter une requête simple avec <acronym>JDBC</acronym></title>
<para>
Cet exemple lance une requête simple et affiche la première colonne de
chaque ligne en utilisant un <classname>Statement</classname>.
<programlisting>Statement st = db.createStatement();
ResultSet rs = st.executeQuery("SELECT * FROM matable WHERE macolonne = 500");
while (rs.next()) {
System.out.print("Colonne 1 renvoyée ");
System.out.println(rs.getString(1));
}
rs.close();
st.close();
</programlisting>
</para>
<para>
Cet exemple lance la même requête que précédemment mais utilise un
<classname>PreparedStatement</classname> et une valeur liée dans la
requête.
<programlisting>int foovalue = 500;
PreparedStatement st = db.prepareStatement("SELECT * FROM matable WHERE
ma colonne = ?");
st.setInt(1, valeur);
ResultSet rs = st.executeQuery();
while (rs.next()) {
System.out.print("Colonne 1 renvoyée ");
System.out.println(rs.getString(1));
}
rs.close();
st.close();
</programlisting>
</para>
</example>
<sect2 id="jdbc-query-with-cursor">
<title>Obtenir des résultats basés sur un curseur</title>
<para>Par défaut, le pilote récupère tous les résultats d'une requête en
une seule fois. Ceci présente des inconvénients pour les gros ensembles de
données. Le pilote JDBC fournit un moyen de baser un
<classname>ResultSet</classname> sur un curseur de bases de données pour ne
récupérer qu'un petit nombre de lignes.</para>
<para>Un bloc de lignes est en cache du côté client de la
connexion et, une fois qu'il est entièrement lu, le prochain bloc de
lignes est récupéré en repositionnant le curseur.
</para>
<example>
<title>Configurer la taille de la récupération pour activer ou désactiver
les curseurs.</title>
<para>Passer le code en mode curseur revient à configurer
la taille de récupération de <classname>Statement</classname> à la bonne
taille. Le reconfigurer à 0 fera que toutes les lignes seront en cache (le
comportement par défaut).
<programlisting>Statement st = db.createStatement();
// Active le curseur.
st.setFetchSize(50);
ResultSet rs = st.executeQuery("SELECT * FROM matable");
while (rs.next()) {
System.out.print("une ligne a été renvoyée.");
}
rs.close();
// Désactive le curseur.
st.setFetchSize(0);
ResultSet rs = st.executeQuery("SELECT * FROM matable");
while (rs.next()) {
System.out.print("toutes les lignes ont été renvoyées.");
}
rs.close();
// Ferme l'instruction.
st.close();
</programlisting>
</para>
</example>
</sect2>
<sect2>
<title>Utiliser les interfaces <classname>Statement</classname> ou
<classname>PreparedStatement</classname></title>
<para>
Points à considérer pour l'utilisation de l'interface
<classname>Statement</classname> ou
<classname>PreparedStatement</classname> :
<itemizedlist>
<listitem>
<para>
Vous pouvez utiliser une simple instance
<classname>Statement</classname> autant de fois que vous le voulez. Vous
pouvez aussi en créer une dès que vous avez ouvert la connexion et
l'utiliser pendant toute la durée de la connexion. Mais vous devez
vous rappeler qu'un seul <classname>ResultSet</classname> peut
exister par <classname>Statement</classname> ou
<classname>PreparedStatement</classname> à un moment donné.
</para>
</listitem>
<listitem>
<para>
Si vous avez besoin de lancer une requête pendant le traitement d'un
<classname>ResultSet</classname>, il suffit de créer et
utiliser un autre <classname>Statement</classname>.
</para>
</listitem>
<listitem>
<para>
Si vous utilisez les threads et que plusieurs personnes
utilisent la base de données, vous pouvez utiliser un
<classname>Statement</classname> pour chaque thread. Référez-vous à
<xref linkend="jdbc-thread"/> si vous pensez utiliser les threads car il
couvre quelques points importants.
</para>
</listitem>
<listitem>
<para>
Une fois que vous avez fini d'utiliser
<classname>Statement</classname> ou
<classname>PreparedStatement</classname>, vous devez le fermer.
</para>
</listitem>
</itemizedlist>
</para>
</sect2>
<sect2>
<title>Utiliser l'interface <classname>ResultSet</classname></title>
<para>
Points à considérer pour l'utilisation de l'interface
<classname>ResultSet</classname> :
<itemizedlist>
<listitem>
<para>
Avant de lire une valeur, vous devez appeler la fonction
<function>next()</function>. Elle renvoie true s'il existe un résultat
mais, surtout, elle prépare le traitement de la ligne.
</para>
</listitem>
<listitem>
<para>
La spécification <acronym>JDBC</acronym> précise que vous ne devez accéder un
champ que une fois seulement. Il est plus sûr de se tenir à cette règle bien
qu'actuellement le pilote <productname>PostgreSQL</productname> vous
autorise à accéder à un champ autant de fois que vous le souhaitez.
</para>
</listitem>
<listitem>
<para>
Vous devez fermer un <classname>ResultSet</classname> en appelant
<function>close()</function> une fois que vous avez fini de
l'utiliser.
</para>
</listitem>
<listitem>
<para>
Lorsque vous faites une autre requête avec le
<classname>Statement</classname> utilisé pour créer un
<classname>ResultSet</classname>, l'instance
<classname>ResultSet</classname> initialement ouverte est fermée
automatiquement.
</para>
</listitem>
</itemizedlist>
</para>
</sect2>
</sect1>
<sect1 id="jdbc-update">
<title>Exécuter des mises à jour</title>
<para>
Pour modifier les données (réaliser un <command>INSERT</command>,
<command>UPDATE</command> ou <command>DELETE</command>), vous utilisez la
méthode <function>executeUpdate()</function>. Cette méthode est similaire à
la méthode <function>executeQuery()</function> utilisée pour exécuter une
instruction <command>SELECT</command> mais elle ne renvoie pas de
<classname>ResultSet</classname> ; à la place, elle renvoie le nombre de
lignes affectées par l'instruction <command>INSERT</command>,
<command>UPDATE</command> ou <command>DELETE</command>. <xref
linkend="jdbc-delete-example"/> illustre cette utilisation.
</para>
<example id="jdbc-delete-example">
<title>Supprimer des lignes dans <acronym>JDBC</acronym></title>
<para>
Cet exemple exécute une instruction <command>DELETE</command> simple et
affiche le nombre de lignes réellement supprimées.
<programlisting>int valeur = 500;
PreparedStatement st = db.prepareStatement("DELETE FROM matable WHERE macolonne
= ?");
st.setInt(1, valeur);
int rowsDeleted = st.executeUpdate();
System.out.println(rowsDeleted + " lignes supprimées");
st.close();
</programlisting>
</para>
</example>
</sect1>
<sect1 id="jdbc-callproc">
<title>Appeler des procédures stockées</title>
<para>Le pilote JDBC de <productname>PostgreSQL</productname> supporte
totalement l'appel de procédures stockées
<productname>PostgreSQL</productname>.</para>
<example id="jdbc-call-function">
<title>Appeler une fonction interne</title>
<para>Cet exemple montre comment appeler une fonction interne
de <productname>PostgreSQL</productname>, <command>upper</command>, qui
convertit simplement l'argument fourni en majuscule.
<programlisting>// Désactive les transactions.
con.setAutoCommit(false);
// Appel de procédure.
CallableStatement upperProc = con.prepareCall("{ ? = call upper( ? ) }");
upperProc.registerOutParameter(1, Types.VARCHAR);
upperProc.setString(2, "minuscule vers majuscule");
upperProc.execute();
String upperCased = upperProc.getString(1);
upperProc.close();
</programlisting>
</para>
</example>
<sect2>
<title>Utiliser l'interface
<classname>CallableStatement</classname></title>
<para>
Toutes les considérations s'appliquant à
<classname>Statement</classname> et
<classname>PreparedStatement</classname> s'appliquent à
<classname>CallableStatement</classname> mais vous devez considérer une
restriction supplémentaire :
</para>
<itemizedlist>
<listitem>
<para>Vous devez être à l'intérieur d'une transaction pour
appeler une procédure stockée.</para>
</listitem>
</itemizedlist>
</sect2>
<sect2>
<title>Obtenir un <classname>ResultSet</classname> à partir d'une
procédure stockée</title>
<para>Les procédures stockées de <productname>PostgreSQL</productname>
peuvent renvoyer des résultats au moyen d'une valeur de type
<type>refcursor</type>. Un <type>refcursor</type>.</para>
<para>En extension de l'API JDBC, le pilote JDBC de
<productname>PostgreSQL</productname> peut renvoyer des valeurs de type
<type>refcursor</type> comme valeurs
<classname>ResultSet</classname>.</para>
<example id="get-refcursor-from-function-call">
<title>Obtenir des valeurs <type>refcursor</type> à partir d'une
fonction</title>
<para>Lors de l'appel d'une fonction renvoyant un
<type>refcursor</type>, vous devez convertir le type de retour de
<function>getObject</function> à un
<classname>ResultSet</classname></para>
<programlisting>// Désactive les transactions.
con.setAutoCommit(false);
// Appel de procédure.
CallableStatement proc = con.prepareCall("{ ? = call fait_requete ( ? ) }");
proc.registerOutParameter(1, Types.Other);
proc.setInt(2, -1);
proc.execute();
ResultSet results = (ResultSet) proc.getObject(1);
while (results.next()) {
// faire quelque chose avec les résultats...
}
results.close();
proc.close();
</programlisting>
</example>
<para>Il est aussi possible de traiter la valeur de
retour <type>refcursor</type> comme un type distinct en lui-même. Le
pilote JDBC fournit la classe
<classname>org.postgresql.PGRefCursorResultSet</classname> dans ce
but.</para>
<example>
<title>Traiter <type>refcursor</type> comme un type distinct</title>
<programlisting>con.setAutoCommit(false);
CallableStatement proc = con.prepareCall("{ ? = call fait_requete ( ? ) }");
proc.registerOutParameter(1, Types.Other);
proc.setInt(2, 0);
org.postgresql.PGRefCursorResultSet refcurs
= (PGRefCursorResultSet) con.getObject(1);
String cursorName = refcurs.getRefCursor();
proc.close();
</programlisting>
</example>
</sect2>
</sect1>
<sect1 id="jdbc-ddl">
<title>Créer et modifier les objets de la base de données</title>
<para>
Pour créer, modifier ou supprimer un objet de la base de données comme une
table ou une vue, vous utilisez la méthode <function>execute()</function>.
Cette méthode est similaire à la méthode <function>executeQuery()</function>
mais elle ne renvoie pas de résultat. <xref linkend="jdbc-drop-table-example"/>
illustre cette utilisation.
</para>
<example id="jdbc-drop-table-example">
<title>Supprimer une table avec JDBC</title>
<para>
Cet exemple supprime une table.
<programlisting>Statement st = db.createStatement();
st.execute("DROP TABLE matable");
st.close();
</programlisting>
</para>
</example>
</sect1>
<sect1 id="jdbc-binary-data">
<title>Stocker des données binaires</title>
<indexterm zone="jdbc-binary-data">
<primary>bytea</primary>
<secondary sortas="JDBC">en JDBC</secondary>
</indexterm>
<indexterm zone="jdbc-binary-data">
<primary>gros objets</primary>
<secondary sortas="JDBC">en JDBC</secondary>
</indexterm>
<para>
<application>PostgreSQL</application> fournit deux façons distinctes de
stocker des données binaires. Elles peuvent être stockées dans une table en
utilisant le type de données <type>bytea</type> ou en utilisant la
fonctionnalité des gros objets qui stockent les données binaires dans une
table séparée dans un format spécial et se réfère à cette table en stockant
une valeur de type <type>oid</type> dans votre table.
</para>
<para>
Pour déterminer quelle méthode est appropriée, vous avez besoin de
comprendre les limitations de chaque méthode. Le type de données
<type>bytea</type> ne convient pas bien pour stocker de grosses
quantités de données binaires. Alors qu'une colonne de type
<type>bytea</type> peut contenir jusqu'à 1 Go de données binaires, elle
demanderait une énorme quantité de mémoire pour traiter une telle valeur. La
méthode des objets larges convient mieux pour stocker de très grands objets
mais a ses propres limitations. Spécifiquement, supprimer une ligne
contenant une référence de gros objet ne supprime pas le gros objet.
Supprimer celui-ci est une opération qui doit être réalisée séparément. Les
gros objets ont aussi quelques problèmes de sécurité car toute personne
connectée à la base de données peut visualiser et/modifier tout gros objet
même s'il n'a pas le droit de visualiser/mettre à jour la ligne contenant la
référence au gros objet.
</para>
<para>
La version 7.2 a été la première à disposer d'un pilote
<acronym>JDBC</acronym> supportant le type de données <type>bytea</type>.
L'introduction de cette fonctionnalité dans la 7.2 a introduit une
changement dans le comportement comparé aux précédentes versions. Depuis la
7.2, les méthodes <function>getBytes()</function>,
<function>setBytes()</function>, <function>getBinaryStream()</function> et
<function>setBinaryStream()</function> opèrent sur le type de données
<type>bytea</type>. En 7.1 et pour les versions précédentes, ces méthodes
opéraient sur le type de données <type>oid</type> associé avec chaque gros
objet. Il est possible de revenir à l'ancien comportement de la version 7.1
en initialisant la propriété <literal>compatible</literal> de l'objet
<classname>Connection</classname> avec la valeur <literal>7.1</literal>.
</para>
<para>
Pour utiliser le type de données <type>bytea</type>, vous devez simplement
utiliser les méthodes <function>getBytes()</function>,
<function>setBytes()</function>,
<function>getBinaryStream()</function> ou
<function>setBinaryStream()</function>.
</para>
<para>
Pour utiliser la fonctionnalité des gros objets, vous devez utiliser soit
la classe <classname>LargeObject</classname> fournie par le pilote
<acronym>JDBC</acronym> de <application>PostgreSQL</application> soit
utiliser les méthodes <function>getBLOB()</function> et
<function>setBLOB()</function>.
</para>
<important>
<para>
Vous devez accéder aux gros objets à l'intérieur d'un bloc de transaction
<acronym>SQL</acronym>. Vous pouvez lancer un bloc de transaction en
appelant <function>setAutoCommit(false)</function>.
</para>
</important>
<note>
<para>
Dans une prochaine version du pilote <acronym>JDBC</acronym> driver, les
méthodes <function>getBLOB()</function> et <function>setBLOB()</function>
n'interagiront plus avec les gros objets mais fonctionneront avec le type
de données <type>bytea</type>. Donc, il vous est recommandé d'utiliser
l'<acronym>API</acronym> de <classname>LargeObject</classname> si vous
pensez utiliser des gros objets.
</para>
</note>
<para>
<xref linkend="jdbc-binary-data-example"/> contient quelques exemples sur le
traitement de données binaires en utilisant le pilote <acronym>JDBC</acronym> de
PostgreSQL.
</para>
<example id="jdbc-binary-data-example">
<title>Traiter des données binaires avec <acronym>JDBC</acronym></title>
<para>
Par exemple, supposez que vous avez une table contenant des noms de fichiers
d'images et que vous souhaitez aussi stocker l'image dans une colonne de
type <type>bytea</type> :
<programlisting>CREATE TABLE images (nomimg text, img bytea);
</programlisting>
</para>
<para>
Pour insérer une image, vous utiliserez :
<programlisting>File file = new File("monimage.gif");
FileInputStream fis = new FileInputStream(file);
PreparedStatement ps = conn.prepareStatement("INSERT INTO images VALUES (?, ?)");
ps.setString(1, file.getName());
ps.setBinaryStream(2, fis, file.length());
ps.executeUpdate();
ps.close();
fis.close();
</programlisting>
Ici, <function>setBinaryStream()</function> transfère un certain nombre
d'octets d'un flux dans la colonne de type <type>bytea</type>. Ceci
pourrait aussi être fait en utilisant la méthode
<function>setBytes()</function> si le contenu de l'image était déjà dans un
<classname>byte[]</classname>.
</para>
<para>
Récupérer une image est encore plus simple. (Nous utilisons
<classname>PreparedStatement</classname>, mais la classe
<classname>Statement</classname> pourrait très bien la remplacer.)
<programlisting>PreparedStatement ps = con.prepareStatement("SELECT img FROM images WHERE nomimg
= ?");
ps.setString(1, "monimage.gif");
ResultSet rs = ps.executeQuery();
if (rs != NULL) {
while (rs.next()) {
byte[] imgBytes = rs.getBytes(1);
// utiliser les données ici...
}
rs.close();
}
ps.close();
</programlisting>
</para>
<para>
Ici, les données binaires ont été récupérées dans un
<classname>byte[]</classname>. À la place, vous pourriez avoir utilisé un
objet <classname>InputStream</classname>.
</para>
<para>
Vous pouvez aussi stocker un très gros fichier et vouloir utiliser
l'<acronym>API</acronym> <classname>LargeObject</classname> pour stocker le
fichier :
<programlisting>CREATE TABLE imageslo (nomimg text, oidimg oid);
</programlisting>
</para>
<para>
Pour insérer une image, vous pourriez :
<programlisting>// Tous les appels à l'API LargeObject doivent se faire dans un bloc de
// transaction
conn.setAutoCommit(false);
// Récupérer le gestionnaire des gros objets pour lui faire réaliser les
// opérations
LargeObjectManager lobj =
((org.postgresql.PGConnection)conn).getLargeObjectAPI();
// Créer un nouveau gros objet
int oid = lobj.create(LargeObjectManager.READ | LargeObjectManager.WRITE);
// Ouvrir le gros objet en écriture
LargeObject obj = lobj.open(oid, LargeObjectManager.WRITE);
// Maintenant, ouvrir le fichier
File file = new File("monimage.gif");
FileInputStream fis = new FileInputStream(file);
// Copier les données du fichier dans le gro objet
byte buf[] = new byte[2048];
int s, tl = 0;
while ((s = fis.read(buf, 0, 2048)) > 0) {
obj.write(buf, 0, s);
tl += s;
}
// Fermer le gros objet
obj.close();
// Maintenant, insérer la ligne dans imageslo
PreparedStatement ps = conn.prepareStatement("INSERT INTO imageslo VALUES (?, ?)");
ps.setString(1, file.getName());
ps.setInt(2, oid);
ps.executeUpdate();
ps.close();
fis.close();
</programlisting>
</para>
<para>
Pour récupérer l'image du gros objet :
<programlisting>// Tous les appels à l'API LargeObject doivent se faire dans un bloc de
// transaction
conn.setAutoCommit(false);
// Récupérer le gestionnaire des gros objets pour lui faire réaliser les
// opérations
LargeObjectManager lobj =
((org.postgresql.PGConnection)conn).getLargeObjectAPI();
PreparedStatement ps = con.prepareStatement("SELECT oidimg FROM imageslo
WHERE nomimg = ?");
ps.setString(1, "monimage.gif");
ResultSet rs = ps.executeQuery();
if (rs != NULL) {
while (rs.next()) {
// Ouvrir le gros objet en lecture
int oid = rs.getInt(1);
LargeObject obj = lobj.open(oid, LargeObjectManager.READ);
// Lire les données
byte buf[] = new byte[obj.size()];
obj.read(buf, 0, obj.size());
// Utiliser les données ici
// Fermer l'objet
obj.close();
}
rs.close();
}
ps.close();
</programlisting>
</para>
</example>
</sect1>
<sect1 id="jdbc-ext">
<title>Extensions <application>PostgreSQL</application> à
l'<acronym>API</acronym> de <acronym>JDBC</acronym></title>
<para>
<productname>PostgreSQL</productname> est un système de bases de données
extensible. Vous pouvez ajouter vos propres fonctions au serveur, qui seront
ensuite appelées à partir des requêtes, voire même ajouter vos propres types
de données. Comme nous les supportons à partir de Java, avec un ensemble
d'extensions de l'<acronym>API</acronym>, certaines fonctionnalités à
l'intérieur du cœur du pilote standard utilisent en fait ces extensions
pour implémenter les gros objets, etc.
</para>
<sect2>
<title>Accéder aux Extensions</title>
<para>
Pour accéder à certaines des extensions, il faut utiliser
quelques méthodes supplémentaires dans la classe
<classname>org.postgresql.PGConnection</classname>. Dans ce cas, vous aurez
besoin de convertir la valeur de retour de
<function>Driver.getConnection()</function>. Par exemple :
<programlisting>Connection db = Driver.getConnection(url, username, password);
// ...
// plus tard
Fastpath fp = ((org.postgresql.PGConnection)db).getFastpathAPI();
</programlisting>
</para>
<sect3>
<title>Classe <classname>org.postgresql.PGConnection</classname></title>
<synopsis>public class PGConnection
</synopsis>
<para>
Quelques méthodes supplémentaires sont utilisées pour accéder aux
extensions de <productname>PostgreSQL</productname>.
</para>
<sect4>
<title>Méthodes</title>
<itemizedlist>
<listitem>
<synopsis>public Fastpath getFastpathAPI() throws SQLException
</synopsis>
<para>
Ceci renvoie le <quote>fast-path</quote> de l'<acronym>API</acronym>
pour la connexion en cours. Il est principalement utilisé par