So I'm trying to understand how to use the RowSet API, specifically CachedRowSet, and I feel like I've been bashing my head against a wall for the last hour or so and could use some help.
I've got some very simple tables set up in a MySQL database that I'm using to test this. I should also add that everything I'm attempting to do with RowSet I've been able to do successfully with ResultSet, which leads me to believe that the issue is with my usage of the ResultSet API, rather than the operation I'm attempting to do itself.
Anyway, I'm trying to insert a new row using ResultSet. I'll paste my code here, then add some notes about it below:
CachedRowSet rowSet = null;
try {
RowSetFactory rsFactory = RowSetProvider.newFactory();
rowSet = rsFactory.createCachedRowSet();
rowSet.setUrl("jdbc:mysql://localhost:3306/van1");
rowSet.setUsername("####");
rowSet.setPassword("####");
rowSet.setKeyColumns(new int[]{1});
} catch (SQLException e) {
e.printStackTrace();
}
String query = "select * from phone";
try {
rowSet.setCommand(query);
rowSet.execute();
printTable(rowSet);
rowSet.moveToInsertRow();
rowSet.setInt(1, 4);
rowSet.setString(2, "Mobile");
rowSet.setString(3, "1");
rowSet.setString(4, "732");
rowSet.setString(5, "555");
rowSet.setString(6, "1234");
rowSet.setString(7, "");
rowSet.insertRow();
rowSet.moveToCurrentRow();
rowSet.acceptChanges();
printTable(rowSet);
} catch (SQLException e) {
e.printStackTrace();
}
So, as you can see, I'm trying to update a table of phone numbers with a new phone number. Here are the details:
1) All the phone number fields are datatype char, so that leading zeroes are not lost.
2) I'm using the default CachedRowSet implementation provided by the JDBC API, as opposed to anything specific from the MySQL driver. Not sure if that matters or not, but I'm putting it here just in case. Also, I didn't see an option to import CachedRowSet from the driver library anyway.
3) I'm setting a value for every column in the table, because the RowSet API doesn't allow for rows to be inserted without a value for every column.
4) I've tried the operation using both the setter methods and the update methods. Same result either way.
5) As far as I can tell, I'm on the insert row when executing the insertRow() method. I also return to the current row before invoking acceptChanges(), but since my code never gets that far I can't really comment on that part.
6) The exception is a SQLException (no chained exception within it) thrown on the invocation of the insertRow() method. Here is the stack trace:
java.sql.SQLException: Failed on insert row
at com.sun.rowset.CachedRowSetImpl.insertRow(Unknown Source)
at firsttry.RowSetPractice.rowSetTest(RowSetPractice.java:87)
at firsttry.RowSetPractice.main(RowSetPractice.java:20)
So, I'm out of ideas. Any help would be appreciated. I've searched every thread on this site I could find, all I see is stuff about it failing on the acceptChanges() method rather than insertRow().
Related
This question already has answers here:
Returning a ResultSet
(8 answers)
Closed 2 years ago.
I'm working on creating a program to manage a MySQL server. I have a working UI and code that can add new entries to the database as it should. Pictured below is the proto GUI that I'm using, the part that's important for the question is the table box that will show the entries of the database when it's working.
The code that I have for reading the contents of the database works as it should. The program is structured so that I have separate classes for the interface, scene controller and SQL commands. The issue that I'm dealing with is that I can pull the needed data from the database but getting to the scene controller class to write it to the database simply isn't working.
The relevant pieces of code are included below is included below.
SQL functions:
public ResultSet readDataBase() throws Exception{
try {
// Establish connection to server
Class.forName("com.mysql.cj.jdbc.Driver");
connect=DriverManager.getConnection("jdbc:mysql://localhost/library?"+"user=tempUser&password=12345");
statement=connect.createStatement();
// Execute query and write the results
resultSet=statement.executeQuery("select * from library.books");
return resultSet;
// writeResultSet(resultSet);
} catch (Exception e){
throw e;
} finally {
close();
}
}
Scene controller code:
public void fillTable() throws SQLException{
ResultSet resultSet=null;
try {
resultSet=commands.readDataBase();
while(resultSet.next()) {
String title = resultSet.getString("title");
String author = resultSet.getString("author");
String genre = resultSet.getString("genre");
String format = resultSet.getString("format");
String isbn = resultSet.getString("isbn");
System.out.println("Title: " + title);
System.out.println("Author: " + author);
System.out.println("Genre: " + genre);
System.out.println("Format: " + format);
System.out.println("ISBN: " + isbn);
System.out.println();
}
}catch (Exception e){
e.printStackTrace();
}
}
The purpose of the code in the scene controller block is simply to test for and read the result set for now. The code for writing to write the data from the result to their respective tables will be added later. This code was selected because I originally had it in the SQL functions class and it worked there, so I knew the code was good and did its job.
Whenever I run the code however, I get this error result.
java.sql.SQLException: Operation not allowed after ResultSet closed
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:129)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:89)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:63)
at com.mysql.cj.jdbc.result.ResultSetImpl.checkClosed(ResultSetImpl.java:445)
at com.mysql.cj.jdbc.result.ResultSetImpl.next(ResultSetImpl.java:1726)
at library.test.windows.interfaceSceneController.fillTable(interfaceSceneController.java:108)
at library.test.windows.winInterface.main(winInterface.java:33)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:464)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:363)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at java.base/sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:1051)
I've done some reading into this prior but the only really relevant source of info I could find was in this post from over seven years ago. The answer for that question made reference to using JavaBeans as a place to put the information as an in-between but then used a class called a 'Biler' in their sample code. I can't find any reference to a Biler anywhere except his post and my IDE (IntelliJ if that's relevant) did not recognize it at all. I've been doing some experimentation with JavaBeans but I'm not sure it solving my problem.
In summary, my question is this. What do I need to do in order to properly pass a ResultSet from the function accessing the SQL server to the class containing the code for writing it to the table in my interface? I know this must be possible somehow but I can't seem to figure it out.
Your database access code should be kept entirely separate from your user-interface code. The UI should not deal with an active ResultSet.
You need to copy the data out of the result set, or use a utility to do so for you.
CachedRowSet
A RowSet may be the solution. This interface extends ResultSet. See the Java Tutorials by Oracle for explanations.
You could use the CachedRowSet interface that keeps a copy of the result set data in memory, detached from the database. Oracle provides an implementation, as might other vendors such as your JDBC driver.
A CachedRowSet implementation is disconnected from the database. In contrast, a ResultSet maintains a database connection (the root of your problem). To quote the Javadoc:
A CachedRowSet object is a container for rows of data that caches its rows in memory, which makes it possible to operate without always being connected to its data source. Further, it is a JavaBeansā¢ component and is scrollable, updatable, and serializable.
That interface is extended by a few more interfaces.
POJOs
Plain old Java objects is another option, copying every field of every row from your ResultSet into properties of a Java object.
You can simply loop the result set while instantiating records. Or you can use any of a variety of frameworks to assist.
Records
Defining a class for such POJOs is much simpler when using the new records feature arriving in Java 16, now previewed in Java 15. The constructor, getters, toString, and equals & hashCode are all synthesized by the compiler. You simply declare the properties.
A record can be declared as a stand-alone class, or as a nested class, or even locally within a method.
Well, I assume you are closing the conneciton in your close() method. So that's why it is closed. The finally is executed after the method body but before the result is passed to the calling funtion.
try {
return resultSet;
} finally {
close();
}
IMHO it is a bad pratice to pass back a resultset anyway because the resultset is not really typesafe. If you change the query you will break the fillTable() function without noticing. And, of course, you would need to find a way to close the resultset and the underlying DB connection at some point. This might be challenging.
Probably the only way to do this is to keep a list of static final Strings but if anyone has any idea it is welcome.
I have a POJO and a database table. The columns(properties) are the same as the class members of the POJO. I want to avoid (it is a fairly big program) to write getColumn("username") but instead write something like getColumn(UserPOJO.getUsername().getActualMemberNameInAString())
The only way I can think of is to have a UserPOJO class with just static variables and update it when I add members in the class. The second option is to have all the properties as enums but that is bad I think in a number of levels.
What I would like to have is a way to get the member name that corresponds to the getter method. Some kind of reflection?
Problem to be solved is typos in a huge codebase. I overemphasize here because I have received comments and answers that are not what I am asking for.
Note that I am not using any framework or ORM tool. I am using a Graph database that maps all columns to a Vertex and then you need to do .getProperty(string s) to get an Object. And I do this many times in the code
My understanding of this question is you want minimum code change when your underlaying Database Table get changed.
so let me put it as , say I have a table Info as
CREATE TABLE Info (
field1 INTEGER ,
field2 VARCHAR(1)
);
and I am expecting field3 could be added in future then I need a solution where
I do not need to add extra code for getting information for field3.
Please confirm if the understanding is correct ? if yes then you can use below strategy
/**
* generic Method for executing Any RAW SQL Query.
* for e.g. sql = "select * from Info";
*/
public List<Map<String,String>> executeQuery(String sql) throws Exception {
logger.info("executing SQL = "+sql);
List<Map<String,String>> resultList = new ArrayList<Map<String, String>>();
Statement statement=null;
ResultSet resultSet=null;
ResultSetMetaData metaData = null;
try{
statement = connection.createStatement();
resultSet = statement.executeQuery(sql);
metaData = resultSet.getMetaData();
int noOfColumns = metaData.getColumnCount();
while(resultSet.next()){
Map<String,String> row = new HashMap<String, String>();
for(int i =1;i<=noOfColumns;i++){
row.put(metaData.getColumnName(i), resultSet.getString(metaData.getColumnName(i)));
}
resultList.add(row);
}
} catch (SQLException e) {
throw new Exception(" Exception while executing query on database. Reason "+e.getMessage());
}
finally {
closeResources(statement, resultSet);
}
return resultList;
}
for input String sql = "select * from Info"; Here you will get all records of Info Table with key-value pair where key suggests columnName and value suggests value of that column.
And this is as good as dynamic ,even if we add extra columns we will get that information in our Map also it does not require to use any framework
You have multiple options:
If you only need the name once, I wouldn't bother to create constants for them.
In one of your comments you stated you will use it a lot and in different places, you could create a class that stores all those strings as constants. At least you will avoid typos, and your code wont be littered with strings.
You could use reflection but that would overcomplicate things (in my opinion).
I wouldn't exclude using enums as an option. This SO answer explains it better, then I could do...
I have problem with java and MySQL. My code:
Connection connection;
// ...
for (String query : updateAndInsertQuery) {
Statement stm = connection.createStatement();
stm.execute(query);
stm.close();
}
Statement stm2 = connection.createStatement();
System.out.println("Before query");
System.out.flush();
ResultSet Result = stm2.executeQuery(selectQuery);
System.out.println("After query");
System.out.flush();
int vfrom, vto;
while (Result.next()) {
// ...
}
When I run program i see in MySQL queries and run
show processlist;
selectQuery is visible on list wth status Sending data or Writing to net. On console print: Before query. Next
show processlist;
returns empty list, but application don't print After query. Do you have similar problem?
-- edit
I resolve my problem.
I think:
wen MySQL returns data and query isn't visible on processlist in MySQL
I should immediately get on console message: After query
but console was empty, java process works (cpu usage was 90-100%) so I think it was my mistake, but after 1h application throws Exception
Increase memory limit resolve my problem.
So I have next question why application throw exception after hour? Garbage collection try dealocate unised objects?
executing queries manually usually leads into many different problems - all of which are platform-specific and DB-specific. I think your best answer will be : "switch to ORM".
This framework has proven to be exceptionally good, wrappig all your SQL-data into Entities and transactions (if required) will resolve most of your problems at the same time - you will only need to annotate your entities and relationships correctly. Database-queries can be executed via JPA-"criteria"s which are platform-independent AND allow you to avoid a lot of problems as well as making your code READABLE.
Tutorial : http://www.vogella.com/tutorials/JavaPersistenceAPI/article.html
SO-question : https://stackoverflow.com/questions/743065/what-is-the-fastest-way-to-learn-jpa
With JPA, you wont need to care about statements or queries anymore (well, at least most of the time) and your mentioned problem will disappear - PLUS : it only takes 30-60min to implement.
Additional tip : use Maven & Eclipselink (JPA2 implementation) - thats a very powerful, portable combination
My mysql-query in Java always stops (i.e. freezes and does not continue) at a certain position, which namely is 543,858; even though the table contains approx. 2,000,000 entries. I've checked this by logging the current result-fetching.
It is reproducible and happens every time at the very same position.
"SELECT abc from bcd WHERE DATEDIFF(CURDATE(), timestamp) <= '"+days+"'");
Addition: It definitely is a Java error, I've just tried out this statement in Navicat (50s running time).
The query seems to freeze after the log tells me that it's now adding the result of position 543,858.
try {
...
ResultSet res = new ResultSet();
PreparedStatement stmt = new PreparedStatement(); // prepare statmenet etc.
stmt.setFetchSize(Integer.MIN_VALUE);
res = stmt.executeQuery();
...
System.out.println(res.getStatement());
...
while (res.next())
treeSet.add(res.getString("userid"));
} catch (Exception e) {
e.printStackTrace();
}
Edit: We were able to figure out the problem. This method is fine and the returned result (500,000 instead of 2,000,000) is right as well (looked up in the wrong db to verify the amount); the problem was, that the next method-call that used the result of the one posted above takes literally forever, but had no logging-implemented. So I've been fooled by missing console-logs.
Thanks anyways!
I think you might be running out of memory after processing half a million records. Try assigning more memory using command line options -Xmx etc. See here for more info about command line options.
In mysql to use streaming ResultSets you have to specify more parameters, not only fetchSize.
Try:
stmt = conn.createStatement('select ...', java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY);
stmt.setFetchSize(Integer.MIN_VALUE);
and see if that works.
It's documented in theĀ ResultSet section.
Strange that it doesn't throw exception, but this is the only suspect I have. Maybe it starts garbage collection/flushes memory to disk and it takes so much time it doesn't get to throw it.
I would try to add to your query " LIMIT 543857" and then " LIMIT 543857" and see what happens.
If the above does not help, use the limit directive combined with order by.
I suspect that there is invalid entry in your table and the way to find it is binary search.
I am trying to get all data from a table contained in a database, and parse it into a List. I have tried a few different techniques and can't get it working. I am trying to serialize the ResultSet so I can send it over a socket. The code I have got so far is:
public Object readJavaObject(String query, Connection con) throws Exception
{
PreparedStatement stmt = con.prepareStatement(query);
//stmt.setString(1, id);
query_results = stmt.executeQuery();
while (query_results.next())
{
dataObject = query_results.getObject(1);
}
query_results.close();
stmt.close();
return dataObject;
}
public void run()
{
try
{
Class.forName(dbClass);
con = DriverManager.getConnection (dbUrl, user, pass);
query = "SELECT * FROM Bench_table";
dataList = (List) this.readJavaObject(query, con);
con.close();
}
}
I'm not sure what code to write around readJavaObject. Can anyone help to point out where the issue is?
Thanks for any help.
Take a look at DbUtils (from apache-commons).
If you need a more complex solution for mapping sql results to java objects, take a look at object-relational mapping. In Java the standard is JPA (Java Persistence API), with Hibernate, EclipseLink and OpenJPA as the most famous implementors.
There are several issues with your code.
You loop through the results but
only return data from the last row.
Instead create a List in the
readJavaObject method and add an
element each pass through the loop.
Instead of getting the item in the first column, I assume you want all the data. You are going to have to be more specific with which "get" methods you use to pull data off the ResultSet. Possibly create a POJO to represent a row of data, then inside the loop populate it and store it in the list.
Assuming you use a POJO, you might want to use generics with your list to make it easier to get items out of it without having to cast.
I don't know exactly what you want to achieve, but can I introduce you to the DAO (Data Access Object) Pattern?
I recently discovered the CachedRowSet, which does exactly what OP needs. It notably allows to serialize a ResultSet, and to restore the original when needed.