Hibernate convert existing Integer[] to String[] (bytea) in Postgres database - java

I'm using Spring, Hibernate and Postgres.
In one of my model class I have:
#Entity
class SomeData {
private Long dataId;
private String name;
private Integer[] data;
}
It is already used and I have data in database in above format.
"data" field is a bytea (visible in phppgadmin) type, one of the example values saved in db is:
��ur[Ljava.lang.Integer;������xpsr...
Problem is that now I need to change model to
String[] data;
But when I do that Hibernate can't read those data because it has type Integer.
My question - is it possible to convert somehow these Integer array objects to String array objects? Using some db tool, sql query or Hibernate?

You need to write a conversion algorithm to run once. Something like:
Connection c = ((SessionImplementor)entitymanager).connection();
boolean oldstate = c.getAutoCommit();
c.setAutoCommit(false);
Statement s = c.createStatement();
PreparedStatement ps = c.prepareStatement("UPDATE my_table SET data = ? WHERE id = ?");
ResultSet rs = s.executeQuery("SELECT id, data FROM my_table");
ByteArrayInputStream bais;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectInputStream ois;
ObjectOutputStream oos = new ObjectOutputStream(baos);
while (rs.next()) {
long id = rs.getLong(1);
byte[] bdata = rs.getBytes(2);
bais = new ByteArrayInputStream(bdata);
ois = new ObjectInputStream(bais);
Integer[] olddata = (Integer[]) ois.readObject();
String[] newdata = new String[olddata.length];
for (int i = 0; i < olddata.length; i++) {
Integer din = olddata[i];
String dout = null;
if (din != null) {
dout = din.toString();
}
newdata[i] = dout;
}
oos.writeObject(newdata);
oos.flush();
ps.setLong(1, id);
ps.setBytes(2, baos.toByteArray());
baos.reset();
}
c.commit();
c.setAutoCommit(oldstate);
If you want to do it through Hibernate, you'd need a secondary column, or you'd have to change it to Object[], run the conversion, then change it to String[], but that's not convenient for update on production, nor is it efficient, as you'd go through a lot of layers of validations and conversions inside Hibernate.
However, if you choose to use array types inside the database itself, I have a standalone module currently proposed that allows for it https://github.com/hibernate/hibernate-orm/pull/1499 . We don't have test-cases that don't work with it, but if you can find any that need fixing, that would help with getting it approved.

Related

java read arrayList<Integer> - objectInputStream EOFEXception

Hi i am trying to read an ArrayList of Integer and get EOFException.
i have a ArrayList of Question which is serialized and i do the same thing for reading it no problem, but with ArrayList Integer dont work.
i write the two ArrayList like so: (im ommiting all the other fields that are not relevant)
String sqlQuery = "insert into test values (?,?,?,?,?,?,?,?,?)";
PreparedStatement pst = null;
try {
if (DBConnector.myConn != null) {
pst = DBConnector.myConn.prepareStatement(sqlQuery);
// serialize object
Blob questionsBlob = DBConnector.myConn.createBlob();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(t.getQuestions());
oos.close();
Blob pointsBlob = DBConnector.myConn.createBlob();
ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
ObjectOutputStream oos2 = new ObjectOutputStream(baos2);
oos.writeObject(t.getPointsPerQuestion());
oos2.close();
// store in byte array
byte[] questionsAsByte = baos.toByteArray();
byte[] pointsAsByte = baos2.toByteArray();
// fill blob object with byte array
questionsBlob.setBytes(1, questionsAsByte);
pointsBlob.setBytes(1, pointsAsByte);
pst.setBlob(3, questionsBlob);
pst.setBlob(4, pointsBlob);
pst.executeUpdate();
and read the objects:
String sqlQuery = "select * from test where teacherUsername = \"" + username + "\";";
ArrayList<Test> arr = new ArrayList<Test>();
ArrayList<Question> questions;
ArrayList<Integer> points;
try {
if (DBConnector.myConn != null) {
Statement st = DBConnector.myConn.createStatement();
ResultSet rs = st.executeQuery(sqlQuery);
while (rs.next()) {
questions = new ArrayList<>();
points = new ArrayList<>();
Blob questionsBlob = rs.getBlob(3);
BufferedInputStream bis = new BufferedInputStream(questionsBlob.getBinaryStream());
ObjectInputStream ois = new ObjectInputStream(bis);
questions = (ArrayList<Question>) ois.readObject();
System.out.println(questions);
Blob qPointsBlob = rs.getBlob(4);
BufferedInputStream bis1 = new BufferedInputStream(qPointsBlob.getBinaryStream());
ObjectInputStream ois1 = new ObjectInputStream(bis1);
try {
points = (ArrayList<Integer>) ois1.readObject(); // PROBLEM HERE
}catch(EOFException e) {
System.out.println(points);
}
when i try to read the data - it works with the ArrayList of Question and really shows me the questions, but with the ArrayList of Integer it gives me the EOFException.
any ideas ?

utf-8 oracle clob, write/read pdf in java?

my mission is to change 2 php pages with a java webapp that writes an upload pdf file to a clob and reads it when user ask for a download.
I threated the pdf as a byte array and have been able to read/write it correctly
the big problem is the backward compatibility: files written by php are not readable by my java webapp and vice-versa
thanks in advance for help
NOTE: Do not answer me to use a Blob, I know it is the easy way but in this case we have to assume we cannot make an alter table on the db due to backward compatibility
Here's my code to read the clob into a byte array:
byte[] result = null;
InputStream is = null;
ByteArrayOutputStream bos = null;
//...prepare the query to get clob as first column in the resultset
ResultSet rs = stmt.executeQuery();
int len;
int size = 1024;
byte[] buf;
if(rs.next()) {
is = rs.getBinaryStream(1);
bos = new ByteArrayOutputStream();
buf = new byte[size];
while ((len = is.read(buf, 0, size)) != -1)
bos.write(buf, 0, len);
bos.flush();
bos.close();
is.close();
result = bos.toByteArray();
}
rs.close();
here's the code to write the byte array into clob:
//...some other sql stuff here...
stmt = conn.prepareStatement("SELECT clob_col FROM my_table WHERE prim_key = ? FOR UPDATE");
stmt.setString(1, param);
ResultSet rs = stmt.executeQuery();
Clob myclob=null;
if(rs.next())
myclob=rs.getClob(1);
OutputStream writer = myclob.setAsciiStream(1L);
writer.write(allegato);
writer.flush();
writer.close();
stmt = conn.prepareStatement("UPDATE my_table SET clob_col = ? WHERE prim_key = ? ");
stmt.setClob(1, myclob);
stmt.setString(2, param);
stmt.executeUpdate();
oracle encoding is ITALIAN_ITALY.WE8ISO8859P1
php encoding is ITALIAN_ITALY.UTF8
A possible solution:
write to clob the hex rapresentation of the byte array, and do the same in read phase
the main advantage are
- few changes in php and java
- no changes to db (alter table)
- indipendent from db encoding
in php we use bin2hex and hex2bin functions before to write and after read the clob
in java we implement 2 easy equivalent functions of bin2hex and hex2bin:
public static byte[] HexToBin(String str){
try{
byte[] result=new byte[str.length()/2];
for (int i = 0; i < result.length; i++)
result[i]=(byte) Integer.parseInt(str.substring(2*i, (2*i)+2), 16);
return result;
}catch(Exception x){
x.printStackTrace();
return null;
}
}
public static String BinToHex(byte[] b){
try{
StringBuffer sb=new StringBuffer();
for (byte bb:b) {
String hexStr = String.format("%02x", bb);
sb.append((hexStr.length()<2)?"0":"");
sb.append(hexStr);
}
return sb.toString();
}catch(Exception x){
x.printStackTrace();
return null;
}
}

Postgres Serializable/Bytea Retrieval

I have a table column type bytea. It is populated using hibernate and a field of type Serializable. Any ideas how I would be able to retrieve the values outside hibernate in a simple java standalone program? The values are of type string, double or integer.
the database is UTF8
while(res.next()){
byte[] byteArr = res.getBytes("value");
}
while (res.next()) {
byte[] byteArr = res.getBytes("value");
ByteArrayInputStream is = new ByteArrayInputStream(byteArr);
ObjectInputStream ois = new ObjectInputStream(is);
Serializable val = (Serializable) ois.readObject();
System.out.println(val);
}

inserting image to sqlite

I'm creating application using sqlite database and netbeans .I have a problem when I save an image to data base.
I have an image field in database which data type is BLOB and i'm inserting a byte array . i know it doesn't match.but when i save it holds value like this "[B#2c8f544b" but actual values should be like this "BLOB (Size: 1850)". if there is some value like that then only i can retrieve the image. I really can't figure out how to do this.if you have any reference please let me know.
my idea is before save to database convert byte array to BLOB type.but I couldn't find any code.
String fname = p.fname;
String lname = p.lname;
byte[] image = p.image_det;
String mob = p.mobile;
String wor = p.work;
String hom = p.home;
String fax = p.fax;
int pID ;
ResultSet rst = stmt.executeQuery("SELECT MAX(pID) FROM person");
pID = Integer.parseInt(rst.getString(1))+1;
Statement stmt1 = con.createStatement();
stmt1.executeUpdate("INSERT INTO person(pID,F_name,L_name,image) VALUES ("+pID+" ,'"+fname+"','"+lname+"','"+image+"' ) ");
//-------------------------getting image path and get image data to array called image_details
File f;
String ipath = f.getAbsolutePath(); // getting image path
byte[] image_detail = null;
try
{
File image = new File(ipath);
FileInputStream fis = new FileInputStream(image);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
for(int readNum;(readNum = fis.read(buf))!= -1;)
{
baos.write(buf, 0,readNum);
}
image_detail = baos.toByteArray();
per.setImage_det(image_detail); // set image data for person class

Snippet to create a file from the contents of a blob in Java

I have some files stored in a database blob column in Oracle 9.
I would like to have those files stored in the file system.
This should be pretty easy, but I don't find the right snipped.
How can I do this in java?
PreparedStatement ptmst = ...
ResutlSet rs = pstmt.executeQuery();
rs.getBlob();
// mistery
FileOutputStream out = new FileOutputStream();
out.write(); // etc et c
I know it should be something like that... what I don't know is what is commented as mistery
Thanks
EDIT
I finally got this derived from David's question.
This is my lazy implementation:
PreparedStatement pstmt = connection.prepareStatement("select BINARY from MYTABLE");
ResultSet rs = pstmt.executeQuery();
while( rs.next() ) {
Blob blob = rs.getBlob("BINARY");
System.out.println("Read "+ blob.length() + " bytes ");
byte [] array = blob.getBytes( 1, ( int ) blob.length() );
File file = File.createTempFile("something-", ".binary", new File("."));
FileOutputStream out = new FileOutputStream( file );
out.write( array );
out.close();
}
You'd want to get the blob as an inputstream and dump its contents to the outputstream. So 'misery' should be something like:
Blob blob = rs.getBlob(column);
InputStream in = blob.getBinaryStream();
OutputStream out = new FileOutputStream(someFile);
byte[] buff = new byte[4096]; // how much of the blob to read/write at a time
int len = 0;
while ((len = in.read(buff)) != -1) {
out.write(buff, 0, len);
}
If you find yourself doing a lot of IO work like this, you might look into using Apache Commons IO to take care of the details. Then everything after setting up the streams would just be:
IOUtils.copy(in, out);
There is another way of doing the same operation faster. Actually the answer above works fine but like IOUtils.copy(in,out) it takes a lot of time for big documents. The reason is you are trying to write your blob by 4KB iteration. Simplier solution :
Blob blob = rs.getBlob(column);
InputStream in = blob.getBinaryStream();
OutputStream out = new FileOutputStream(someFile);
byte[] buff = blob.getBytes(1,(int)blob.getLength());
out.write(buff);
out.close();
Your outputStream will write the blob in one shot.
Edit
Sorry didn't see the Edit section on the intial Post.

Categories

Resources