I am trying to retrieve the generated key for an insert statement to an Oracle database. I'm using a PreparedStatementSetter with the jdbcTemplate. I've read the other StackOverflow post on this topic here, but the answer basically proposes moving away from StatementSetters. Is there a way to retrieve the key while using a PreparedStatementSetter? The documentation on using PreparedStatementSetters to retrieve keys is surprisingly sparse.
Here's my jdbcTemplate update statement:
statementSetter.setUpdatedBy(...);
statementSetter.setFileStatus(...);
statementSetter.setCompany(...);
int modifiedCount = jdbcTemplate.update(sql, statementSetter);
Here's my SQL string:
database.insertFileControlRecordQuery = INSERT INTO MY_TABLE \
(PRIMARY_KEY_FIELD, FIELD2, FIELD3,...) VALUES (MY_TABLE_SEQ.NEXTVAL, ?,?, ...)
Using a NEXTVAL as suggested in comments is potentially hazardous: if some other process does an insert and commit in between you fetching the NEXTVAL and committing your insert, you'll have a different key than what you assumed.
Instead, I would suggest using the update-function which has provisions for returning a key, i.e. ...
JdbcTemplate.update(PreparedStatementCreator, KeyHolder);
... from which you can determine the key.
An example to illustrate:
String insertQuery="INSERT INTO MY_TABLE (FIELD1, FIELD2) " +
"VALUES (?, ?)";
KeyHolder keyHolder = new GeneratedKeyHolder();
PreparedStatementCreator preparedStatementCreator = connection -> {
PreparedStatement preparedStatement = connection.prepareStatement(insertQuery, new String[]{"PRIMARY_KEY_FIELD"});
preparedStatement.setString(1, field1);
preparedStatement.setDate(2, field2);
return preparedStatement;
};
jdbcTemplate.update(preparedStatementCreator, keyHolder);
Number primaryKey = keyHolder.getKey();
Note that the setting of the column which holds the primary key upon creating the PreparedStatement is essential for Oracle! Without it, you'll be getting the ROWID instead.
Related
When I try to sort by a value descending my SQL table does it correctly, but if it sees for example "1000" it always puts it in the middle?
for Example:
this even happens when I reference it in spigot (I'm using it for a plugin) it outputs it the same way
this is how I'm calling it in my plugin:
PreparedStatement statement = database.getConnection().prepareStatement("SELECT uuid FROM player_stats ORDER BY blocks_broken DESC");
ResultSet rs = statement.executeQuery();
while (rs.next()) {
String name = rs.getString("uuid");
LeaderboardCommand.name = name;
String player = String.valueOf(Bukkit.getPlayer(UUID.fromString(name)));
p.sendMessage(player);
I know it's not perfect as I'm just learning/experimenting with databases currently, but I'm mainly asking for help on why the SQL is outputted this way & advice on any severe mistakes I'm making is greatly appreciated!
Thanks in advance -Occy
public void createPlayerStats(PlayerStats playerStats) throws SQLException {
PreparedStatement statement = getConnection()
.prepareStatement("INSERT INTO player_stats(uuid, blocks_broken, last_login, last_logout) VALUES (?, ?, ?, ?)");
statement.setString(1, playerStats.getPlayerUUID());
statement.setLong(2, playerStats.getBlocksBroken());
statement.setDate(3, new Date(playerStats.getLastLogin().getTime()));
statement.setDate(4, new Date(playerStats.getLastLogout().getTime()));
statement.executeUpdate();
statement.close();
It happens because block_broken type is a varchar and not a number.
In this case you are ordering lexycographically and not numerically.
You can change your query to handle that as a numeric value with an explicit cast so your query should be:
SELECT uuid FROM player_stats ORDER BY cast(blocks_broken as numeric) DESC
Update: In MariaDb try to use this (You can try directly in the db client and once it is working update your java code):
SELECT uuid FROM player_stats ORDER BY CAST(blocks_broken AS INTEGER) DESC
My insert query returns the UUID of the record that was inserted. Below is the relevant code.
KeyHolder keyHolder = new GeneratedKeyHolder();
template.update(connection -> {
PreparedStatement ps = connection.prepareStatement(insertQuery);
ps.setString(1, stateName);
ps.setString(2, stateAb);
ps.setObject(3, propertyDetailsObject);
return ps;
}, keyHolder);
I'm using a long CTE query that ends with select id from tmpproperty limit 1; I have shortened things here for easier readability.
But I get the following exception
org.springframework.dao.DataIntegrityViolationException:
PreparedStatementCallback; A result was returned when none was
expected.; nested exception is org.postgresql.util.PSQLException: A
result was returned when none was expected.
I found the above example online and was expecting it to work. Any idea what I should be doing differently?
Need to change to:
PreparedStatement ps = connection.prepareStatement(youSQL, new String[]{"id"});
where id is the primary key column name
And remove select id from tmpproperty limit 1;, update query will be enough.
I want to make use of spring JdbcTemplate to insert a line and return the id autogenerated by the mysql db.
Without spring I'd do similar as follows:
String sql = "INSERT INTO mytable (id, filename, timestamp) VALUES (NULL, ?, NOW())";
Statement st = con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
st.setString("test.csv");
st.executeUpdate();
st.getGeneratedKeys().next().getLong(1);
Question: how could I achive the same with JdbcTemplate?
In short its
Number key = jdbcInsert.executeAndReturnKey(new MapSqlParameterSource(
parameters));
You can check my answer in identity from sql insert via jdbctemplate
How can I use a prepared statement to delete entries from a database? I have found that I must write the following code
String deleteSQL = "DELETE DBUSER WHERE USER_ID = ?
but I want to specify a clause with more than one variable. I have used the AND operator but it doesn't seem to work.
Here is an example if your syntax is not correct..
DELETE DBUSER WHERE USER_ID = ? and USER_NAME = ?;
you can append more conditions in where clause by using more AND ... operators.
OR if you have more than one USER_IDs to delete in a single query..
DELETE DBUSER WHERE USER_ID in (?, ?, ?, ?);
It's must work/ for example
Select from Employee e where e.ID < ? and e.ID >= ? order by e.ID
to set values use this:
int id1 = 1;
int id2 = 10;
preparedStatement.setInt(2, id1);
preparedStatement.setInt(1, id2);
for delete I use this code:
public synchronized boolean deleteNewsById(Integer[] idList)
throws NewsManagerException {
DatabaseConnection connection = pool.getConnection();
StringBuffer buffer = new StringBuffer();
buffer.append("(");
buffer.append(idList[0]);
for (int i = 1; i < idList.length; i++) {
buffer.append(",");
buffer.append(idList[i]);
}
buffer.append(")");
PreparedStatement statement = connection
.getPreparedStatement(DELETE_NEWS_BY_ID + buffer);
}
and sql query looks like this
private static final String DELETE_NEWS_BY_ID = "delete from NEWS where ID in ";
or simple write delete from NEWS where ID in (?,?,?) and set values like in first example
I think the response from Aleksei Bulgak is correct, but to perhaps more straightforwardly word it...you can set your parameters like this:
String stmt = "DELETE DBUSER WHERE USER_ID = ? and (USER_NAME = ? or USER_NAME = ?)";
preparedStatement.setInt(1, firstParam);
preparedStatement.setString(2, secondParam);
preparedStatement.setString(3, thirdParam);
...and for however many parameters(question marks) in your SQL (no matter if you're using IN or whatever you want), you should set that many parameters here(using setInt for ints, setString for Strings, etc). This goes for select and delete queries.
Are you looking for the IN operator which allows you to specify multiple values in the WHERE clause such as in my example.
String deleteSQL = "DELETE DBUSER WHERE USER_ID IN (?)"
Though in PreparedStatement IN clause alternatives there are some useful answers and links that you may want to take a look at such as Batch Statements in JDBC which discuss the pros and cons of different batching approaches. The IN approach I'm suggesting is part of that discussion. The end result is that you make just one trip to the database, rather than one per delete and that's better performing because of the reduced network activity required.
I use a SQLite Database and a Java GUI. The information entered on the GUI will be added to a table in the database. This table contains an autoincrement. On the same time I want to display the information on the GUI and change it later.
creating the database:
stat.executeUpdate("create table t1(ROWID INTEGER PRIMARY KEY AUTOINCREMENT, Value);";
adding values to the database:
Statement stat = con.createStatement();
String sql = "insert into t1 values ($next_id,'"+value+');";
stat.executeUpdate(sql);
How can I save the ID in my program so that it will be the same as in the database and I have easy access to my database?
Edit:
I tried the solution mentioned in the comments and run into a NYI exception ...
String sql = "insert into t1 values($next_id,'"+value+"');";
PreparedStatement stmt = con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
ResultSet res = stmt.getGeneratedKeys();
while (res.next()){
int id = res.getInt(1);
System.out.println(id);
}
con.commit();
tried also the solution of Russel and got another exception ("not implemented by SQLite JDBC driver"):
String sql = "insert into t1 values($next_id,'"+value+"');";
Statement stat = con.createStatement();
stat.executeUpdate(sql);
stat.executeUpdate("SELECT LAST_INSERT_ROWID() from t1;", Statement.RETURN_GENERATED_KEYS);
ResultSet res = stat.getGeneratedKeys();
while (res.next()){
int id = res.getInt(1);
System.out.println(id);
}
What did I wrong?
What about just calling SELECT LAST_INSERT_ID()? This returns the last auto-increment value generated for your connection (not affected by other clients' actions).
Alternately, looking at the Javadoc, it seems you should be able to do this with an ordinary Statement:
stat.executeUpdate(sql, Statement.RETURN_GENERATED_KEYS);
ResultSet keys = stat.getGeneratedKeys();
If you don't mind using an ORM library, try sormula. It will do all of the work for identity column for you. All that is required is #Column(identity=true) annotation on the POJO field that is to be auto incremented.
The test cases in org.sormula.tests.identity package shows you how. Sqlite test set up and sqlitejdbc-v056.jar jar is included. Change build.properties to run db.dir=sqlitejdbc.