How can update a JdbcRowSet object? - java

I want to execute the following SQL command with one JdbcRowSet object:
INSERT INTO Authors (FirstName,LastName) VALUES ('Sue', 'Smith')
I know that i can execute with Connection and Satements objects, but i want to do this with the interface JdbcRowSet, because one JdbcRowSet object is updatable and scrollable by default.
My code is:
public class JdbcRowSetTest
{
public static final String DATABASE_URL = "jdbc:mysql://localhost/books";
public static final String USERNAME = "Ezazel";
public static final String PASSWORD = "Ezazel";
public JdbcRowSetTest()
{
try
{
JdbcRowSet rowSet = new JdbcRowSetImpl();
rowSet.setUrl( DATABASE_URL );
rowSet.setUsername( USERNAME );
rowSet.setPassword( PASSWORD );
rowSet.setCommand( "INSERT INTO Authors (FirstName,LastName) VALUES ('Sue', 'Smith')" );
rowSet.execute();
}
catch( SQLException e )
{
}
}
public static void main( String[] args )
{
JdbcRowSetTest app = new JdbcRowSetTest ();
}
}
SQLException error:
java.sql.SQLException: Can not issue data manipulation statements with executeQuery().
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1086)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:989)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:975)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:920)
at com.mysql.jdbc.StatementImpl.checkForDml(StatementImpl.java:502)
at com.mysql.jdbc.PreparedStatement.executeQuery(PreparedStatement.java:2224)
at com.sun.rowset.JdbcRowSetImpl.execute(JdbcRowSetImpl.java:582)
at JdbcRowSetTest.(JdbcRowSetTest.java:23)
at JdbcRowSetTest.main(JdbcRowSetTest.java:53)

You can't use a JdbcRowSet to execute insert statements like that. If that is what you want, then you should use a normal Statement or PreparedStatement instead.
The RowSet.setCommand is for queries only:
Sets this RowSet object's command property to the given SQL query. This property is optional when a rowset gets its data from a data source that does not support commands, such as a spreadsheet.
Parameters:
cmd - the SQL query that will be used to get the data for this RowSet object; may be null
If you really want to use a row set, then you can update or insert new rows in the way documented for an updatable ResultSet:
Updating:
to update a column value in the current row. In a scrollable ResultSet
object, the cursor can be moved backwards and forwards, to an absolute
position, or to a position relative to the current row. The following
code fragment updates the NAME column in the fifth row of the
ResultSet object rs and then uses the method updateRow to update the
data source table from which rs was derived.
rs.absolute(5); // moves the cursor to the fifth row of rs
rs.updateString("NAME", "AINSWORTH"); // updates the
// NAME column of row 5 to be AINSWORTH
rs.updateRow(); // updates the row in the data source
For inserting:
to insert column values into the insert row. An updatable ResultSet
object has a special row associated with it that serves as a staging
area for building a row to be inserted. The following code fragment
moves the cursor to the insert row, builds a three-column row, and
inserts it into rs and into the data source table using the method
insertRow.
rs.moveToInsertRow(); // moves cursor to the insert row
rs.updateString(1, "AINSWORTH"); // updates the
// first column of the insert row to be AINSWORTH
rs.updateInt(2,35); // updates the second column to be 35
rs.updateBoolean(3, true); // updates the third column to true
rs.insertRow();
rs.moveToCurrentRow();
Note that the reference implementation of javax.sql.rowset in my experience regularly doesn't work as expected. You might be better off using plain JDBC.

You are missing execute():
To query using "Select" statement and return a JdbcRowSet:
public void createJdbcRowSet(String url, String username, String password, String sql) {
jdbcRs = new JdbcRowSetImpl();
jdbcRs.setCommand(sql);
jdbcRs.setUrl(url);
jdbcRs.setUsername(username);
jdbcRs.setPassword(password);
jdbcRs.execute();
// ...
}
Update:
Once you have the returned JdbcRowSet, you can insert a new row as this example shows:
public void updateJdbcRowSet(String username, String password) {
jdbcRs.moveToInsertRow();
jdbcRs.updateString("USERNAME", "NewUser");
jdbcRs.updateString("PASSWORD", "ENCRYPTED");
jdbcRs.insertRow();
}

public static void main(String[] args) {
try {
RowSetFactory rowset =RowSetProvider.newFactory();
JdbcRowSet jdbcrow=rowset.createJdbcRowSet();
jdbcrow.setUrl(BD_Url);
jdbcrow.setUsername(DB_User);
jdbcrow.setPassword(DB_password);
//this is your database connectivity like uername,password and url and
driver in not imp in mysql 5.1.23 jar file
jdbcrow.setCommand("Select * from Demo");
jdbcrow.execute();
//uppere parts is use to fetch the record from table
System.out.println("--------------------Insert----------");
jdbcrow.moveToInsertRow();
jdbcrow.updateInt("ID", 115);
jdbcrow.updateString("Username","Hitesh");
jdbcrow.updateString("Password","Sir");
jdbcrow.insertRow();
//The down code is used for update the value
System.out.println("=--------------------Update-------------------------");
jdbcrow.absolute(3);// 3rd row
jdbcrow.updateString("Password","Sirs" ); //colname password
jdbcrow.updateRow();
For Delete you can use
System.out.println("=-----------Delete-----------------------------");
while (jdbcrow.next()) {
String id=jdbcrow.getString("Id");
//here i getting ID record from my table
System.out.println("s" +id);
if(id.equals("102")) {
//then i my Specifying that if my table iD is .eqauls to 102 then delete
that record you also used this type to updatetbale
jdbcrow.deleteRow();
System.out.println("gaye");
break;
}
}
//if record delete then preform `enter code here`some operation
//this is used to delete last record from table
System.out.println("=---------------delete-------------------------");
jdbcrow.last();
jdbcrow.deleteRow();

Related

Inconsistent updates of jdbc ResultSet rows when updating one-to-many tables

I'm using SQL Server together with Microsoft jdbc driver to do some updates in Java.
My schema looks like following:
drop table edit
drop table editrow
create table edit (i int, data varchar(30))
create table editrow (i int, row int, data varchar(30))
insert into edit select 1, 'testedit'
insert into editrow select 1, 1, 'test1' union select 1, 2, 'test2'
select er.*
from edit e
inner join editrow er
ON er.i = e.i
I have two tables which are joined together by the above SELECT. I want to update columns by selecting the resultSet by using JDBC and ResultSet.updateXXX, but, for some reason I can only update columns in the first table, even though it's the table that gets "duplicated" in the SELECT. I can update both tables by using manual UPDATE in SQL Server, but I need to do it through Java.
There are no exceptions in any of the methods, but data doesn't get updated.
The code that fails to update looks something like this:
private static String getUrl()
{
StringBuilder connString = new StringBuilder()
.append("jdbc:sqlserver://")
.append("localhost")
.append(";databaseName=")
.append("test");
connString.append(";integratedSecurity=true");
connString.append(";lastUpdateCount=false");
connString.append(";selectMethod=direct");
connString.append(";responseBuffering=adaptive"); //allows streaming resultsets
return connString.toString();
}
...
public void static main(String[] args)
{
Connection conn = DriverManager.getConnection(getUrl());
createStatement = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
ResultSet rs = createStatement.executeQuery("select er.* from edit e inner join editrow er ON er.i = e.i");
//Now,move to row 1...
rs.first();
//Update data with some string
rs.updateString(3, "New row edit");
rs.updateRow();
}
The code that succeeds:
createStatement = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
rs = createStatement.executeQuery("select * from edit e inner join editrow er ON er.i = e.i");
//Now,move to row 1...
rs.first();
//Update data with some string
rs.updateString(2, "New head edit");
rs.updateRow();
The whole test file is here: https://pastebin.com/zrBperE5
Note that both tables can be updated by issuing TSQL updates on the subquery.
What am I doing wrong?

Why does H2 alias duplicate inserted rows?

I have created the following H2 alias:
CREATE ALIAS INSERT_CHANGE_RECORD AS '
java.sql.ResultSet insertChangeRecord(final java.sql.Connection conn) throws java.sql.SQLException
{
String sql = "insert into `change_records` (`made_when`, `made_by`) values (CURRENT_TIMESTAMP(9), ''admin'');";
java.sql.PreparedStatement ps = conn.prepareStatement(sql, java.sql.Statement.RETURN_GENERATED_KEYS);
ps.executeUpdate();
java.sql.ResultSet results = ps.getGeneratedKeys() ;
return results;
}
';
Weirdly enough, when I call the alias (once) using call INSERT_CHANGE_RECORD(); 3 identical records are created in the table, instead of one.
When I use the following definition of the alias instead (the difference is that I actually retrieve the generated ID from the resultset), only 1 row is inserted.
CREATE ALIAS IF NOT EXISTS INSERT_CHANGE_RECORD AS '
Long insertChangeRecord(final java.sql.Connection conn) throws java.sql.SQLException
{
String sql = "insert into `change_records` (`made_when`, `made_by`) values (CURRENT_TIMESTAMP(9), ''admin'');";
java.sql.PreparedStatement ps = conn.prepareStatement(sql, java.sql.Statement.RETURN_GENERATED_KEYS);
ps.executeUpdate();
java.sql.ResultSet results = ps.getGeneratedKeys();
results.next();
return results.getLong(1);
}
';
Is this a bug in H2, or is there any reasonable explanation for this behavior? I'm using H2 2.1.210.
Here is the DDL of the table that I insert into.
CREATE TABLE IF NOT EXISTS `change_records` (
`id` BIGINT NOT NULL AUTO_INCREMENT CONSTRAINT `change_records_id_pk` PRIMARY KEY,
`made_when` TIMESTAMP (9) WITH TIME ZONE NOT NULL,
`made_by` VARCHAR NOT NULL
);
Function aliases returning a ResultSet are called multiple times. You need to check URL of passed connection. If it is equal to jdbc:columnlist:connection, you need to return an empty ResultSet with properly configured columns, you can use org.h2.tools.SimpleResultSet or some other implementation. These invocations are performed during compilation or recompilation of the query before its actual execution.
String url = conn.getMetaData().getURL();
if (url.equals("jdbc:columnlist:connection")) {
SimpleResultSet rs = new SimpleResultSet();
// With some connection options "id" should be used instead
rs.addColumn("ID", Types.BIGINT, 19, 0);
return rs;
}
// main code
This result set must have columns with exactly the same names and data types as your function normally returns. Be careful with column names, `id` usually means "ID", but if ;DATABASE_TO_LOWER=TRUE or ;DATABASE_TO_UPPER=FALSE were specified in JDBC URL used by your application, it means "id". Your function shouldn't modify any data during this execution, it is only asked about its metadata.
If URL is different, it is a real function call during execution of a query or other command and you need to execute your code.
See also an example:
https://github.com/h2database/h2database/blob/45b609dec0e45125e6a93f85c9018d34551332a1/h2/src/test/org/h2/samples/Function.java#L140

How can I update the result set without removing the previous result from it

I am using jdbc to retrieve data from database. I have four different queries for that, however, the result of first query is used to get the data of second and fourth query. But, the resultset, gets updated as i run other queries. So is there any way that i can keep the resultset and add new results in it.
Here is my code:
class GetData{
String toDate;
String fromDate;
GetData(String d1,String d2) throws ClassNotFoundException, SQLException, ParseException, TransformerException, ParserConfigurationException {
toDate=d1;
fromDate=d2;
Connection connection= null;
ResultSet resultset= null;
String customerquery="SELECT o.ordernumber,o.orderdate,o.customernumber,c.customername,c.addressLine1,c.postalCode,c.city,c.country from orders o join customers c on o.customernumber=c.customernumber where orderdate between ? and ?";
String orderdetailquery="SELECT orderNumber,productCode,quantityOrdered,priceEach,orderLineNumber,(quantityOrdered * priceEach) as total FROM orderdetails where ordernumber=?";
String productsquery="SELECT productName,productLine,productVendor FROM products where productcode=?";
String employeequery="SELECT c.salesRepEmployeeNumber,e.firstname,e.lastname,o.officecode,o.city from customers c join employees e on c.salesRepEmployeeNumber = e.employeeNumber join offices o on e.officecode=o.officecode where c.customernumber=?";
Class.forName("com.mysql.cj.jdbc.Driver");
connection=DriverManager.getConnection("jdbc:mysql://localhost:3306/classicmodels","root","root");
if(d1!=null || d2!=null) {
PreparedStatement p1= connection.prepareStatement(customerquery);
p1.setString(1, toDate);
p1.setString(2,fromDate);
resultset= p1.executeQuery();
PreparedStatement p2= connection.prepareStatement(orderdetailquery);
while(resultset.next()) {
p2.setString(1, resultset.getString("orderNumber"));
}
resultset=p2.executeQuery();
PreparedStatement p3= connection.prepareStatement(productsquery);
while (resultset.next()) {
p3.setString(1, resultset.getString("productcode"));
}
resultset=p3.executeQuery();
PreparedStatement p4=connection.prepareStatement(employeequery);
while(resultset.next()) {
p4.setString(1, resultset.getString("customernumber"));
}
resultset=p4.executeQuery();
resultset.close();
connection.close();
}
}
I am trying to use the result of first query that contains the column customernumber to fetch the data. While, executing the code a error comes that customernumber column not found. So, how can I use the result of first query in other queries. Also, i am trying to get all the result of queries in one resultset as I am trying to create a xml out of it by using DOM.
You can put everything in a single query. using joins.
select o.ordernumber,o.orderdate
, o.customernumber,c.customername
, c.addressLine1,c.postalCode,c.city,c.country
, t1.productCode, t1.quantityOrdered, t1.priceEach, t1.orderLineNumber,(t1.quantityOrdered * t1. priceEach) as total
, t2.productName, t2.productLine, t2.productVendor
, c.salesRepEmployeeNumber,t4.firstname,t4.lastname,t3.officecode,t3.city
from orders o
join customers c on o.customernumber=c.customernumber
left join orderdetails t1 on t1.orderNumber = o.orderNumber
left join products t2 on t2.productCode = t1.productCode
left join offices t3 on t3.offiecode = c.customernumber
left join employees t4 on t3.officecode = t4.officecode
ResultSet is getting changed because you are using the same reference (object) every time you are executing the sql statement, hence it is overriding the old result, so if you want to deal with previously returned ResultSet, you can create new ResultSet instance to use, and also you can create a Bean class to set the elements and make it List type to keep on adding the results as per your logic.
Create a POJO to store required properties retrieved from different queries.
The resultset is getting updated because the same reference variable is being used to assign the resultset of subsequent queries.
If you want to use the same variable, you can follow like this -
create a POJO (which can be used to create a xml out of it by using DOM).
get result of first query in resultset.
populate relevant properties of POJO from this resultset.
re-use the resultset to store result of next queries.
When you need to use previous results, get them from the POJO to be used as parameters in subsequent queries.

Getting column metadata from jdbc/postgresql for newly created table

I'm trying to get the column list from newly created table(it is created in the java code).
The thing is that I do not get the columns.
The code works for tables that are already in the database, but if i create a new one and try to get the column info immediately it does not find any...
Update:
Here is full code that I used for testing:
#Test
public void testtest() throws Exception {
try (Connection conn = dataSource.getConnection()) {
String tableName = "Table_" + UUID.randomUUID().toString().replace("-", "");
try (Statement statement = conn.createStatement()) {
statement.executeUpdate(String.format("create table %s (id int primary key,name varchar(30));", tableName));
}
DatabaseMetaData metaData = conn.getMetaData();
try (ResultSet rs = metaData.getColumns(null, null, tableName, null)) {
int colsFound = 0;
while (rs.next()) {
colsFound++;
}
System.out.println(String.format("Found %s cols.", colsFound));
}
System.out.println(String.format("Autocommit is set to %s.", conn.getAutoCommit()));
}
}
The and the output:
Found 0 cols.
Autocommit is set to true.
The problem is with the case of your tablename:
String tableName = "Table_"
As that is an unquoted identifier (a good thing) the name is converted to lowercase when Postgres stores its name in the system catalog.
The DatabaseMetaData API calls are case sensitive ( "Table_" != "table_"), so you need to pass the lowercase tablename:
ResultSet rs = metaData.getColumns(null, null, tableName.toLowerCase(), null))
More details on how identifiers are using are in the manual: http://www.postgresql.org/docs/current/static/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
I have made simple test and it seems to work. I can create new table and show its columns using PostgreSQL JDBC (I use Jython):
conn = db.createStatement()
conn.execute("CREATE TABLE new_table (id SERIAL, txt VARCHAR(200))")
db_meta_data = db.getMetaData()
for tbl_name in ('date_test', 'new_table'):
print('\n-- %s --' % (tbl_name))
rs = db_meta_data.getColumns(None, None, tbl_name, None)
while (rs.next()):
print('%s:%s' % (rs.getString(3), rs.getString(4)))
conn.close()
This code shows columns for both already existing table: date_test and for just created new_table. I also added some code to close connection after CREATE TABLE but my results are always the same and correct.
Maybe it is problem with your JDBC driver. I use driver from postgresql-9.3-1100.jdbc41.jar.
It may be also problem with user permissions. Do you use the same user for both creating table and getting metadata? Is new table visible in psql, pgAdmin or other tool?
Other reason is that PostgreSQL uses transactions also for schema changes. So if you disabled default autocommit and closed connection your schema changes will be lost. Do you use db.setAutoCommit(false)?
You can also query PostgreSQL schema directly:
SELECT DISTINCT table_name, column_name
FROM information_schema.columns
WHERE table_schema='public'
AND table_name = 'new_table'
ORDER BY 1, 2
Strangely giving passing table name in lower case to getColumns method does work...thanks for the query MichaƂ Niklas it got me on the right track.

Why does getGeneratedKeys() return "GENERATED_KEY" as column name?

I'm playing with the JDBC/MySQL 5.1. I created an insert query to insert some data into a table and want to return the generated key from the newly created row. However, when I go to reference the column by "id" which is my PK and auto-increment column.
PreparedStatement ps = St0rm.getInstance().getDatabase("main")
.prepare("INSERT INTO quests (name,minlevel,start_npc,end_npc) VALUES(?,?,?,?)", true); // creates a prepared statement with flag RETURN_GENERATED_KEYS
// ...
int affected = ps.executeUpdate();
ResultSet keys = ps.getGeneratedKeys();
if (affected > 0 && keys.next()) {
St0rm.getInstance().getLogger().warning(String.format("ID Column Name: %s", keys.getMetaData().getColumnName(1))); // says the column name is: GENERATED_KEY
q = new Quest(keys.getInt(1)); // column index from the generated key, no error thrown.
q = new Quest(keys.getInt("id")); // actual column name, line throws a SQLException
// ...
}
So, my question: Why does ResultSet.getGeneratedKeys use GENERATED_KEY as the column name?
You shouldn't retrieve these columns by name. Only by index, since
there can only ever be one column with MySQL and auto_increments that
returns value(s) that can be exposed by Statement.getGeneratedKeys().
Currently the MySQL server doesn't return information directly that
would make the ability to retrieve these columns by name in an
efficient manner possible, which is why I'm marking this as "to be
fixed later", since we can, once the server returns the information in
a way that the driver can use.
From here (in 2006!).

Categories