JDBC query RETURNING value - java

I have a problem trying to figure out how to get the id of the last inserted row using PostgreSQL and JDBC.
CREATE TABLE vet_care (
id serial PRIMARY KEY,
deworming int,
castration int
);
My query is
String insertVetCare = "INSERT INTO vet_care(castration,deworming) VALUES("+castrated+","+dewormed+") RETURNING id";
I want the id value (which is serial) for later use. I tried to execute the query like so:
int id = statement.executeUpdate(insertVetCare);
But this says after compilation, that "A result was returned when none was expected." and it does not insert the other values into table.
How can I get this to work?

If "id" means "generated identity key", then you've got it wrong.
The executeUpdate() method returns the number of affected rows, according to the javadocs.
You want it to return auto generated keys, like this.
More advice: Use PreparedStatement and bind values. Building up a SQL query that way is asking for a SQL injection attack.
// Why make the gc work? This query never changes.
private static final String INSERT_VET_CARE = "INSERT INTO vet_care(castration,deworming) VALUES(?, ?)";
PreparedStatement ps = connection.prepareStatement(INSERT_VET_CARE, Statement.RETURN_GENERATED_KEYS);
ps.setInt(1, castration);
ps.setInt(2, deworming);

Related

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

select scope_identity() using createSQLQuery in Hibernate

I am forced to use createSQLQuery to insert values into tables with an Identity column (the first column and the primary key) using hibernate. Using hibernate classes are not an option since the tables are created on the fly for each customer that is added to the system. I have run the query and it successfully inserts into the table. I then execute a "select scope_identity()" and it always returns null. "select ##Identity" works but that is not guaranteed to be the correct one. I have also tried to append "select scope_identity()" to the insert query. Then I tried query.list() and query.uniqueResult() both of which throw the hibernate exception of "No Results ..."
Session session = DatabaseEngine.getSessionFactory().openSession();
String queryString = "insert into table1 (dataid) values (1)"
SQLQuery query = session.createSQLQuery(insertQueryString);
query.executeUpdate();
query = session.createSQLQuery("select scope_identity()");
BigDecimal entryID = (BigDecimal)query.uniqueResult();
The simple example table is defined as follows:
"CREATE TABLE table1 (EntryID int identity(1,1) NOT NULL," +
"DataID int default 0 NOT NULL, " +
"PRIMARY KEY (EntryID))";
Is there a way I am missing to use scope_identity() with createSQLQuery?
Actually the SQLServerDialect class used by Hibernate uses the same "scope_identity()" too.
The reason why it's not working is because you need to execute those in the same statement or stored procedure.
If you execute the scope_identity() call in a separate statement, SQL Server will not be able to give you last inserted identity value.
You cannot do it with the SQLQuery, even Hibernate uses JDBC to accomplish this task. I wrote a test on GitHub to emulate this and it works like this:
Session session = entityManager.unwrap(Session.class);
final AtomicLong resultHolder = new AtomicLong();
session.doWork(connection -> {
try(PreparedStatement statement = connection.prepareStatement("INSERT INTO post VALUES (?) select scope_identity() ") ) {
statement.setString(1, "abc");
if ( !statement.execute() ) {
while ( !statement.getMoreResults() && statement.getUpdateCount() != -1 ) {
// do nothing until we hit the resultset
}
}
try (ResultSet rs = statement.getResultSet()) {
if(rs.next()) {
resultHolder.set(rs.getLong(1));
}
}
}
});
assertNotNull(resultHolder.get());
The code uses Java 8 lambdas instead of anonymous classes, but you can easily port it to Java 1.7 too.

How to avoid inserting null values into Primary key or Not Null columns?

I am inserting into a table from my jdbc program,
like this
PreparedStatement ps = con.prepareStatement(sqlqry);
ps.setInt(1,dto.getInstall_id());
ps.setString(2, dto.getDashboard_name());
ps.setString(3, dto.getDashboard_type());
ps.setString(4, dto.getDashboard_image());
But in the table i have column say D_ID which in is primary key and i dont want o insert the D_ID from my program into table because the same id might be already exist. So for avoiding the PK_CONSTRAINT I am not inseting it.
But when i try this i am getting this error.
ORA-01400: cannot insert NULL into ("TESTDB"."TESTATBLE"."D_ID")
So how can i solve this problem, Any alternative like if i insert D_ID from the program my JDBC program the D_ID column should dynamically generate id's in the table.
I am banging my head for this. Please help!
You should create that ID using a sequence. So for each ID column that you have, you create a corresponding sequence:
create table testatble
(
d_id integer not null primary key,
install_id integer not null,
dashboard_name varchar(100)
... more columns ....
);
create sequence seq_testatble_d_id;
You can use it like this:
// note that there is no placeholder for the D_ID column
// the value is taken directly from the sequence
String sqlqry =
"insert into testatble (d_id, install_id, dashboard_name) " +
"values (seq_testatble_d_id.nextval, ?, ?)";
PreparedStatement ps = con.prepareStatement(sqlqry);
ps.setInt(1,dto.getInstall_id());
ps.setString(2, dto.getDashboard_name());
... more parameters ...
ps.executeUpdate();
That way the id will be generated automatically.
If you need the generated ID in your Java code after the insert, you can use getGeneratedKeys() to return it:
// the second parameter tells the driver
// that you want the generated value for the column D_ID
PreparedStatement ps = con.prepareStatement(sqlqry, new String[]{"D_ID"});
// as before
ps.setInt(1,dto.getInstall_id());
ps.setString(2, dto.getDashboard_name());
... more parameters ...
ps.executeUpdate();
// now retrieve the generated ID
int d_id = -1;
ResultSet rs = ps.getGeneratedKeys();
if (rs.next()) // important!
{
d_id = rs.getInt(1);
}
rs.close();
More on sequences in the Oracle manual: http://docs.oracle.com/cd/E11882_01/server.112/e26088/pseudocolumns002.htm#SQLRF00253
You should use Auto Increment number for ID(I Oracle you can use sequence). You can do this at the link:
Create ID with auto increment on oracle
You should also read this. If there is a sequence to your ID then here you can read information about that.

Executing update query, with prepared statement

int selectie = toernooienUitvoer.getSelectedRow();
int selectiec = toernooienUitvoer.getSelectedColumn();
String primarykey = (String) toernooienUitvoer.getValueAt(selectie, 0).toString();
String waarde = toernooienUitvoer.getValueAt(selectie, selectiec).toString();
String columnaam = toernooienUitvoer.getModel().getColumnName(selectiec).toString();
String input = JOptionPane.showInputDialog("wijzig geselecteerde data", waarde);
toernooienUitvoer.setValueAt(input, selectie, selectiec);
PreparedStatement stat = con.prepareStatement("UPDATE fullhouse.toernooi SET ? = ? WHERE toernooi.T_code = ?");
stat.setString(1, columnaam);
stat.setString(2, input);
stat.setString(3, primarykey);
Guys, i know the query is correct, if i input the values. my guess my mistake is somewhere in the preparedstatement
i am getting a MySQLSyntaxErrorException:
As mentioned in other answer, the placeholder ? can only be used for values, not for table and column names. Since you are not reusing the PreparedStatement this is quite simple.
Change from
PreparedStatement stat = con.prepareStatement("UPDATE fullhouse.toernooi SET ? = ? WHERE toernooi.T_code = ?")
to
PreparedStatement stat = con.prepareStatement("UPDATE fullhouse.toernooi SET " + columnName + " = ? WHERE toernooi.T_code = ?")
And adjust the index parameter in the setString calls.
I don't think you can use place holder for dynamically passing the column name,your query should be:
"UPDATE fullhouse.toernooi SET colname = ? WHERE toernooi.T_code = ?"
When you use bind variables, it means the statement is precompiled and on the next executions, it will be faster. You are trying to make the name of the column to be a bind variable, which is not possible.
because you obviously need to update several different columns, in order to achieve some speed, you should declare several prepared statements, one for each column. Keep them in a HashMap<String, PreparedStatement>
The column name of a prepared statement cannot be dynamic because, depending on the column name, the query plan will be wildly different (e.g. sometimes table scan will be the fastest, sometimes using an index, sometimes something even more esoteric).
If SQL can't rely on a certain plan being the fastest, it needs to come up with a new one every time - which means there's no point in making a prepared statement which is why you can't do it.

SQLException when using a PreparedStatement for Derby DB

I have an SQL query that i am going to run using a PreparedStatement, and it is
UPDATE tbl_HitsCounter SET count = ? WHERE keyid = (SELECT id FROM tbl_HitsMaster WHERE sitename = '?')
Now when i set the 2nd paramater, which is a string value, i am getting a strange SQLException.
preparedStatement.setInt(1, 99);
preparedStatement.setString(2, masterKey);
As the setString() method is executed, i am getting an SQLException
The column position '2' is out of range. The number of columns for this ResultSet is '1'.
I have no idea what this is about, i havent even executed the executeUpdate() method.
There is only one placeholder in your SQL but you are trying to assign a value for the second. Your problem is that you have quoted the second placeholder, your SQL should look more like this:
UPDATE tbl_HitsCounter
SET count = ?
WHERE keyid = (
SELECT id
FROM tbl_HitsMaster
WHERE sitename = ?
)
Note the lack of quotes in sitename = ?. This is a placeholder: ?. This is an SQL question mark string literal: '?'.

Categories