Comment rédiger / mettre à jour Oracle blob dans un moyen fiable?
Je suis en train d'écrire et de mettre à jour un document pdf dans une colonne blob mais je suis juste en mesure de mettre à jour le blob n'écrivant plus de données que les précédentes données stockées.
Si j'essaie de mettre à jour la colonne blob avec un petit document les données que je obtenir seulement un corrompu pdf.
D'abord la colonne blob a été initialisé à l'aide de empty_blob() fonction. J'ai écrit l'exemple de la classe Java ci-dessous pour tester ce comportement. Je le lance pour la première fois avec le "vrai" comme premier paramètre de la principal méthode dans la première ligne il y a stocké un document d'environ 31 ko et dans la deuxième ligne, il y a un document de 278ko.
Puis je le lance avec "faux" comme paramètre, de cette façon, les deux lignes doivent être mis à jour en échangeant les documents. Le résultat est que j'obtiens un résultat correct que lorsque j'écris plus de données que l'existant.
Comment est-il possible d'écrire une méthode qui écrit et met à jour une goutte de manière fiable sans worring sur des données binaires de taille?
import static org.apache.commons.io.IOUtils.copy;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import oracle.jdbc.OracleDriver;
import oracle.jdbc.OracleResultSet;
import oracle.sql.BLOB;
import org.apache.commons.lang.ArrayUtils;
/**
* Prerequisites:
* 1) a table named 'x' must exists [create table x (i number, j blob);]
* 2) that table should have two columns [insert into x (i, j) values (1, empty_blob()); insert into x (i, j) values (2, empty_blob()); commit;]
* 3) download lsp.pdf from http://www.objectmentor.com/resources/articles/lsp.pdf
* 4) download dotguide.pdf from http://www.graphviz.org/Documentation/dotguide.pdf
*/
public class UpdateBlob {
public static void main(String[] args) throws Exception {
processFiles(new String[]{"lsp.pdf", "dotguide.pdf"}, Boolean.valueOf(args[0]));
}
public static void processFiles(String [] fileNames, boolean forward) throws Exception {
if(!forward){
ArrayUtils.reverse(a);
}
int idx = 1;
for(String fname : fileNames){
insert(idx++, fname);
}
}
private static void insert(int idx, String fname) throws Exception{
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
DriverManager.registerDriver(new OracleDriver());
conn = DriverManager.getConnection("jdbc:oracle:thin:@"+db+":"+port+":"+sid, user, pwd);
ps = conn.prepareStatement("select j from x where i = ? for update");
ps.setLong(1, idx);
rs = ps.executeQuery();
if (rs.next()) {
FileInputStream instream = new FileInputStream(fname);
BLOB blob = ((OracleResultSet)rs).getBLOB(1);
OutputStream outstream = blob.setBinaryStream(1L);
copy(instream, outstream);
instream.close();
outstream.close();
}
rs.close();
ps.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
throw new Exception(e);
}
}
}
Oracle version: 11.1.0.7.0 - 64bit
J'ai même essayé de la norme API JDBC sans l'aide d'Oracle spécifique (comme dans l'exemple ci-dessus), sans succès.
copy
méthode définie? Mon intuition est que le copy
méthode n'est pas de faire ce que vous attendez et est remplacer uniquement les N premiers octets si les nouvelles données sont plus petites que les anciennes données.Btw: vous ne devez pas ouvrir une nouvelle connexion pour chaque appel de la fonction. Qui va être très lente.
copy
méthode est définie dans le org.apache.commons.io.IOUtils de classe. Il y a une statique à l'importation au début du code. Statique des importations rend moins lisible, mais je pense que dans certains cas, il est pratique à utiliser.Le code ci-dessus est juste un vraiment rapide POC. J'ai toujours utiliser un pool de connexion pour gérer la connexion db.
OriginalL'auteur alexyz78 | 2011-12-01
Vous devez vous connecter pour publier un commentaire.
Il est beaucoup plus facile:
Il fonctionne de la même manière lors de l'utilisation d'une instruction INSERT. Pas besoin de
empty_blob()
et une deuxième mise à jour de la déclaration.setBinaryStream
signature de l'est(int parameterIndex, InputStream x, int length)
).Que faire si c'est un objet java, et pas un fichier?
Hari: utiliser un ObjectInputStream ou un ByteArrayInputStream
Je vais avoir le même problème et tentent de mettre en œuvre cette solution. Que dois-je mettre la valeur de clé primaire pour? Est-il dépend de la base de données?
savoir ce que la valeur de clé primaire est. Cela pourrait être une valeur de séquence, ou d'un GUID ou quelque chose d'autre. Vous êtes le que celui qui peut répondre à cette question.
OriginalL'auteur a_horse_with_no_name
En plus de a_horse_with_no_name's réponse (qui s'appuie sur PreparedStatement.setBinaryStream(...) API), il y a au moins deux options pour les Gouttes, et 3 de plus pour CLOBs et NCLOBs:
Créer explicitement un LOB, écrire et utiliser
PreparedStatement.setBlob(int, Blob)
:Mise à jour d'un vide LOB (inséré par l'intermédiaire
DBMS_LOB.EMPTY_BLOB()
ouDBMS_LOB.EMPTY_CLOB()
) viaSELECT ... FOR UPDATE
. Cet Oracle est spécifique et nécessite deux instructions exécutées au lieu d'un. En outre, c'est ce que vous essayez d'accomplir en premier lieu:Pour CLOBs et NCLOBs, vous pouvez en outre utiliser
PreparedStatement.setString()
etsetNString()
, respectivement.OriginalL'auteur Bass
FWIW, pour quelque chose qui tient dans la mémoire, j'ai trouvé que je pouvais tout simplement passer dans un tableau d'octets que l'instruction préparée paramètre, plutôt que de passer par le "flux" de la rigueur de moral (ou pire Oracle spécifiques/a suggéré des choses)
À l'aide d'un Printemps "JDBC modèle" wrapper (org.springframework.jdbc.de base.JdbcTemplate) pour mettre le contenu d'un "grand" (ou pas) chaîne de caractères dans une colonne BLOB, le code est quelque chose comme ce qui suit:
Est pas l'étape 2.
OriginalL'auteur Roboprog