问题
So I'm trying to retrieve text and a blob(image) from a database inside a jtable in a nice way. For some reason, whenever I try to retrieve the blob I get this:
java.io.ByteArrayInputStream@1cb63183
in every other column, although I've specified that the blob is on the 8th !
here is a screenshot:
http://s24.postimg.org/t71o3izlh/Screen_Shot_2016_01_28_at_1_26_55_PM.png
Here is my code
sql = "SELECT * FROM Products";
ResultSet rs = stmt.executeQuery(sql);
ResultSetMetaData rsmd = rs.getMetaData();
// getting the columns number
int columnsNumber = rsmd.getColumnCount();
// setting a vector with columns number
Vector columns = new Vector(columnsNumber);
// adding column names
for(int i=1; i<=columnsNumber; i++)
columns.add(rsmd.getColumnName(i));
// setting a vector with row data
Vector rowdata = new Vector();
Vector row;
JTable table = new JTable(rowdata, columns);
while(rs.next())
{
row = new Vector(columnsNumber);
for(int i=1; i<=columnsNumber; i++)
{
// adding the rows in "row vector"
InputStream binaryStream = rs.getBinaryStream(i);
row.add(rs.getString(i));
row.add(rs.getBinaryStream(8));
}
// adding the rows in the database
rowdata.add(row);
}
Could someone explain to me why is this approach not doing the job?
by the way, if I removed this line:
row.add(rs.getBinaryStream(8));
The problem of getting the java.io.ByteArrayInputSteam will disappear, however, I will only be getting the text representation of the image.
it's actually not a duplicate because I'm trying to add it in a specific column depending on how many rows I have, which are BOTH text and Image. So it's dynamically done and the sql has both the image and the text at the same time unlike the "possible duplicate"
Thanks in advance!
回答1:
So, you seem to have a series of compounding problems. Let's start here, where you load the data from the database...
while(rs.next())
{
row = new Vector(columnsNumber);
for(int i=1; i<=columnsNumber; i++)
{
// adding the rows in "row vector"
InputStream binaryStream = rs.getBinaryStream(i);
row.add(rs.getString(i));
row.add(rs.getBinaryStream(8));
}
// adding the rows in the database
rowdata.add(row);
}
So, for each row in the ResultSet
you look through the columns, BUT, for each column, you add the String
value AND the blob from the last column, so the blob will be added 6 times (based on your screen shot). This is, obviously, not what you want and also the reason why you're getting java.io.ByteArrayInputStream@1cb63183
in every other column.
Instead, you want to loop through columns 1
-columnsNumbers - 1
, because we don't want the last column, and add the image to the last column, maybe something like...
while(rs.next())
{
row = new Vector(columnsNumber);
for(int i=1; i < columnsNumber; i++)
{
// adding the rows in "row vector"
InputStream binaryStream = rs.getBinaryStream(i);
row.add(rs.getString(i));
}
row.add(rs.getBinaryStream(8));
// adding the rows in the database
rowdata.add(row);
}
Next problem...
It still prints java.io.ByteArrayInputStream@1cb63183
in the last column!?
This is simply because all you added to the row
Vector
was the binary stream representing the binary data in the database, JTable
has no means by which to render this. You should start by having a look at Concepts: Editors and Renderers and Using Custom Renderers for more details about how you can customise the rendering of these components
First, we need to convert the binary data to an image format which we can use
row.add(ImageIO.read(rs.getBinaryStream(8)));
And use something similar to what is outlined in Rendering BufferedImage in JTable cell
or
row.add(new ImageIcon(ImageIO.read(rs.getBinaryStream(8))));
which should allow the "default" TableCellRenderer
to render it
Runnable Example...
Warning: This example is a little long, because I had to build the database and populate it. It uses a standalong H2 Database engine for simplicity, but should be translatable to most other database. The example also uses a column type of blob
, this is deliberate, as it improves the performance of the database engine.
The problem with getting images to display in a JTable
when using a DefaultTableModel
is, DefaultTableModel
returns Object.class
from the TableModel#getColumnClass
method
Even the DefaultTableModel documentation makes note of this...
Warning:
DefaultTableModel
returns a column class ofObject
. WhenDefaultTableModel
is used with aTableRowSorter
this will result in extensive use oftoString
, which for non-String
data types is expensive. If you useDefaultTableModel
with aTableRowSorter
you are strongly encouraged to overridegetColumnClass
to return the appropriate type.
I overcame this by customising the DefaultTableModel
I returned from TestPane#makeTableModel
...
DefaultTableModel model = new DefaultTableModel(new String[]{"Name", "Image"}, 0) {
@Override
public Class<?> getColumnClass(int columnIndex) {
return columnIndex == 1 ? Icon.class : super.getColumnClass(columnIndex);
}
};
This allowed the JTable
to use the correct TableCellRenderer
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Blob;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.imageio.ImageIO;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.DefaultTableModel;
public class Main {
public static void main(String[] args) {
try {
Class.forName("org.h2.Driver");
makeDatabase();
populateDatabase();
new Main();
} catch (ClassNotFoundException | SQLException | IOException exp) {
exp.printStackTrace();
}
}
protected static Connection makeConnection() throws SQLException {
String path = "jdbc:h2:./TestDatabase";
return DriverManager.getConnection(path, "sa", "");
}
protected static void makeDatabase() throws SQLException {
String cmd = "create table if not exists fruits ("
+ "key BIGINT IDENTITY, "
+ "name varchar(128), "
+ "image longblob)";
try (Connection con = makeConnection()) {
try (PreparedStatement stmt = con.prepareStatement(cmd)) {
System.out.println("> Make fruits table");
stmt.executeUpdate();
}
}
}
protected static void populateDatabase() throws SQLException, IOException {
removeAlFruits();
insert("Apple", ImageIO.read(new File("Apple.png")));
insert("Banana", ImageIO.read(new File("Banana.png")));
insert("Cherries", ImageIO.read(new File("Cherries.png")));
insert("Grapes", ImageIO.read(new File("Grapes.png")));
insert("Orange", ImageIO.read(new File("Orange.png")));
insert("Pear", ImageIO.read(new File("Pear.png")));
insert("Pine Apple", ImageIO.read(new File("PineApple.png")));
insert("Strewberry", ImageIO.read(new File("Strewberry.png")));
insert("Water Melon", ImageIO.read(new File("WaterMelon.png")));
}
protected static void insert(String name, BufferedImage image) throws SQLException, IOException {
String cmd = "insert into fruits (name, image) values (?, ?)";
try (Connection con = makeConnection()) {
try (PreparedStatement stmt = con.prepareStatement(cmd)) {
try (InputStream is = convertImageToInputStream(image)) {
System.out.println("> Insert " + name);
stmt.setString(1, name);
stmt.setBinaryStream(2, is);
int rows = stmt.executeUpdate();
System.out.println("> " + rows + " rows updated");
}
}
}
}
protected static InputStream convertImageToInputStream(BufferedImage image) throws IOException {
ByteArrayOutputStream baos = null;
ByteArrayInputStream bais = null;
try {
baos = new ByteArrayOutputStream();
ImageIO.write(image, "png", baos);
baos.close();
bais = new ByteArrayInputStream(baos.toByteArray());
} finally {
if (baos != null) {
try {
baos.close();
} catch (IOException ex) {
}
}
}
return bais;
}
protected static void removeAlFruits() throws SQLException {
String cmd = "delete from fruits";
try (Connection con = makeConnection()) {
try (PreparedStatement stmt = con.prepareStatement(cmd)) {
System.out.println("> Remove all fruits");
int rows = stmt.executeUpdate();
System.out.println("> " + rows + " rows updated");
}
}
}
public Main() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (SQLException | IOException ex) {
ex.printStackTrace();
}
}
});
}
public class TestPane extends JPanel {
public TestPane() throws SQLException, IOException {
setLayout(new BorderLayout());
DefaultTableModel model = makeTableModel();
JTable table = new JTable(model);
table.setRowHeight(100);
add(new JScrollPane(table));
}
protected DefaultTableModel makeTableModel() throws SQLException, IOException {
DefaultTableModel model = new DefaultTableModel(new String[]{"Name", "Image"}, 0) {
@Override
public Class<?> getColumnClass(int columnIndex) {
return columnIndex == 1 ? Icon.class : super.getColumnClass(columnIndex);
}
};
String cmd = "select name, image from fruits";
try (Connection con = makeConnection()) {
try (PreparedStatement stmt = con.prepareStatement(cmd)) {
try (ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
String name = rs.getString(1);
Blob blob = rs.getBlob(2);
ImageIcon icon = null;
try (InputStream is = blob.getBinaryStream()) {
BufferedImage img = ImageIO.read(is);
icon = new ImageIcon(img);
}
model.addRow(new Object[]{name, icon});
}
}
}
}
return model;
}
}
}
The alternative is to use a custom TableCellRender
as demonstrated in Rendering BufferedImage in JTable cell
来源:https://stackoverflow.com/questions/35069359/trying-to-retrieve-both-text-and-blob-from-mysql-to-jtable