Java Forum / Databases / October 2007
com.mysql.jdbc.UpdatableResultSet.updateBlob gives AbstractMethodError
pjvleeuwen@gmail.com - 02 Oct 2007 16:25 GMT Hi all,
I'm quite new to all jdbc, but so far working with resultset's over SQL seemed easy so far, but I'm having some problems writing a blob to the database. What I try to do is two steps: 1. read an image from the web and write it to a blob 2. read the blob and display it as an image
I did the same thing before without trying to write to the blob, but I want to store the images in DB because the only stay on the web for a couple of weeks.
Googling for UpdatableResultset updateBlob AbstractMethodError gets me lost. Any help is well appreciated.
By the way: the image is 18257 bytes and max_allowed_packet of MySQL is 1048576
Anyone? thanx
### stderr output ### Exception in thread "main" java.lang.AbstractMethodError: com.mysql.jdbc.UpdatableResultSet.updateBlob(Ljava/lang/String;Ljava/ io/InputStream;)V at nl.vanleeuwenwilmot.housing.search.woonnet.app.Test.main(Test.java: 52) ######
### java class ### package nl.vanleeuwenwilmot.housing.search.woonnet.app;
import java.awt.BorderLayout; import java.awt.Image; import java.io.IOException; import java.io.InputStream;
import java.net.URL; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement;
import javax.imageio.ImageIO; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JLabel;
public class Test {
/** * @param args * @throws IOException * @throws SQLException */ public static void main(String[] args) throws IOException, SQLException {
Connection con = null; try { Class.forName("com.mysql.jdbc.Driver").newInstance(); con = DriverManager.getConnection("jdbc:mysql:///woonnet", "root", "<A2Cc|4w"); } catch (InstantiationException e) { e.printStackTrace(); System.exit(1); } catch (IllegalAccessException e) { e.printStackTrace(); System.exit(1); } catch (ClassNotFoundException e) { e.printStackTrace(); System.exit(1); }
Statement updatestmt = con.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE);
ResultSet resultset = updatestmt .executeQuery("SELECT * FROM `image` WHERE `house_id` = 1"); resultset.first(); // fine till here resultset.updateBlob("image", new URL( // <==== line 52 "http://www.woonnet-haaglanden.nl/library/fotos/ 18/12845.jpg") .openStream()); resultset.updateRow(); resultset.close(); updatestmt.close();
Statement readstmt = null; readstmt = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
resultset = readstmt.executeQuery("SELECT * FROM `image` " // + "WHERE `house_id` = 1;"); resultset.first(); InputStream theimage = resultset.getBlob("image").getBinaryStream();
Image image = ImageIO.read(theimage);
// Use a label to display the image JFrame frame = new JFrame(); JLabel label = new JLabel(new ImageIcon(image)); frame.getContentPane().add(label, BorderLayout.CENTER); frame.pack(); frame.setVisible(true); } } ######
Arne Vajhøj - 02 Oct 2007 17:54 GMT > Googling for UpdatableResultset updateBlob AbstractMethodError gets me > lost. Any help is well appreciated.
> ### stderr output ### > Exception in thread "main" java.lang.AbstractMethodError: [quoted text clipped - 3 lines] > 52) > ###### What version of the MySQL JDBC driver are you using ?
I suspect an old version that does not have this method.
Arne
Dyreatnews@sun.com - 02 Oct 2007 18:00 GMT > Hi all, > [quoted text clipped - 11 lines] > Googling for UpdatableResultset updateBlob AbstractMethodError gets me > lost. Any help is well appreciated. Why do you have to use an updatable result set to store the blob? Why can't you use an ordnary update statment?
I don't know if MySQL supports updatable result sets with blobs, (it probably does) but why make things more complicated than necessary...?
 Signature dt
pjvleeuwen@gmail.com - 02 Oct 2007 19:29 GMT Sorry for the late response, just back from diner.
Arne
> What version of the MySQL JDBC driver are you using ? <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.0.3</version> </dependency> And MySQL monitor informed me that I have 5.0.26-community-nt...
Dt
> Why do you have to use an updatable result set to store the blob? Why > can't you use an ordnary update statment? Since I wouldn't know the answer to that question I also tried the following
PreparedStatement pstmt = con .prepareStatement("INSERT INTO `image` (`house_id`, `image`) " + "VALUES(1, ?)"); pstmt.setBlob(1, new URL( // <===============line 51 "http://www.woonnet-haaglanden.nl/library/fotos/ 18/12845.jpg") .openStream()); pstmt.executeUpdate();
Exception in thread "main" java.lang.AbstractMethodError: com.mysql.jdbc.ServerPreparedStatement.setBlob(ILjava/io/ InputStream;)V at nl.vanleeuwenwilmot.housing.search.woonnet.app.Test.main(Test.java: 51)
And if I try setObject like the following I get no error at 51, but a null-pointer exception after reading it.
public static void main(String[] args) throws IOException, SQLException {
Connection con = null; try { Class.forName("com.mysql.jdbc.Driver").newInstance(); con = DriverManager.getConnection("jdbc:mysql:///woonnet", "root", "<A2Cc|4w"); } catch (InstantiationException e) { e.printStackTrace(); System.exit(1); } catch (IllegalAccessException e) { e.printStackTrace(); System.exit(1); } catch (ClassNotFoundException e) { e.printStackTrace(); System.exit(1); }
PreparedStatement pstmt = con .prepareStatement("INSERT INTO `image` (`house_id`, `image`) " + "VALUES(1, ?)"); pstmt.setObject(1, new URL( "http://www.woonnet-haaglanden.nl/library/fotos/ 18/12845.jpg") .openStream()); pstmt.executeUpdate();
Statement readstmt = null; readstmt = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
ResultSet resultset = readstmt.executeQuery("SELECT * FROM `image` " // + "WHERE `house_id` = 1;"); resultset.first(); InputStream theimage = resultset.getBlob("image").getBinaryStream();
Image image = ImageIO.read(theimage);
// Use a label to display the image JFrame frame = new JFrame(); JLabel label = new JLabel(new ImageIcon(image)); // <=== line 69 frame.getContentPane().add(label, BorderLayout.CENTER); frame.pack(); frame.setVisible(true); }
Exception in thread "main" java.lang.NullPointerException at javax.swing.ImageIcon.<init>(ImageIcon.java:161) at nl.vanleeuwenwilmot.housing.search.woonnet.app.Test.main(Test.java: 69)
Further more I tried reading the inputstream to a string (and play ignorant on charset headaches), but I couldn't get that to work... (furthermore, it doesn't seem the right way to go).
*Any* suggestions are warmly welcome. I stay tuned.. (oh and off topic suggestions on coding like dt.'s are also welcome, there's just a lot that I don't know...)
Lew - 02 Oct 2007 22:05 GMT > ResultSet.TYPE_FORWARD_ONLY, ...
> resultset.first(); <http://java.sun.com/javase/6/docs/api/java/sql/ResultSet.html#first()>
> Returns: > true if the cursor is on a valid row; false if there are no rows in the result set You don't check the return code. This might not matter here, but you should be aware of it.
> Throws: > SQLException - if ... this method is called on a ... result set type ... TYPE_FORWARD_ONLY Which is what you typed your ResultSet.
> PreparedStatement pstmt = con > .prepareStatement("INSERT INTO `image` (`house_id`, [quoted text clipped - 10 lines] > at nl.vanleeuwenwilmot.housing.search.woonnet.app.Test.main(Test.java: > 51) This is pretty much the same error you got with the ResultSet.
> And if I try setObject like the following I get no error at 51, but a > null-pointer exception after reading it. [quoted text clipped - 5 lines] > try { > Class.forName("com.mysql.jdbc.Driver").newInstance(); You don't need the newInstance(). You could also do the forName() call in a static initializer.
...
> PreparedStatement pstmt = con > .prepareStatement("INSERT INTO `image` (`house_id`, [quoted text clipped - 14 lines] > + "WHERE `house_id` = 1;"); > resultset.first(); This time it's with a ResultSet.TYPE_SCROLL_INSENSITIVE, but you still should consider using next() instead, and you must check the return code!
> InputStream theimage = > resultset.getBlob("image").getBinaryStream(); [quoted text clipped - 16 lines] > > Further more I tried reading the inputstream That's "InputStream".
> to a string (and playignorant on charset headaches), but I couldn't get that to work... > (furthermore, it doesn't seem the right way to go). It isn't, but byte[] would be all right.
The AbstractMethodError only occurs if the implementing class for the abstract call is not available or incompatible at run time. Something isn't finding the MySQL-specific implementation of these methods. I'd review how you have the MySQL JAR located in your classpath.
It's a weird one, because a classpath issue shows up at compile time, or worst case, at runtime when the Class.forName() fails. How you could get to the MySQL Driver class and not have access to its implementation classes is a mystery.
There's a clue, however, at the MySQL web site: <http://dev.mysql.com/doc/refman/5.0/en/connector-j-installing-classpath.html> where the tell you to put the JAR
mysql-connector-java-[version]-bin.jar
in your classpath, but you've got
mysql-connector-java-5.0.3.jar
I downloaded the latest Connector/J (version 5.0.7) and there was only the *-bin.jar version, not the version without the "-bin".
 Signature Lew
David Harper - 02 Oct 2007 20:23 GMT [SNIP]
> ### stderr output ### > Exception in thread "main" java.lang.AbstractMethodError: [quoted text clipped - 3 lines] > 52) > ###### [SNIP]
The Java API documentation for java.lang.AbstractMethodError explains:
public class AbstractMethodError extends IncompatibleClassChangeError
Thrown when an application tries to call an abstract method. Normally, this error is caught by the compiler; this error can only occur at run time if the definition of some class has incompatibly changed since the currently executing method was last compiled.
This can happen if you have two versions of the Connector/J JAR file, and you are using the newer one when compiling your code but an older version when running it. Check your compile-time and run-time classpaths to ensure that they both reference the newer version of the JAR file.
David Harper Cambridge, England
pjvleeuwen@gmail.com - 02 Oct 2007 20:41 GMT David
> Thrown when an application tries to call an abstract method. Normally, > this error is caught by the compiler; this error can only occur at run
> version when running it. Check your compile-time and run-time > classpaths to ensure that they both reference the newer version of the > JAR file. Thanks, but I only *have* one version. I guess the MySQL Connection\J guys felt that the next "*Normally* this error..." was optional. :)
$ ls -R .m2/repository/mysql/ .m2/repository/mysql/: mysql-connector-java
.m2/repository/mysql/mysql-connector-java: 5.0.3
.m2/repository/mysql/mysql-connector-java/5.0.3: mysql-connector-java-5.0.3.jar mysql-connector-java-5.0.3.jar.sha1 mysql-connector-java-5.0.3.pom mysql-connector-java-5.0.3.pom.sha1
David Harper - 03 Oct 2007 07:27 GMT > David >> Thrown when an application tries to call an abstract method. Normally, [quoted text clipped - 5 lines] > Thanks, but I only *have* one version. I guess the MySQL Connection\J > guys felt that the next "*Normally* this error..." was optional. :) I suspect that I wasn't thinking clearly last night. Sorry. Here's a more likely explanation.
Your code never refers directly to the concrete implementation classes in the Connector/J JAR file (such as com.mysql.jdbc.Driver) but only to the classes (actually, mostly interfaces, like java.sql.Driver) in java.sql.* and javax.sql.*
Therefore, your compiler will have no reason to check any of the classes in the Connector/J JAR file, and the abstract method problem is not detected at compile time. Remember that java.sql.ResultSet, like java.sql.Driver, is an interface, not a class.
You only hit the abstract method problem when you actually run your code, and the Java virtual machine needs to load the concrete classes such as com.mysql.jdbc.Driver, which implements the java.sql.Driver interface.
When your program reaches the call to ResultSet.updateBlob at line 52, the JVM attempts to call the method updateBlob on a concrete com.mysql.jdbc.UpdatableResultSet object. That method is apparently abstract, and you get the mysterious AbstractMethodError.
I hope this makes better sense :-)
David Harper Cambridge, England
pjvleeuwen@gmail.com - 03 Oct 2007 13:06 GMT David
> When your program reaches the call to ResultSet.updateBlob at line 52, > the JVM attempts to call the method updateBlob on a concrete > com.mysql.jdbc.UpdatableResultSet object. That method is apparently > abstract, and you get the mysterious AbstractMethodError. So MySQL Connector/J doesn't implement any code for handling blobs here. Makes the most sence to me. Forget about the nice blob interface then.
Lew
> > to a string (and playignorant on charset headaches), but I couldn't get that to work... > > (furthermore, it doesn't seem the right way to go). > It isn't, but byte[] would be all right. You are very right there. I liked the Blob's because they work with streams, but the following code below works! With byte[].
I'll just wrap it in an URL2Bytes utility class and all is done. Thanks for all your tips! Ciao
public static void main(String[] args) throws MalformedURLException, IOException, SQLException { // download photo to byte[] URL fileurl = new URL( "http://www.woonnet-haaglanden.nl/" + "library/fotos/18/12845.jpg"); int length = fileurl.openConnection() .getContentLength(); byte[] bytebuf = new byte[length]; InputStream is = fileurl.openStream(); byte[] b = new byte[1]; for (int i = 0; is.read(b) > -1; i++) { bytebuf[i] = b[0]; }
// write to DB Connection con = null; try { Class .forName( "com.mysql.jdbc.Driver") .newInstance(); con = DriverManager.getConnection( "jdbc:mysql:///woonnet", "root", "<A2Cc|4w"); } catch (InstantiationException e) { e.printStackTrace(); System.exit(1); } catch (IllegalAccessException e) { e.printStackTrace(); System.exit(1); } catch (ClassNotFoundException e) { e.printStackTrace(); System.exit(1); } PreparedStatement pstmt = con .prepareStatement("INSERT INTO `image` " + "(`house_id`, `image`) " + "VALUES(4, ?)"); pstmt.setBytes(1, bytebuf); pstmt.executeUpdate();
// read from DB Statement readstmt = con .createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); ResultSet resultset = readstmt .executeQuery("SELECT * FROM `image` " // + "WHERE `house_id` = 4;"); resultset.first(); byte[] readbytes = resultset .getBytes("image"); Image image = ImageIO .read(new ByteArrayInputStream( readbytes));
// Use a label to display the image JFrame frame = new JFrame(); JLabel label = new JLabel(new ImageIcon( image)); frame.getContentPane().add(label, BorderLayout.CENTER); frame.pack(); frame.setVisible(true); }
Lew - 03 Oct 2007 16:17 GMT > public static void main(String[] args) > throws MalformedURLException, > IOException, SQLException { ...
> // write to DB > Connection con = null; [quoted text clipped - 3 lines] > "com.mysql.jdbc.Driver") > .newInstance(); Why are you creating a throwaway instance here?
 Signature Lew
Arne Vajhøj - 05 Oct 2007 02:37 GMT >> public static void main(String[] args) >> throws MalformedURLException, [quoted text clipped - 9 lines] > > Why are you creating a throwaway instance here? That is a common practice when loading the MySQL JDBC driver.
My guess is that some very old version of the driver registered itself with DriverManager in the constructor, so that it was necessary.
It is not necessary today and has not been for many years, but you will see that code all the time.
The MySQL docs even use it even though they blame Java:
<quote> Example 3.1. Registering the Driver With the DriverManager
The following section of Java code shows how you might register MySQL Connector/J from the main() method of your application.
import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException;
// Notice, do not import com.mysql.jdbc.* // or you will have problems!
public class LoadDriver { public static void main(String[] args) { try { // The newInstance() call is a work around for some // broken Java implementations
Class.forName("com.mysql.jdbc.Driver").newInstance(); } catch (Exception ex) { // handle the error } </quote>
Arne
Lew - 03 Oct 2007 16:23 GMT > PreparedStatement pstmt = con > .prepareStatement("INSERT INTO `image` " > + "(`house_id`, `image`) " > + "VALUES(4, ?)"); Remember, prepareStatement() "[t]hrows ... SQLException ... if a database access error occurs or this method is called on a closed connection."
> pstmt.executeUpdate(); This method call returns a value that you should not ignore, nor should you ignore the possible SQLException.
> // read from DB > Statement readstmt = con > .createStatement( > ResultSet.TYPE_SCROLL_INSENSITIVE, > ResultSet.CONCUR_READ_ONLY); "Throws ... SQLException ... if a database access error occurs or this method is called on a closed connection"
> ResultSet resultset = readstmt > .executeQuery("SELECT * FROM `image` " // > + "WHERE `house_id` = 4;"); possible SQLException
> resultset.first(); Why not call next(), and why, oh, why do you disregard the returned value?
 Signature Lew
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|