How to get the ID of inserted row in JDBC with old driver? - sql

I want to get auto increment id of inserted row. I know that there is a lot of examples how to do that:
link1
link2
But I use HSQL 1.8.0.10 and following code:
PreparedStatement ps = conn.prepareStatement("insert into dupa (v1) values(3)", Statement.RETURN_GENERATED_KEYS);
throws expection:
java.sql.SQLException: This function is not supported
How to get id if driver does not support the above solution. Is any other way to get auto increment key of inserted row? I want to handle as much as possible drivers. So want to use obove code in try section and use another way in catch section.
Second question: Is possible that database does not support this feature. So even if I use new driver and old database It will still not work? I tried to use hsql 2.3.2 driver but I can not to connect to 1.8.0.10 database.

The following code illustrates how to retrieve generated keys from HSQLDB 2.2.9 and later using the included JDBC 4 driver. This method returns a two element long[]. The first element contains the number of rows that were updated; the second contains the generated key if any:
static final long[] doUpdate( ... ) {
final long[] k = new long[] {0, KEY_UNDEFINED};
PreparedStatement ps = null;
try {
ps = CXN.get().prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
JdbcValue jv;
for (int i = 0; i < data.size(); i++) {
jv = data.get(i);
jv.type().setJdbcParameter(i + 1, ps, jv);
}
k[NUM_OF_ROWS] = (long) ps.executeUpdate();
if (k[NUM_OF_ROWS] > 0L) {
try (ResultSet rs = ps.getGeneratedKeys()) {
final String identColName = idCol.colName();
while (rs.next()) {
if (k[ROW_CREATED] != KEY_UNDEFINED) throw new AssertionError();
k[ROW_CREATED] = rs.getLong(identColName);
}
}
}
} catch (SQLException e) { ... }
finally {
try { if (ps != null) ps.close(); } catch (SQLException e) { }
}
return k;
}
I am unable to say whether this approach will work with old versions of HSQLDB.

You will have to use some vendor-specific solution, i.e. in mysql you would call LAST_INSERT_ID function.
I don't have valid installation of HSQL to test it, but you can give a try to the highest voted solution from this topic: how to return last inserted (auto incremented) row id in HSQL?

Related

Select Count(*) Query using Dapper in .Net Core API returns incorrect value

I'm trying to do a select count query in Sql Server using Dapper. The expected response should be 0 when a profile does not exist. When I do the query in SSMS it returns correctly, but in the API using Dapper it returns 1. Any idea why this is happening?
public IActionResult GetProfileCount(string profileId)
{
int profileCount = 0;
using (IDbConnection db = new SqlConnection(connectionString))
{
try
{
profileCount = db.Query($"select count(*) from Profile where Id='{profileId}'").Count();
}
catch(Exception ex)
{
Console.WriteLine($"Error retrieving count for ProfileId: {profileId}", ex.Message);
}
}
return Ok(profileCount);
}
I see you added your own answer but could I recommend not doing it that way. When you do
profileCount = db.Query($"select * from Profile where Id='{profileId}'").Count();
What you are actually doing is selecting every field from the database, pulling it into your C# application, and then counting how many results you got back. Then you are binning all that data you got back, very inefficient!
Change it to this :
profileCount = db.QueryFirst<int>($"select count(*) from Profile where Id = #profileId", new { profileId })");
Instead you are selecting an "int" from the result set, which just so happens to be your count(*). Perfect!
More on querying in Dapper here : https://dotnetcoretutorials.com/2019/08/05/dapper-in-net-core-part-2-dapper-query-basics/
Also notice that (similar to the other answer), I am using parameterized queries. I also heavily recommend this as it protects you from SQL Injection. Your initial example is very vulnerable!
You can read a little more about SQL Injection in C#/MSSQL here https://dotnetcoretutorials.com/2017/10/11/owasp-top-10-asp-net-core-sql-injection/ But just know that Dapper protects you from it as long as you use the inbuilt helpers to add parameters to your queries.
Another option is use the method ExecuteScalar for "select count" queries:
profileCount = db.ExecuteScalar<int>("select count(*) from Profile where Id=#profileId", new { profileId });
Ref.: https://www.learndapper.com/selecting-scalar-values
Try and change your query to the following:
db.Query($"select count(*) from Profile where Id = #ProfileId", new { ProfileId = profileId }).Count()
I figured it out. The .Count() is counting the rows of the result, which is going to be 1 because the result is one row displaying the number 0. I switched my code to this and it works now.
public IActionResult GetProfileCount(string profileId)
{
int profileCount = 0;
using (IDbConnection db = new SqlConnection(connectionString))
{
try
{
profileCount = db.Query($"select * from Profile where Id='{profileId}'").Count();
}
catch(Exception ex)
{
Console.WriteLine($"Error retrieving count for ProfileId: {profileId}", ex.Message);
}
}
return Ok(profileCount);
}

Extract Data from a Data table to a text file in JPA

I'm working on data manipulation and my order is to extract all the content of a table into a text file ! I have already implemented this but apprently, I have commited a mistake while creating the request :
public void extractDonneesFichierPlat(){
TypedQuery<NotificationCnavOP> query = entityManager.createQuery("SELECT * INTO OUTFILE 'C:\\Test.txt' FROM NotificationCnavOP", NotificationCnavOP.class);
}
You cannot use "select * into outfile..." syntax in JPQL which is a separate query language different than SQL. If you want to execute native SQL queries in JPA you must use entityManager.createNativeQuery() method.
But still it is not possible to execute such 'bulk manipulation' query using this method.
The only solution I can think of is to use JDBC instead of JPA and execute:
Object o = entityManager.getDelegate();
System.out.println(o.getClass()); //prints the object class
// in my case it is org.hibernate.internal.SessionImpl
SessionImpl s = (SessionImpl)o;
Connection c = s.connection();
try {
Statement stmt = c.createStatement();
stmt.executeQuery("SELECT * INTO OUTFILE 'C:/Test.txt' FROM NotificationCnavOP");
} catch (SQLException e) {
e.printStackTrace();
}
The actual implementation of Session depends on the provider. In the case of Hibernate it is org.hibernate.internal.SessionImpl.
For newer versions of Hibernate it is org.hibernate.Session and it doesn't have the connection() method. So, you should try:
Object o = entityManager.getDelegate();
Session s = (Session)o;
s.doWork(connection -> {
try {
Statement stmt = connection.createStatement();
stmt.executeQuery("SELECT * INTO OUTFILE 'C:/Test2.txt' FROM NotificationCnavOP");
} catch (SQLException e) {
e.printStackTrace();
}
});
Both versions work in Hibernate 4.

Apache DBUtils - Why need resultsethandler for Insert?

I run an insert statement using Apache DBUtils. However, I am not sure why I have to include ResultSetHandler for this case:
String theQuery = QueryGenerator.insertintoStats();
ResultSetHandler<Object> dummyHandler = new ResultSetHandler<Object>() {
#Override
public Object handle(ResultSet rs) throws SQLException
{
return null;
}
};
try
{
queryRunner.insert(connection, theQuery, dummyHandler, Constants.UUIDSTR.toString(), name, prevbackupTime,
curbackupTime, updStartTime, delStartTime, bkupType.toString(), rowCount);
}
catch (SQLException e)
{
LOGGER.info(theQuery.toString());
LOGGER.error("Caught exception!", e);
}
Similar's the case for insertbatch which does use ResultSetHandler. I have resorted to use batch call for batch queries. Can anyone explain why we would be needing resultset handler for insert?
From documentation https://commons.apache.org/proper/commons-dbutils/apidocs/:
public <T> T insert(String sql,
ResultSetHandler<T> rsh,
Object... params)
throws SQLException
rsh - The handler used to create the result object from the ResultSet
of auto-generated keys.
If you insert values in a table which generate id upon insertion, you can retrieve it back, for example see this answer how to do this manually : https://stackoverflow.com/a/1915197/947111
You need ResultSetHandler<T> rsh to iterate over ResultSet which returned with id's which has been created.

Dapper.Net and the DataReader

I have a very strange error with dapper:
there is already an open DataReader associated with this Command
which must be closed first
But I don't use DataReader! I just call select query on my server application and take first result:
//How I run query:
public static T SelectVersion(IDbTransaction transaction = null)
{
return DbHelper.DataBase.Connection.Query<T>("SELECT * FROM [VersionLog] WHERE [Version] = (SELECT MAX([Version]) FROM [VersionLog])", null, transaction, commandTimeout: DbHelper.CommandTimeout).FirstOrDefault();
}
//And how I call this method:
public Response Upload(CommitRequest message) //It is calling on server from client
{
//Prepearing data from CommitRequest
using (var tr = DbHelper.DataBase.Connection.BeginTransaction(IsolationLevel.Serializable))
{
int v = SelectQueries<VersionLog>.SelectVersion(tr) != null ? SelectQueries<VersionLog>.SelectVersion(tr).Version : 0; //Call my query here
int newVersion = v + 1; //update version
//Saving changes from CommitRequest to db
//Updated version saving to base too, maybe it is problem?
return new Response
{
Message = String.Empty,
ServerBaseVersion = versionLog.Version,
};
}
}
}
And most sadly that this exception appearing in random time, I think what problem in concurrent access to server from two clients.
Please help.
This some times happens if the model and database schema are not matching and an exception is being raised inside Dapper.
If you really want to get into this, best way is to include dapper source in your project and debug.

JDBC select query returns wrong values

I was performing JDBC select query in my web service to return some values from my database. Part of this table is attached to this question. After performing following query:
SELECT * FROM uses WHERE uses_user_fk='22';
I receive only one row, but in database are two values that meet the query conditions, as you can see in attached picture. Can anyone tell me where I made a mistake. I’m using following JDBC instruction to execute the query
ResultSet tempResultSet = statement.executeQuery(query);
Bellow image of database table uses:
Below the compete method that query the database, argument query is the same as listed earlier “SELECT * FROM uses…”. I should add that the answer for that query is 4, I also try this query without using quotes (uses_user_fk=22) but the result was the same:
protected ArrayList<Integer> queryForIds(String query, String column) throws Exception {
ArrayList<Integer> ids = new ArrayList<>();
try {
Class.forName("com.mysql.jdbc.Driver");
connect = DriverManager
.getConnection(GeneralDatabaseConstants.DATABASE_CONNECTION_URL);
statement = connect.createStatement();
ResultSet tempResultSet = statement.executeQuery(query);
if (tempResultSet.next())
ids.add(new Integer(tempResultSet.getInt(column)));
} catch (Exception e) {
throw e;
} finally {
close();
}
return ids;
}
replace
if (tempResultSet.next())
with
while (tempResultSet.next())