Objekte in Oracle8 speichern?

Hallo!

Ich moechte gerne Java-Objekte in Oracle8 persistent machen, aber nicht auf die Weise, dass einfach die Attribute in einer Tabelle
gespeichert werden, sondern das ganze Objekt als eine Art BLOB.

Wie funktioniert so etwas? Ich bin dankbar fuer Links zu diesem Thema und Code-Beispiele in Java / SQL.

Gruesse, Tanja

Hallo Tanja,

Ich moechte gerne Java-Objekte in Oracle8 persistent machen,
aber nicht auf die Weise, dass einfach die Attribute in einer
Tabelle
gespeichert werden, sondern das ganze Objekt als eine Art
BLOB.

ungeachtet der Frage, ob dies besonders sinnvoll sei: um mit BLOBS zu arbeiten, benutzt Du die Package DBMS_LOB. Hier ist eine Anleitung dazu:
http://oradoc.photo.net/ora816/appdev.816/a76940/adl…

Und hier steht was allgemein zu LOBs:
http://oradoc.photo.net/ora816/appdev.816/a76940/toc…

Gruß

J.

Zur Ergänzung noch ein Link wie du ein Java-Objekt serialisierst und damit speichern kannst:

http://java.sun.com/products/jdk/1.1/docs/guide/seri…

Grüße, Robert

Hallo Robert!

Zur Ergänzung noch ein Link wie du ein Java-Objekt
serialisierst und damit speichern kannst:

Danke, das funktioniert jetzt soweit. Ich habe jetzt eine kleine Java-Anwendung geschrieben, die ein Objekt in eine Datei speichert und wieder auslesen kann.

Mein Problem ist jetzt, daß ich nicht weiß, wie ich die Oracle-DB mit Java ansprechen kann. Ich habe folgende Angaben: Datenbankname, Port, Rechnername, Username und Passwort.

// Load the Oracle JDBC driver: 
Class.forName ("oracle.jdbc.driver.OracleDriver");

// Connect to the database: 
Connection conn =
DriverManager.getConnection ("jdbc:oracle:oci8:Rechnername:stuck\_out\_tongue:ort", "Username", "Passwort");

Stimmt das so? Wo muß ich den Datenbanknamen angeben? Was muß ich sonst noch beachten? Ich habe bis jetzt noch nie mit Oracle, sondern immer nur mit Java und Access gearbeitet.

Im Oracle Technet habe ich zwei gute Code-Beispiel gefunden (siehe unten). Irgendwie sind mir aber noch einige Sachen unklar. In der Klasse Ex2_66 wird doch nur der String „Some Text To Write“ als CLOB gespeichert, und keine Datei, oder? Wie müßte es aussehen, wenn ich eine Datei speichern will?

Grüße, Tanja

-----------------------------------------------
// Example: Write Data to a LOB Using Java (JDBC)

// Java IO classes: 
import java.io.InputStream;
import java.io.OutputStream;

// Core JDBC classes: 
import java.sql.DriverManager;
import java.sql.Connection;
import java.sql.Statement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

// Oracle Specific JDBC classes: 
import oracle.sql.\*;
import oracle.jdbc.driver.\*;

public class Ex2\_66
{
 static final int MAXBUFSIZE = 32767;

 public static void main (String args [])
 throws Exception
 {
 // Load the Oracle JDBC driver: 
 Class.forName ("oracle.jdbc.driver.OracleDriver");

 // Connect to the database: 
 Connection conn =
 DriverManager.getConnection ("jdbc:oracle:oci8:@", "samp", "samp");

 // It's faster when auto commit is off: 
 conn.setAutoCommit (false);

 // Create a Statement: 
 Statement stmt = conn.createStatement ();

 try
 {
 CLOB lob\_loc = null;
 String buf = new String ("Some Text To Write");

 ResultSet rset = stmt.executeQuery (
 "SELECT intab.transcript FROM TABLE(
 SELECT mtab.inseg\_ntab FROM multimedia\_tab mtab 
 WHERE mtab.clip\_id = 1) intab WHERE intab.segment=1 FOR UPDATE");

 if (rset.next())
 {
 lob\_loc = ((OracleResultSet)rset).getCLOB (1);
 }

 OracleCallableStatement cstmt = (OracleCallableStatement) 
 conn.prepareCall ("BEGIN DBMS\_LOB.OPEN( ?,
 DBMS\_LOB.LOB\_READWRITE); END;");
 cstmt.setCLOB(1, lob\_loc);
 cstmt.execute();

 long pos = 0; // This is the offset within the CLOB where the data is to be written
 long length = 0; // This is the size of the buffer to be written.

 // This loop writes the buffer three times consecutively: 
 for (int i = 0; i 

Hallo Jose!

Danke für Deine Antwort, ich habe im Oracle Technet ausführliche Infos darüber gefunden.

ungeachtet der Frage, ob dies besonders sinnvoll sei: um mit
BLOBS zu arbeiten, benutzt Du die Package DBMS_LOB.

Das Ganze soll ein kleiner Test für die Vorlesung „Datenbanken in Netzen“ werden, die ich gerade besuche. Kannst Du mir vielleicht ein paar Vor- und Nachteile für die beiden Ansätze nennen?

Kennst Du Anwendungen, bei denen Objekte tatsächlich als BLOBS gespeichert werden? Ehrlich gesagt, sehe ich momentan auch keinen großen Nutzen darin, meine Objekte als BLOBS anstatt die Attribute ganz normal in Tabellen zu speichern. Ich habe durch die notwendige Serialisierung der Objekte eigentlich doch nur mehr Aufwand.

Wie lege ich jetzt eine Tabelle an, in der ich ein serialisiertes Objekt speicher kann?

CREATE TABLE Person ( 
 Person\_ID NUMBER NOT NULL, 
 Person\_Objekt BLOB default EMPTY\_BLOB()
) 

Stimmt das so?

Grüße, Tanja

Das Ganze soll ein kleiner Test für die Vorlesung „Datenbanken
in Netzen“ werden, die ich gerade besuche. Kannst Du mir
vielleicht ein paar Vor- und Nachteile für die beiden Ansätze
nennen?

Vorteile ohne Blob:
Du kannst auf Attribute abfragen, also so etwas wie:
– Dicke auswählen:
select * from personen where gewicht > 120 ;
– Große auswählen:
select * from personen where groesse > 1.95 ;

usw. Beim Blob mußt Du jeweils das ganze Objekt raussuchen und kannst dann erst die Überprüfungen vornehmen. Das dürfte TIERISCH langsam sein.

Nachteile fallen mir keine ein…

Kennst Du Anwendungen, bei denen Objekte tatsächlich als BLOBS
gespeichert werden? Ehrlich gesagt, sehe ich momentan auch
keinen großen Nutzen darin, meine Objekte als BLOBS anstatt
die Attribute ganz normal in Tabellen zu speichern. Ich habe
durch die notwendige Serialisierung der Objekte eigentlich
doch nur mehr Aufwand.

Eben. Blobs sind laut Oracle da, um „nichtstrukturierte Datenmengen“ abzulegen, das sind z.B. Bilder, Audiodaten, sowas. Dort gibt es keinen Grund, sie nicht als Blob abzulegen, denn diese Daten machen nur gemeinsam Sinn.

Wie lege ich jetzt eine Tabelle an, in der ich ein
serialisiertes Objekt speicher kann?

CREATE TABLE Person (
Person_ID NUMBER NOT NULL,
Person_Objekt BLOB default EMPTY_BLOB()
)

Stimmt das so?

Ja, ich würde Person_ID als Primary Key definieren und eine Sequence dafür anlegen (+Trigger):

create sequence pers\_id\_seq;
create or replace trigger pers\_id\_autowert before insert on personen for each row
begin
 select pers\_id\_seq.nextval into :new.pers\_id from dual;
end;

Gruß

J.

Hallo Jose!

Ja, ich würde Person_ID als Primary Key definieren und eine
Sequence dafür anlegen (+Trigger):

create sequence pers_id_seq;
create or replace trigger pers_id_autowert before insert on
personen for each row
begin
select pers_id_seq.nextval into :new.pers_id from dual;
end;

OK, danke. Was ist eine Sequenz und warum ist das in diesem Fall sinnvoll? Sorry, meine SQL-Kenntnisse sind leider etwas bescheiden.

Außerdem überlege ich schon die ganze Zeit, wie ich das INSERT-Statement formulieren muß, das schließlich die Datei in der DB speichert und wie ich es nachher wieder herausbekomme. Hast Du da vielleicht auch noch einen Tip? Im Oracle Technet konnte ich bis jetzt noch kein Beispiel finden.

Grüße, Tanja

OK, danke. Was ist eine Sequenz und warum ist das in diesem
Fall sinnvoll? Sorry, meine SQL-Kenntnisse sind leider etwas
bescheiden.

Eine Sequence garantiert immer eindeutige nummern; sie wird bei jedem Select um einen eingestellten Wert inkrementiert (defaultmäßig 1). Damit realisierst Du einen Autowert, den es unter Oracle nicht gibt. In diesem Fall finde ich das sinnvoll, da es Dir die Arbeit abnimmt.

Außerdem überlege ich schon die ganze Zeit, wie ich das
INSERT-Statement formulieren muß, das schließlich die Datei in
der DB speichert und wie ich es nachher wieder herausbekomme.
Hast Du da vielleicht auch noch einen Tip? Im Oracle Technet
konnte ich bis jetzt noch kein Beispiel finden.

Unter
http://oradoc.photo.net/ora816/appdev.816/a76940/adl… werden dei Java-Methoden aufgeführt (Package oracle.sql.BLOB). Das ist dann recht simpel. Hier sind auch ein paar Beispiele aufgeführt:
http://www.jguru.com/jguru/faq/printablefaq.jsp?faq=…
Beim OTN gibt es auch Beispiele:
http://technet.oracle.com/sample_code/tech/java/sqlj…

Gruß und viel Spaß!

J.

Stimmt das so? Wo muß ich den Datenbanknamen angeben? Was muß
ich sonst noch beachten? Ich habe bis jetzt noch nie mit
Oracle, sondern immer nur mit Java und Access gearbeitet.

Hmmm, ich hab bis jetzt immer den thin-JDBC-Driver verwendet (der kommuniziert direkt über Sockets mit der Oracle-DB, ohne OCI). Bei OCI muß man AFAIK die Datenbank-Verknüpfung lokal mit dem Oracle-Client einrichten und dann ähnlich wie bei ODBC einen Namen dafür vergeben. Ansprechen tust es dann nur noch mit „jdbc:oracle:oci8:@lokaleeingerichtetername“. Wie das mit dem OCI funkt wird dir hier wahrscheinlich jemand anderer besser erklären können, hab leider wenig Ahnung von dem ganzen Oracle-Zeugs. :o)

Im Oracle Technet habe ich zwei gute Code-Beispiel gefunden
(siehe unten). Irgendwie sind mir aber noch einige Sachen
unklar. In der Klasse Ex2_66 wird doch nur der String „Some
Text To Write“ als CLOB gespeichert, und keine Datei, oder?
Wie müßte es aussehen, wenn ich eine Datei speichern will?

Auch hier alle Aussagen mit Vorsicht genießen, aber so wie es aussieht, will diese CLOB-Klasse einfach nur mit dem lob_loc.plsql_write(pos, buf.toCharArray()); ein CharArray, und das kannst sicher auch aus einem InputStream generieren. Oder gleich dahin serialiseren.

Grüße, Robert

Hallo Robert!

Danke für Deine Antwort!

Hmmm, ich hab bis jetzt immer den thin-JDBC-Driver verwendet
(der kommuniziert direkt über Sockets mit der Oracle-DB, ohne
OCI). Bei OCI muß man AFAIK die Datenbank-Verknüpfung lokal
mit dem Oracle-Client einrichten und dann ähnlich wie bei ODBC
einen Namen dafür vergeben.

Genau, aber der DB-Admin hat mir gesagt, daß man dafür auf dem Client ein Tool benötigt, das Net8 unterstützt. Leider sind solche Tools im Oracle Technet nicht zu finden, da sie kostenpflichtig sind.

Deshalb wollte ich auch die Thin-JDBC-Driver verwenden, allerdings frage ich mich, ob ich damit überhaupt mit LOBS arbeiten kann. „For example, the JDBC specification doesn’t support LOB data, but the Oracle OCI8 JDBC driver does.“ (Quelle: http://www.oracle.com/oramag/webcolumns/tbrief.html) Heißt das jetzt, das man für LOBS unbedingt mit OCI arbeiten muß, oder geht das auch mit den Thin-JDBC-Drivern?

Momentan schaffe ich es nicht mal, irgendwie auf Oracle zuzugreifen und mal eine DB und eine Tabelle anzulegen! :frowning:

import oracle.sql.\*;
import oracle.jdbc.driver.\*;

Class.forName ("oracle.jdbc.driver.OracleDriver");

Connection conn = DriverManager.getConnection ("jdbc:oracle://141.28.XXX.XXX:1521/Datenbankname", "user", "pass"); 

Der Compiler sagt:

DBTest.java:17: Package oracle.sql not found in import.
import oracle.sql.\*;
DBTest.java:18: Package oracle.jdbc.driver not found in import.
import oracle.jdbc.driver.\*;

Anscheinend muß ich jetzt auf meinem Rechner irgendwelche JDBC-Treiber installieren, aber wie mache ich das, wo kriege ich die her und welche brauche ich da eigentlich genau?

Grüße, Tanja

Deshalb wollte ich auch die Thin-JDBC-Driver verwenden,
allerdings frage ich mich, ob ich damit überhaupt mit LOBS
arbeiten kann. „For example, the JDBC specification doesn’t
support LOB data, but the Oracle OCI8 JDBC driver does.“
(Quelle: http://www.oracle.com/oramag/webcolumns/tbrief.html)
Heißt das jetzt, das man für LOBS unbedingt mit OCI arbeiten
muß, oder geht das auch mit den Thin-JDBC-Drivern?

Das geht vermutlich auch mit den Thin-Drivern. Es ist so, es gibt die Standard-JDBC-Interfaces, die werden auch alle von den Oracle-Treibern implementiert, aber es gibt dann noch für fast alle Klassen proprietäre Oracle-Klassen, mit denen man an bestimmte Features kommt die man über die normalen JDBC-Klassen nicht kommt. Die werden aber sowohl über das Thin als auch über das OCI unterstützt.

Anscheinend muß ich jetzt auf meinem Rechner irgendwelche
JDBC-Treiber installieren, aber wie mache ich das, wo kriege
ich die her und welche brauche ich da eigentlich genau?

Job, da brauchst die Treiber-Klassen. Die gibts im Technet kostenlos zum runterladen. Die mußt du dann einfach in den CLASSPATH stellen, wie mit allen andren Klassen auch (d. h. entweder entpackt in ein Verzeichnis im CLASSPATH oder direkt als .jar-File in den CLASSPATH schreiben).

Grüße, Robert

Hmmm, ich hab bis jetzt immer den thin-JDBC-Driver verwendet
(der kommuniziert direkt über Sockets mit der Oracle-DB, ohne
OCI). Bei OCI muß man AFAIK die Datenbank-Verknüpfung lokal
mit dem Oracle-Client einrichten und dann ähnlich wie bei ODBC
einen Namen dafür vergeben.

Genau, aber der DB-Admin hat mir gesagt, daß man dafür auf dem
Client ein Tool benötigt, das Net8 unterstützt. Leider sind
solche Tools im Oracle Technet nicht zu finden, da sie
kostenpflichtig sind.

Was meint er für ein Tool? Wenn Du den Client hast (hast Du ihn? Kannst Du Dich über SQLPlus verbinden?), dann ist die Einrichtung von Net8 recht simpel; mail mich bei Bedarf an.
Den Client müßtest Du haben, denn er wird ja mit dem Server ausgeliefert.

Deshalb wollte ich auch die Thin-JDBC-Driver verwenden,
allerdings frage ich mich, ob ich damit überhaupt mit LOBS
arbeiten kann. „For example, the JDBC specification doesn’t
support LOB data, but the Oracle OCI8 JDBC driver does.“
(Quelle: http://www.oracle.com/oramag/webcolumns/tbrief.html)
Heißt das jetzt, das man für LOBS unbedingt mit OCI arbeiten
muß, oder geht das auch mit den Thin-JDBC-Drivern?

Werde ich mal austesten, ich habe beide (arbeite aber auch nur mit dem thin-Client).

Anscheinend muß ich jetzt auf meinem Rechner irgendwelche
JDBC-Treiber installieren, aber wie mache ich das, wo kriege
ich die her und welche brauche ich da eigentlich genau?

Die gibt es, wie Robert sagt, auf Oracles Website (classes111.zip). Achte darauf, daß Du die richtige Version runterlädst, die sind je nach Serverversion unterschiedlich und miteinander nicht kompatibel).

Gruß

J.

Hallo Robert!

Jetzt muß ich Dich doch noch mal mit meinen Blobs nerven! :wink:

Das Speichern der serialisieren Objekt klappt mit setBinaryStream() schon einwandfrei, allerdings gibt es beim Auslesen noch ein kleines Problem.

Das serialisierte Objekt PersonObject.ser wird als
Blob in die DB gespeichert und beim Auslesen aus der DB schreibe ich es in meinem kleinen Test in die Datei
PersonObjectDB.ser. Ich habe dann das File PersonObjectDB.ser und PersonObject.ser miteinander verglichen und dabei festgestellt, daß bei PersonObjectDB.ser immer die Hälfte fehlt!!!

-> http://www.fh-furtwangen.de/~schaett/PersonObjectDB.gif
-> http://www.fh-furtwangen.de/~schaett/PersonObject.gif

Woran könnte das liegen?

// Read blob from database
 try
 { 
 // Write Object to a new file
 String fileName="PersonObjectDB.ser";
 Statement stmt = null;
 ResultSet rset = null;

 stmt = conn.createStatement();
 rset = stmt.executeQuery("SELECT \* FROM blobtest27"); 


 if (rset.next())
 { 
 FileOutputStream file = new FileOutputStream(fileName); 
 System.out.println("Blob wird in " + fileName + " geschrieben.");

 System.out.println("Blob aus DB holen.");
 InputStream fin = rset.getBinaryStream(1);

 int c;
 while ((c = fin.read()) != 1)
 file.write(c);
 file.close();
 }

 rset.close();
 stmt.close(); 
 }

 catch (SQLException e)
 {
 System.out.println("Fehler beim Auslesen des Blobs aus der DB!");
 e.printStackTrace();
 }

Grüße, Tanja

Hallo!

Schätzomativ passiert das beim Schreiben ins File, dort hast du nämlich folgende Abbruchbedingung:

while ((c = fin.read()) != 1)
file.write©;

D. h., er bricht ab wenn er einen Character mit dem Wert 01 einliest und wenn du dir die beiden Hex-Dumps ansiehst, genau das ist das erste Zeichen das in der zweiten Date fehlt.

Ich denke du solltest da einfach über available() prüfen ob noch was am Stream liegt. :smile:

Grüße, Robert

Hallo Robert!

Schätzomativ passiert das beim Schreiben ins File, dort hast
du nämlich folgende Abbruchbedingung:

while ((c = fin.read()) != 1)
file.write©;

D. h., er bricht ab wenn er einen Character mit dem Wert 01
einliest und wenn du dir die beiden Hex-Dumps ansiehst, genau
das ist das erste Zeichen das in der zweiten Date fehlt.

Aha! Vielen Dank für diesen Hinweis, da wäre ich mal wieder nicht so schnell drauf gekommen!

Ich denke du solltest da einfach über available() prüfen ob
noch was am Stream liegt. :smile:

Ich versuche das jetzt schon seit einer ganzen Weile, kriege es aber einfach nicht hin.

 while (fin.available() != 1)
 file.write(fin.read());
 file.close();

Hiermit funktioniert es soweit, allerdings fehlt jetzt in PersonObjectDB.ser immer der letzte Buchstabe, also das „a“ und die 61 im Hex-Code.

Wenn ich != 0 schreibe wird PersonObjectDB.ser bis zu 18 MB groß (immer unterschiedlich). Wie mache ich das mit available() richtig?

Grüße, Tanja

Hiermit funktioniert es soweit, allerdings fehlt jetzt in
PersonObjectDB.ser immer der letzte Buchstabe, also das „a“
und die 61 im Hex-Code.

available() gibt die Anzahl der noch übrigen Bytes zurück, in dem Fall bricht er dann ab wenn noch ein Byte übrige ist, also läßt er das letzte Zeichen weg.

Machs einfach so:
while( in.available() > 0 )

Grüße, Robert

Hallo Robert!

Danke für Deine Antwort.

available() gibt die Anzahl der noch übrigen Bytes zurück, in
dem Fall bricht er dann ab wenn noch ein Byte übrige ist, also
läßt er das letzte Zeichen weg.

Genau.

Machs einfach so:
while( in.available() > 0 )

Das habe ich auch schon ausprobiert, aber es passiert dann das gleiche oder etwas ähnliches wie wenn ich != 0 schreibe. Es entsteht ein PersonObjectDB.ser-File mit seltsamen Zeichen (alle sind gleich). Ich mache die Files jetzt lieber nicht mehr auf, da mir dadurch heute nachmittag schon einige Male mein Rechner abgeschmiert ist. Die Files werden auf jeden Fall immer sehr groß, bis zu 100 MB nach einigen Minuten! Sie wachsen so lange weiter, bis ich javaw.exe abschieße.

Also kann da irgendetwas nicht stimmen, da PersonObjectDB.ser eigentlich nur 1 KB groß werden sollte, aber ich habe immer noch nicht herausgefunden, an was es liegt…

Grüße, Tanja

Lösung!

Hallo Robert!

int c;
while ((c = fin.read()) != -1)
 file.write(c);

Es muß -1 anstatt 1 heißen! So kommt es halt, wenn man zu blöde zum Lesen der Doku ist!

read() 
returns the next byte of data, or -1 if the end of the stream is
reached.

Da hier „unten“ wahrscheinlich außer uns zweien sowieso niemand mehr mitliest, habe ich die Frage gestern abend auch mal im Java-Brett gestellt. Der Antworter meinte, daß available() hier nicht geeignet sei. Trotzdem noch mal Danke für Deine Hilfe.

Grüße, Tanja