|
Oracle 8i Release 2 supports
-
Internal LOBs and
-
External LOBs.
LOB datatypes have the following characteristics:
-
Up to four (4) GB of data can be stored in LOB columns.
-
Multiple LOB columns of the same or different type are allowed in a
single database table.
-
LOBs support random piece-wise access to their data.
-
They can be broken into chunks to minimise network round trips.
-
Selecting a LOB column returns the LOB locator, not the LOB value.
One of the programmatic environments (see below) has to get used
to access the LOB value.
The following restrictions constrain the use of LOBs:
-
No support for distributed LOBs.
Remote LOB locators cannot be used in a SELECT or WHERE clause, including DBMS_LOB
package functions.
-
No support in clustered tables.
-
LOBs are not supported in the clauses GROUP BY, ORDER BY, SELECT DISTINCT.
-
No support in aggregations.
-
No support in JOIN conditions.
-
LOBs are not supported in partitioned, index-organised tables.
-
No support in VARRAYs.
-
No support in ANALYZE or ESTIMATE statements.
-
Restrictions apply in a trigger body (see [APPDEVLOB] for details).
Internal LOBs
Internal LOBs are stored within the database, either in-line in the table
or in a separate tablespace.
The supported datatypes are:
-
Binary LOB (BLOB)
Used to store binary (raw) data like formatted text, image, audio or video content.
-
Character LOB (CLOB)
Unformatted character data, using the predefined database character set.
-
National Character LOB (NCLOB)
Unformatted character data, using the predefined national database character set.
They are further divided into persistent and temporary LOBs. This document does only
concentrate on persistent BLOBs. Although most of the information applies to any type
of LOB.
Internal LOBs have the following characteristics:
-
LOBs smaller than four (4) KB are stored in-line in the table row.
By default, any LOB that is smaller than four (4) KB is stored in-line with the table
row. Once it grows bigger, it is automatically moved out of the table. This behaviour
enables fast access to small LOB columns.
If there are lots of accesses to the other (non-LOB) columns of the table,
particularly full table scans, this default behaviour should be turned off by
specifying DISABLE STORAGE IN ROW.
When inserting or updating a LOB with another LOB, the LOB value is copied. Each row
has its own, separate copy of the LOB value. There will be two different LOB locators
and two copies of the LOB value.
-
They are part of transactions.
Changes to a LOB can be committed or rolled back. In the event of a database failure,
they can get recovered. (This is just like the normal behaviour of all other database
objects).
External LOBs
External LOBs are stored in operating system files, outside of the database. The
supported datatypes are:
-
Binary File (BFILE)
Used to store binary (raw) data like formatted text, image, audio or video content
and character data.
External LOBs (BFILEs) have the following characteristics:
-
They are stored outside of the database, as operating system files.
The LOB locator is a pointer to the file.
-
They use reference semantics.
When inserting or updating a LOB with another LOB, only the LOB locator is copied,
not the actual LOB data. There will be two identical LOB locators and only one copy
of the LOB value (operating system file).
-
External LOBs allow read-only byte stream access to the operating system file.
The operating system of the database server must support stream-mode access to the
file. The files must be managed by the client application directly. I.e. before you
insert a reference to a file, it should exist in the file system. Trying to access a
BFILE that points to a non-existent operating system file will result in an
error.
-
They do not participate in transactions.
Integrity must be supported by the underlying file system and the client
application.
-
A single external LOB must reside on a single device.
E.g. striping across disk arrays is not supported.
-
Only a limited number of BFILEs can be open simultaneously per session.
The database initialisation parameter SESSION_MAX_OPEN_FILES defines the
upper limit (default value is 10).
-
The size of an external LOB is constrained to four (4) GB by the database (as for
internal LOBs), and further by the operating system maximum file size.
Programmatic Environments
Six (6) programmatic environments are supported to operate on LOBs. They can only work
on LOBs that have been initialised to either actual data or empty, not on NULL values.
The programmatic environments are:
-
PL/SQL (package DBMS_LOB),
-
C using OCI,
-
C/C++ using Pro*C,
-
COBOL using Pro*COBOL,
-
Visual Basic using Oracle Objects for OLE and
-
Java using JDBC.
Not every environment supports the full set of features. For a complete
list please see [APPDEVLOB].
In Java, for example, the JDBC API is used to work with LOBs. oracle.sql.BLOB for
internal binary LOBs and oracle.sql.BFILE for external LOBs. For a complete reference
please see [APPDEVLOB] and [JDBC].
It is highly recommended to create an abstraction layer on top of the APIs for internal
and external LOB manipulation. This will allow for switching between the two types
quite easily.
Internal Versus
External LOBs
This chapter compares internal LOBs to external LOBs. However, it does not try to judge
them, as the decision on whether to use internal or external LOBs heavily depends on
the application and its use of the LOB data.
|
|
|
|
|
|
The LOB can only be accessed via the database.
|
The LOB can be accessed via the database (read-only) and directly via the file
system.
|
|
|
Backed up together with the other database data. I.e. automatically consistent
backups.
|
Only the LOB locator is saved in the database backup. A separate backup of the
operating system files where the locators point to is required.
|
|
|
LOB data must get loaded into the database.
Database gets larger.
Write operations on LOBs generate REDO data.
|
The LOB data is accessed from the file system.
|
|
|
Depends heavily on database (buffer cache!!) and file system tuning, but is
roughly the same for both internal and external LOBs.
Piece wise/non-sequential access of internal LOBs are faster (chunks get indexed
automatically).
|
|
|
Recoverable through database recovery, as operations are part of a transaction
and get written to the REDO log.
|
Only possible with a combined file system and database recovery.
|
|
|
Database security applies.
|
Database and operating system security applies. This might lead to difficulties
on controlling the access to the LOB data.
|
|
|
The required amount of space is higher than the size of the actual data. This is
because the LOBs get indexed for random piece wise access automatically.
|
Equals the size of the actual data (if no special file system is being used).
|
|
|
Part of transaction. I.e. can be committed or rolled back and recovered.
|
Not part of transactions. Client application must ensure integrity.
|
|
|
Yes.
|
No. Files must get written and changed directly on the file system.
|
JDBC Code
Examples
This chapter shows code snippets of the most important methods on internal and external
BLOBs in Java. For more examples and a complete description of the API, please see
[APPDEVLOB].
The examples are part of the provided sample application (see below).
Internal BLOBs
As discussed above, internal BLOBs are stored entirely within the database. For storing
or retrieving them, it does not matter whether their data is stored in-line with the
table row or in another tablespace, this is transparent to the application programmer.
The following code snippet reads a file and stores it into the database:
// variables
String strStatement;
Statement stmt = null;
ResultSet resSet = null;
InputStream sampleFileStream = null;
OutputStream blobOutputStream = null;
try {
// create a statement (connection is already
existent)
stmt = dbConn.createStatement();
// get new id
strStatement = " SELECT lobsamp_seq.nextval"
+
" FROM dual";
resSet = stmt.executeQuery(strStatement);
if (resSet.next()) {
lngId = new Long(resSet.getLong(1));
}
// insert new row
// The LOB column value is initialized to empty in this step,
// and will be loaded in the steps below.
stmt.execute(" INSERT INTO lobsamp "
+ " (id"
+ "
,blob_col"
+ " ) VALUES
"
+ " (" +
lngId
+ "
,empty_blob()"
+ " )");
// Retrieve BLOB locator
strStatement = " SELECT blob_col"
+
" FROM lobsamp"
+
" WHERE id = " + lngId
+
" FOR UPDATE";
resSet = stmt.executeQuery(strStatement);
if (resSet.next()) {
// Get the BLOB locator and open output
stream for the BLOB
BLOB bCol = ((OracleResultSet)resSet).getBLOB(1);
blobOutputStream = bCol.getBinaryOutputStream();
// Open the sample file as a stream for
insertion
// into the BLOB column
File file2Load = new File(strDirectory + strFile);
sampleFileStream = new FileInputStream(file2Load);
// Buffer to hold chunks of data to being
written to the BLOB.
byte[] bBuffer = new byte[bCol.getBufferSize()*NUMCHUNKS];
// Read a chunk of data from the sample file
input stream,
// and write the chunk to the BLOB column output stream.
// Repeat till file has been fully read.
int intBytesRead = 0;
// read from file until done
while ((intBytesRead = sampleFileStream.read(bBuffer)) != -1) {
// write to BLOB
blobOutputStream.write(bBuffer,0,intBytesRead);
}
// closing the streams and
committing
sampleFileStream.close();
blobOutputStream.close();
dbConn.commit();
}
} catch (Exception ex) {
try { dbConn.rollback(); } catch (Exception e) {}
} finally {
try { sampleFileStream.close(); } catch (Exception e) {}
try { blobOutputStream.close(); } catch (Exception e) {}
try { stmt.close(); } catch (Exception e) {}
}
The code below reads an internal BLOB from the database and stores it into
an operating system file:
// variables
String strStatement = null;
Statement stmt = null;
ResultSet resSet = null;
BLOB bCol = null;
String strFileName = null;
String strDirectory = null;
InputStream blobInputStream = null;
OutputStream sampleFileStream = null;
try {
// select the blob locator from the database
strStatement = " SELECT name"
+
" , blob_col"
+
" FROM lobsamp"
+
" WHERE id = " + lngId;
stmt = dbConn.createStatement();
resSet = stmt.executeQuery(strStatement);
if (resSet.next()) {
strFileName = resSet.getString("name");
// get the locator
bCol = ((OracleResultSet)resSet).getBLOB("blob_col");
}
// Save as dialog box to get directory and file name to
save as
FileDialog selFile = new FileDialog(mainFrame,
"Save as", FileDialog.SAVE);
selFile.setFile(strFileName);
selFile.show();
strDirectory = selFile.getDirectory();
strFileName = selFile.getFile();
// get the input stream
blobInputStream = bLocator.getBinaryStream();
// Open the file to write to
File file2Save = new File(strDirectory + strFile);
sampleFileStream = new FileOutputStream(file2Save);
// get the buffer size to use
int intBufferSize = bLocator.getBufferSize()*NUMCHUNKS;
// Buffer to hold chunks of data to read from the
BLOB.
byte[] bBuffer = new byte[intBufferSize];
// Read a chunk of data from the BLOB, and
// write the chunk to the file.
int intBytesRead = 0;
while ((intBytesRead = blobInputStream.read(bBuffer)) != -1) {
sampleFileStream.write(bBuffer,0,intBytesRead);
}
} catch (Exception ex) {
System.err.println("Saving LOB data: " + ex.toString());
} finally {
// close input stream, output stream and
statement
if (sampleFileStream != null) {
try { sampleFileStream.close(); } catch (Exception e) {
System.err.println("Could not close file output
stream!"); }
}
if (blobInputStream != null) {
try { blobInputStream.close(); } catch (Exception e) {
System.err.println("Could not close blob input
stream!"); }
}
try { stmt.close(); } catch (Exception e) {
System.err.println("Could not close the statement!"); }
}
External BLOBs (BFILEs)
External BLOBs do only store a reference to the operating system file within the
database. The reference is done using a DIRECTORY database object and a file name.
The application programmer must ensure that the file exists and that Oracle processes
have operating system read permissions on the file (although this is only checked when
trying to access the file, not on insertion of a BFILE object).
A directory object specifies an alias for a directory on the server’s file system
where external binary file LOBs (BFILEs) are located.
All directories are created in a single namespace and are not owned by an individual
schema. You can secure access to the BFILEs stored within the directory structure by
granting object privileges on the directories to specific users. When you create a
directory, you are automatically granted the READ object privilege and can grant READ
privileges to other users and roles.
You must have CREATE ANY DIRECTORY system privileges to create directories.
Example:
CREATE OR REPLACE DIRECTORY bfile_dir AS ’/data1/LOB/files’;
The following code snippet stores a reference to an operating system file that is
located within the directory BFILE_DIR into the database:
// variables
Statement stmt = null;
ResultSet resSet = null;
try {
// create a statement
stmt = dbConn.createStatement();
// insert new row
stmt.execute(" INSERT INTO lobsamp "
+ " (id"
+ "
,name"
+ "
,bfile_col"
+ " )"
+ " SELECT
lobsamp_seq.nextval"
+ "
, '" + strFile + "'"
+ "
, bfilename('BFILE_DIR','" + strFile + "')"
+ "
FROM dual");
} catch (Exception ex) {
System.err.println("Error loading BFILE data: " + ex.toString());
try { dbConn.rollback(); } catch (Exception e) {}
} finally {
// close the statement
try { stmt.close(); } catch (Exception e) {}
}
The code below reads an external BLOB (BFILE) via the database and stores it into
another operating system file:
// variables
String strStatement = null;
Statement stmt = null;
ResultSet resSet = null;
BFILE bFile = null;
String strFileName = null;
String strDirectory = null;
InputStream bfileInputStream = null;
OutputStream sampleFileStream = null;
try {
// select the bfile locator from the database
strStatement = " SELECT name"
+
" , bfile_col"
+
" FROM lobsamp"
+
" WHERE id = " + lngId;
stmt = dbConn.createStatement();
resSet = stmt.executeQuery(strStatement);
if (resSet.next()) {
strFileName = resSet.getString("name");
// get the locator
bFile = ((OracleResultSet)resSet).getBFILE("bfile_col");
}
// Save as dialog box to get directory and file name to
save as
FileDialog selFile = new FileDialog(mainFrame,
"Save as", FileDialog.SAVE);
selFile.setFile(strFileName);
selFile.show();
strDirectory = selFile.getDirectory();
strFileName = selFile.getFile();
// show directory alias
System.out.println("... directory alias: "
+ bLocator.getDirAlias() + " ...");
// show file name
System.out.println("... file name: "
+ bLocator.getName() + " ...");
// check if file exists
System.out.println("... does the file exist: "
+ bLocator.fileExists() + " ...");
// show file length
System.out.println("... file length = "
+ bLocator.length() + " ...");
// open the file and get the stream
bLocator.openFile();
bfileInputStream = bLocator.getBinaryStream();
// open the file to write to
File file2Save = new File(strDirectory + strFile);
sampleFileStream = new FileOutputStream(file2Save);
// define the buffer size
int intBufferSize = BFILEBUFSIZE*NUMCHUNKS;
// buffer to hold chunks of data to read from the
BFILE
byte[] bBuffer = new byte[intBufferSize];
// read a chunk of data from the BFILE, and write
// the chunk to the file.
int intBytesRead = 0;
while ((intBytesRead = bfileInputStream.read(bBuffer)) != -1) {
sampleFileStream.write(bBuffer,0,intBytesRead);
}
} catch (Exception ex) {
System.err.println("Saving BFILE data: " + ex.toString());
} finally {
// close input stream, output stream, bfile and
statement
if (sampleFileStream != null) {
try { sampleFileStream.close(); } catch (Exception e) {
System.err.println("Could not close file output
stream!"); }
}
if (bfileInputStream != null) {
try { bfileInputStream.close(); } catch (Exception e) {
System.err.println("Could not close bfile input
stream!"); }
}
try { bLocator.closeFile(); } catch (Exception e) {
System.err.println("Could not close bfile file!"); }
try { stmt.close(); } catch (Exception e) {
System.err.println("Could not close the statement!"); }
}
|
APPDEVLOB
|
Application Developer's Guide - Large Objects, Release 2 (8.1.6), December 1999,
Oracle Corporation
|
|
SQLREF
|
Oracle 8i, SQL Reference, Release 8.1.6, December 1999, Oracle Corporation
|
|
METALINK
|
MetaLink, Oracle Support Services, http://metalink.oracle.com/
|
|
ORAWEB
|
Oracle Corporation Internet Portal, http://www.oracle.com/
|
|
TECHNET
|
Oracle Technology Network, http://technet.oracle.com/
|
|