Inserting blob data in Java using PreparedStatement - java

I am using the following code to insert an image in a database. It will store two images because I have used PreparedStatement and Statement.
When I run this code, I get two images in the database. But the two images are different, and I don't understand why. Using PreparedStatement, it is inserting perfectly. I want to have the same image when I use Statement. Why is it not working now, and how can I make it work?
import java.io.*;
import java.sql.*;
public class Image
{
public static void main(String args[]) throws Exception
{
System.out.println("kshitij");
Class.forName("com.mysql.jdbc.Driver");
Connection cn=DriverManager.getConnection("jdbc:mysql://localhost:3306/jsfdb","root","kshitij");
Statement st=cn.createStatement();
File f1=new File("c:\\k1.jpg");
FileInputStream fin=new FileInputStream(f1);
//DataInputStream dataIs = new DataInputStream(new FileInputStream(f1));
PreparedStatement pst = cn.prepareStatement("insert into registration(image) values(?)");
//pst.setInt(1,67);
pst.setBinaryStream(1,fin,fin.available());
pst.executeUpdate();
//int length=(int)f1.length();
byte [] b1=new byte[(int)f1.length()];
fin.read(b1);
fin.close();
st.executeUpdate("insert into registration(image) values('"+b1+"')");
System.out.println("Quesry Executed Successfully");
FileOutputStream fout=new FileOutputStream("d://k1.jpg");
fout.write(b1);
fout.close();
}
}
MySQL
CREATE DATABASE IF NOT EXISTS jsfdb;
USE jsfdb;
-- Definition of table `registration`
DROP TABLE IF EXISTS `registration`;
CREATE TABLE `registration` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`image` blob NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=234 DEFAULT CHARSET=latin1;

Of course they will be different. The following query does the following thing:
"insert into registration(image) values('"+b1+"')"
Take b1, which is a byte array, and call its toString() method. This results in a String like [B#8976876, which means "an object of type byte array with hashCode 8976876", but doesn't represent the contents of the byte array at all. Then insert this string in the table.
A byte array is not a String. End of story. You must use a prepared statement to insert binary data in a table. In fact, you should always use a prepared statement to execute any query that has a non-constant parameter.

Use setBlob with InputStream
File file= new File("your_path");
FileInputStream inputStream= new FileInputStream(file);
PreparedStatement statement = connection.prepareStatement("INSERT INTO yourTable (yourBlob) VALUES (?)");
statement.setBlob(1, inputStream);

Your problem is that you are concatenating a string with a byte array (in your call to executeStatment)
See this answer on how to insert a blob using a statement:
https://stackoverflow.com/a/2609614/355499

Related

Issue inserting Oracle Blob content into another record using Java

I am working on proof of concept to read a blob content from Oracle, manipulate it and then insert back as new record using Java. Currently, I am trying to just read and then write back blob content to Oracle but facing issues. I am able to write back but looks like the file is not getting inserted completely.
Error when trying to view/download Blob via SQL developer
Code used to read and write back
conn.setAutoCommit(false);
stmt = conn.createStatement();
sql = "SELECT DOC_ID, NAME, BLOB_CONTENT FROM DOCUMENTS WHERE DOC_ID = " + String.valueOf(docid);
ResultSet rs_docs = stmt.executeQuery(sql);
while (rs_docs.next()) {
Show_Message("Conversion sub process started ...");
doc_name = rs_docs.getString("name");
Blob ib = rs_docs.getBlob("blob_content");
Show_Message("Uploading converted pdf to database ... ");
InputStream input = ib.getBinaryStream();
String filename = doc_name;
CallableStatement callableStatement = conn.prepareCall("begin INSERT INTO DOCUMENTS(NAME, BLOB_CONTENT) VALUES(?,?) RETURNING DOC_ID INTO ?; end;");
callableStatement.setString(1, filename);
callableStatement.setBinaryStream(2, input, input.available());
callableStatement.registerOutParameter(3, Types.INTEGER);
callableStatement.executeQuery();
docid = callableStatement.getInt(3);
callableStatement.close();
Show_Message("New record created, doc # " + String.valueOf(docid));
Show_Message("Conversion Process completed!!!");
}
stmt.close();
conn.commit();
conn.close();
rs_docs.close();
Connection.prepareCall() is for creating a Statement that calls a stored procedure. If you want to do that then you should define an SP in the database, outside the scope of this method, and call it by name via your [Callable]Statement. But if the only point is to get the DOC_ID assigned to the new row, then there are other ways, such as Statement.getGeneratedKeys().
Do not use InputStream.available() to determine the size of the blob. "Available" means the number of bytes readable without blocking, right now, and it is allowed to be an arbitrarily inaccurate underestimate -- even zero. It is not a reliable measure of the total number of bytes that may eventually be readable from the stream. Instead, use the Blob's length() method.

Append data to a DB2 blob

In my DB2 database, I have a table with a Blob:
CREATE TABLE FILE_STORAGE (
FILE_STORAGE_ID integer,
DATA blob(2147483647),
CONSTRAINT PK_FILE_STORAGE PRIMARY KEY (FILE_STORAGE_ID));
Using the db2jcc JDBC driver (db2jcc4-9.7.jar), I can read and write data in this table without any problems.
Now I need to be able to append data to existing rows, but DB2 gives the cryptic error
Invalid operation: setBinaryStream is not allowed on a locator or a reference. ERRORCODE=-4474, SQLSTATE=null
I use the following code to append my data:
String selectQuery = "SELECT DATA FROM FILE_STORAGE WHERE FILE_STORAGE_ID = ?";
try (PreparedStatement ps = conn.prepareStatement(selectQuery, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE)) {
ps.setInt(1, fileStorageID);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
Blob existing = rs.getBlob(1);
try {
// The following line throws the exception:
try (OutputStream output = existing.setBinaryStream(existing.length() + 1)) {
// append the new data to the output:
writeData(output);
} catch (IOException e) {
throw new IllegalStateException("Error writing output stream to blob", e);
}
rs.updateBlob(1, existing);
rs.updateRow();
} finally {
existing.free();
}
} else {
throw new IllegalStateException("No row found for file storage ID: " + fileStorageID);
}
}
}
My code is using the methods as suggested in OutputStream to the BLOB column of a DB2 database table. There also seem to be other people who have the same problem: Update lob columns using lob locator.
As a workaround, I currently read all the existing data into memory, append the new data in memory, and then write the complete data back into the blob. This works, but it's very slow and obviously it will take longer if there's more data in the blob, getting slower with each update.
I do need to use Java to update the data, but apart from switching away from the JVM, I am happy to try any possible alternatives at all, I just need to append the data somehow.
Thanks in advance for any ideas!
If you only need to append data to the end of a BLOB column and don't want to read the entire value into your program, a simple UPDATE statement will be faster and more straightforward.
Your Java program could run something like this via executeUpdate():
UPDATE file_storage SET data = data || BLOB(?) WHERE file_storage_id = ?
The parameter markers for this would be populated by setBlob(1, dataToAppend) and setInt(2, fileStorageID).

How to use setClob() in PreparedStatement inJDBC

Here are the info:
I have a String
I want to insert a record in a table with the String in a column whose
datatype is CLOB.
I would like to use setClob() method of the preparedstatement.
So my question is how to create a Clob object from this String so that I
can use setClob() method.
Thanks in advance,
Naveen
If you want to write a String to CLOB column just use PreparedStatement.setString.
If you want to know how to create a CLOB from String this is it
Clob clob = connection.createClob();
clob.setString(1, str);
You may create the clob from a connection object as follows
Connection con = null;// write code to make a connection object
Clob clob = con.createClob();
String str = "this is a stirng";
clob.setString(1, str );
PreparedStatement ps = null;// write code to create a prepared statement
ps.setClob(4, clob);
Or you may try the alternative code as follows :
//alternative way
String str = "this is a stirng";
ByteArrayInputStream inputStream = new ByteArrayInputStream(str.getBytes());
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
int parameterIndex = 1;
PreparedStatement ps = null;// write code to create a prepared statement
ps.setClob(parameterIndex, inputStreamReader);
For CLOB it is of String already. So, just use .setString() and that should work. One thing about ORACLE jdbc if you are using it, it like the CLOB INPUT parameter to be the last one in your statement especially with a large data.
Example:
INSERT INTO MY_TABL (NUM_COL, VARC_COL, VARC_COL, TS_COL, CLOB_COL)
VALUES(?,?,?,?,?);
As you can see, the CLOB_COL is of type CLOB and should be last so that when
you do .setString(5) and 5 is the last index.
I had a specific variation of this issue which required to insert a clob into an Oracle database from java code running on that db. None of the answers here quite worked for me.
I eventually found solution, the trick being to use oracle.sql.CLOB
This the approach I discovered:
create table test_clob (
c clob
);
create or replace and compile java source named java_clob_insert as
import java.sql.Connection;
import java.sql.PreparedStatement;
import oracle.sql.CLOB;
import java.io.Writer;
public class JavaClobInsert {
public static void doInsert () {
try {
//create the connection and statement
Connection oracleConn =
(new oracle.jdbc.OracleDriver()).defaultConnection();
String stmt = "INSERT INTO test_clob values (?)";
PreparedStatement oraclePstmt = oracleConn.prepareStatement(stmt);
//Imagine we have a mysql longtext or some very long string
String s = "";
for (int i = 0; i < 32768; i++) {
s += i % 10;
}
//Initialise the Oracle CLOB
CLOB clob;
clob = CLOB.createTemporary(oracleConn, true, CLOB.DURATION_CALL);
//Good idea to check the string is not null before writing to clob
if (s != null) {
Writer w = clob.setCharacterStream( 1L );
w.write(s);
w.close();
oraclePstmt.setClob(1, clob);
} else {
oraclePstmt.setString(1, "");
}
//clean up
oraclePstmt.executeUpdate();
oracleConn.commit();
oraclePstmt.close();
oracleConn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
/
create or replace procedure clob_insert as language java name
'JavaClobInsert.doInsert()';
/
begin
clob_insert;
end;
/
select *
from test_clob;
Today i had an issue with a Clob field because i was using "setString" to set the parameter, but then i had this error while testing with a very long string: "setString can handle only Strings with less than 32766 characters"
I used connection.createClob but it gave me this exception:
java.lang.AbstractMethodError: org.apache.tomcat.dbcp.dbcp.PoolingDataSource$PoolGuardConnectionWrapper.createClob()Ljava/sql/Clob;
So looking for this exception i found this
using CLOB in java throwing exception and the accepted answer (using setCharacterStream instead of setClob) worked for me
Copy/Pasted from the accepted answer (so all credits are for a_horse_with_no_name )
StringReader reader = new StringReader(userAbout);
PreparedStatement insertClob = dbCon.prepareStatement("UPDATE user_data SET user_about=? WHERE user_id=?");
insertClob.setCharacterStream(1, reader, userAbout.length());
insertClob.setInt(2,userId);
My answer is slightly different than others...
I had a PreparedStatement, stmt, and was using stmt.setString(colIndex, value) for updates to my database that had a CLOB column.
This worked without fail for me when inserting and updating rows in the database table.
When others tested this code though they would occasionally see an exception occur:
ORA-22275: invalid LOB locator
It only seemed to happen on updates, not inserts - not sure why on that, when value was null. And I only ever had this occur with Oracle databases, not MSSQL or DB2.
Anyway to fix it I changed the logic to test for a null value
if (value == null) {
stmt.setNull(colIndex, java.sql.Types.CLOB);
}
else {
stmt.setString(colIndex, value);
}
This worked without fail for me and others!

how to insert image in to database

CREATE TABLE IMGTABLE
(
NAME char,
PHOTO blob
)
This is Query for imagetable:
import java.sql.*;
import java.io.*;
public class arralistclaaa
{
public static void main(String[] args)
{
try
{
Class.forName("com.mysql.jdbc.Driver");
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "");
PreparedStatement ps = con.prepareStatement("insert into imgtable values(?,?)");
ps.setString(1, "sonoo");
FileInputStream fin = new FileInputStream("E:\\actsandrulesMobile_biggerb.png");
ps.setBinaryStream(2, fin, fin.available());
int i = ps.executeUpdate();
System.out.println(i + " records affected");
con.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
This is Java class code for insert image in database When i run this code then com.mysql.jdbc.MysqlDataTruncation: Data truncation: Data too long for column 'NAME' at row 1 this Error is coming i dont know where is Problem to insert image in data base please help me why this Error is coming.
Why did you use char instead of varchar(100) in NAME column. Change Name column type to varchar
The issue at hand is that your value for NAME is too long. Your table definition has it as a single character (Or zero characters? I honestly don't know what you get with char and no size).
Also, PreparedStatement includes a .setBlob() method that takes an InputStream as the second argument.
http://docs.oracle.com/javase/7/docs/api/java/sql/PreparedStatement.html#setBlob%28int,%20java.io.InputStream%29
Change ´char´ to ´varchar´ in your table like this :
CREATE TABLE IMGTABLE
( NAME varchar,
PHOTO blob
)
No need of Storing entire Image in DB, Just Store Image Path in the column (upload Image in Server) and retrieve it and form the Path and Display it in Browser.

Size of byte array gets double when it retrieve from Postgres database

I am using JSF2.0, jetty server and Postgres database. I want to store mp3 file in database in byte array formet. I upload the byte array of mp3 file. But when I retrive same byte array from database I get byte array of double size and my because of that my mp3 file is not working as well.
public void updateAnnouncementDetail(AnnouncementDetail announcementDetail) {
System.out.println("Upload byte array size:" + announcementDetail.getContent().length);
Session sess=get Session();
sess.beginTransaction();
sess.save(announcementDetail);
sess.getTransaction().commit();
AnnouncementDetail detail;
detail = retrieveAnnouncementDetailById(new AnnouncementDetailPK(announcementDetail.getAnnouncementDetailPK().getAnnouncement(), announcementDetail.getAnnouncementDetailPK().getLanguage()));
System.out.println("Retrived byte array size:" + detail.getContent().length);
}
Output:
Upload byte array size:384440
Retrived byte array size:768879
I dont know why it is happening. I am storing byte array and on the next line I am retriving it. But I get byte array of double size. Let me know if you need more details.
Postgres 9.x bytea values have hex as default output. So, try:
ALTER DATABASE database_name SET bytea_output = 'escape';
When dealing with binary data in Postgres, it is important to use proper data binding.
First, data column must be of type BYTEA. Is that the case?
To store data, use prepared statements and be sure to call setBinaryStream():
File file = new File("myimage.gif");
FileInputStream fis = new FileInputStream(file);
PreparedStatement ps = conn.prepareStatement(
"INSERT INTO images VALUES (?, ?)"
);
ps.setString(1, file.getName());
ps.setBinaryStream(2, fis, (int)file.length());
ps.executeUpdate();
ps.close();
fis.close();
To retrieve data, also use prepared statements and use getBytes():
PreparedStatement ps = conn.prepareStatement(
"SELECT img FROM images WHERE imgname = ?"
);
ps.setString(1, "myimage.gif");
ResultSet rs = ps.executeQuery();
while (rs.next()) {
byte[] imgBytes = rs.getBytes(1);
// use the data in some way here
}
rs.close();
ps.close();
See more here.

Categories