28 votes

Gestion des BLOBs Oracle JDBC trop compliquée

Quand je fais une recherche sur le web pour l'insertion d'objets Blob dans la base de données Oracle avec jdbc mince pilote, la plupart des pages web suggèrent un 3 étapes:

  1. insérez empty_blob() de la valeur.
  2. sélectionnez la ligne avec for update.
  3. insérer la valeur réelle.

Cela fonctionne très bien pour moi, voici un exemple:

Connection oracleConnection = ...

byte[] testArray = ...

PreparedStatement ps = oracleConnection.prepareStatement(
    "insert into test (id, blobfield) values(?, empty_blob())");
ps.setInt(1, 100);
ps.executeUpdate();
ps.close();
ps = oracleConnection.prepareStatement(
    "select blobfield from test where id = ? for update");
ps.setInt(1, 100);
OracleResultSet rs = (OracleResultSet) ps.executeQuery();
if (rs.next()) {
    BLOB blob = (BLOB) rs.getBLOB(1);
    OutputStream outputStream = blob.setBinaryStream(0L);
    InputStream inputStream = new ByteArrayInputStream(testArray);
    byte[] buffer = new byte[blob.getBufferSize()];
    int byteread = 0;
    while ((byteread = inputStream.read(buffer)) != -1) {
        outputStream.write(buffer, 0, byteread);
    }
    outputStream.close();
    inputStream.close();
}

Il y a quelques pages où les auteurs suggèrent à l'aide d'un simple 1-l'étape de la solution. Exemple précédent avec cette solution:

Connection oracleConnection = ...

byte[] testArray = ...

PreparedStatement ps = oracleConnection.prepareStatement(
    "insert into test(id, blobfield) values(?, ?)");
BLOB blob = BLOB.createTemporary(oracleConnection, false, BLOB.DURATION_SESSION);
OutputStream outputStream = blob.setBinaryStream(0L);
InputStream inputStream = new ByteArrayInputStream(testArray);
byte[] buffer = new byte[blob.getBufferSize()];
int byteread = 0;
while ((byteread = inputStream.read(buffer)) != -1) {
    outputStream.write(buffer, 0, byteread);
}
outputStream.close();
inputStream.close();

ps.setInt(1, 100);
ps.setBlob(2, blob);
ps.executeUpdate();
ps.close();

Le deuxième code est beaucoup plus facile, donc ma question est: qu'est-Ce que le premier point de (populaire) solution? Est-il (était-il) une sorte de contrainte pour la deuxième solution (serveur Oracle numéro de version du pilote jdbc version, la taille de la goutte,...)? La première solution mieux (vitesse, mémoire, consommation,...)? Toutes les raisons pour ne pas utiliser le plus simple seconde approche?

Exactement la même question s'applique pour CLOB champs.

11voto

L'approche de mise à jour que vous mentionnez dans le premier cas peut être réécrite en utilisant du code JDBC pur et ainsi réduire votre dépendance vis-à-vis des classes spécifiques à Oracle. Cela pourrait être utile si votre application doit être indépendante de la base de données.

 public static void updateBlobColumn(Connection con, String table, String blobColumn, byte[] inputBytes, String idColumn, Long id) throws SQLException {
  PreparedStatement pStmt = null;
  ResultSet rs = null;
  try {
    String sql = 
      " SELECT " + blobColumn + 
      " FROM " + table + 
      " WHERE " + idColumn + " = ? " +
      " FOR UPDATE";
    pStmt = con.prepareStatement(sql, 
      ResultSet.TYPE_FORWARD_ONLY, 
      ResultSet.CONCUR_UPDATABLE);
    pStmt.setLong(1, id);
    rs = pStmt.executeQuery();
    if (rs.next()) {
      Blob blob = rs.getBlob(blobColumn);
      blob.truncate(0);
      blob.setBytes(1, inputBytes);
      rs.updateBlob(blobColumn, blob);
      rs.updateRow();
    }
  }
  finally {
    if(rs != null) rs.close();
    if(pStmt != null) pStmt.close();
  }
}
 

Pour MSSQL, je comprends que la syntaxe de verrouillage est différente:

 String sql = 
  " SELECT " + blobColumn + 
  " FROM " + table + " WITH (rowlock, updlock) " + 
  " WHERE " + idColumn + " = ? "
 

7voto

Ivan Points 41

Un autre point de vue à partir d'Oracle DBA. Soleil gars ont fait du très mauvais travail quand ils ont conçu JDBC normes(1.0, 2.0, 3.0, 4.0). BLOB est caractérisée par de grandes objet et, par conséquent, il peut être très grande. C'est quelque chose qui ne peut pas être stocké dans le segment de la JVM. Oracle pense BLOBs comme quelque chose comme descripteurs de fichiers(il fait qu'ils sont appel, puis "lob locators"). LOBS ne peut pas être créé par le constructeur et ne sont pas des objets Java. Aussi LOB locators(oracle.sql.BLOB) ne peut pas être créé par constructeur - ils DOIVENT être créés dans la base de données secondaires. Dans Oracle il y a deux façons de créer un LOB.

  1. DBMS_LOB.CREATETEMPORATY - le retour de l'locator dans ce cas, des points dans l'espace de stockage temporaire. Toutes les écritures/lectures contre le localisateur sera envoyé via le réseau sur le serveur de base de données. Rien n'est stocké dans le segment de la JVM.

  2. Appel à EMPTY_BLOB fonction. INSERT INTO T1(NOM de FICHIER) VALUES("un.avi", EMPTY_BLOB()) RETOUR FICHIER DANS ?; Dans ce cas, retourné lob localisateur de points de données du tablespace. Toutes les écritures/lectures contre le localisateur sera envoyé via le réseau sur le serveur de base de données. Toutes les écritures sont "gardés" par écrit dans les redo logs. Rien n'est stocké dans le segment de la JVM. Le retour de la clause n'a pas été pris en charge par JDBC normes (1.0, 2.0), donc vous pouvez trouver de nombreux exemples sur internet où les gens proposer une démarche en deux étapes: "INSÉRER...; SELECT ... FOR UPDATE;"

Oracle lobs doit être associée à une certaine connexion de base de données, ils ne peuvent pas être utilisés DB de perte de connexion/fermé/(ou "engagés"). Ils ne peuvent pas être transmis à partir d'une connexion à un autre.

Deuxième exemple peut fonctionner, mais nécessitera une copie excessive si les données d'espace de stockage temporaire des données du tablespace.

5voto

Brian Points 7072

Une chose intéressante avec JDBC est que vous pouvez mettre à niveau plutôt agressive pour les pilotes les plus récents et de travailler avec JDBC 4.0 caractéristiques. Oracle JDBC drivers travaillera avec les anciennes versions de base de données, de sorte que vous pouvez utiliser un 11g de marque pilote JDBC à l'encontre d'une base de données 10g. La base de données Oracle 11g JDBC est disponible en deux versions: ojdbc5.jar pour Java 5 (c'est à dire, JDK 1.5) et ojdbc6.jar pour Java 6 (c'est à dire, JDK 1.6). L'ojdbc6.jar prend en charge la nouvelle JDBC 4.0 spécification.

Avec les pilotes les plus récents/jdbc 4.0, vous pouvez créer des Gouttes et Clobs hors de la connexion de l'objet:

Blob aBlob = con.createBlob();
int numWritten = aBlob.setBytes(1, val);

4voto

skaffman Points 197885

La gestion des LOB du serveur Oracle est assez médiocre et peut souffrir de graves problèmes de performances (par exemple, une surutilisation massive du journal de rétablissement), donc la première solution peut être un moyen de les résoudre.

Je suggère d'essayer les deux approches. si vous disposez d'un administrateur de base de données compétent, il pourra peut-être vous indiquer quelle approche a le plus faible impact sur le serveur.

4voto

Yinch Points 1

Cette déclaration :

 blob.setBytes(1, inputBytes);
 

donne des problèmes lorsque j'utilise le client léger Oracle ojdbc14.jar, "Fonctionnalités non prises en charge"

J'ai donc dû contourner:

 rset.next();
Blob bobj = rset.getBlob(1);
BLOB object = (BLOB) bobj;
int chunkSize = object.getChunkSize();
byte[] binaryBuffer = new byte[chunkSize];
int position = 1;
int bytesRead = 0;
int bytesWritten = 0, totbytesRead = 0, totbytesWritten = 0;
InputStream is = fileItem.getInputStream();
while ((bytesRead = is.read(binaryBuffer)) != -1) {
bytesWritten = object.putBytes(position, binaryBuffer, bytesRead);
position += bytesRead;
totbytesRead += bytesRead;
totbytesWritten += bytesWritten;
is.close();
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X