Insert or update table using JDBC - java

I have some records to import. It's ok the first time when they are being inserted. If I try to import the same data again I receive a org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint. How can I update the records in the database if the data is the same/or changed and insert if it's new data using JDBC?
public void store(Object entity) throws Exception {
try {
if (this.updateEntity((XEntity) entity) == 0) {
this.insertEntity((XEntity) entity);
}
...
} catch (SQLException sqlEx) {
...
}
}
private int updateEntity(XEntity entity) throws SQLException {
PreparedStatement prepStmt = this.getUpdatePreparedStmt();
...
return prepStmt.executeUpdate();
}
private void insertEntity(XEntity entity) throws SQLException {
...
this.getInsertPreparedStmt().executeUpdate();
}
The problem is fixed now. I've provided an answer below.

You can try using postgres SQL 'MERGE' or 'REPLACE'

You can pass the UPDATE command as a string through JDBC.
According to this SO post, you will have to write 2 statements.

If you want to use the same method to insert and update your data, you'll need to check if the data exists first. The SQL command used to insert a new object is INSERT, whereas the one used to update an element is UPDATE. So, what you could do is do a SELECT to check if your data is already here, and then do an INSERT or UPDATE based on the results.
However, this is a workaround. You would really need to clarify your implementation, and make different methods whether you are adding or updating data. Business-side, these are clearly two very different functions, so one method for both seems to me like a design problem.

This test logic works.
if (this.updateEntity((XEntity) entity) == 0) {
this.insertEntity((XEntity) entity);
}
The problem was in the method that updated the record. The WHERE clause in the update prepared statement was using different data(data containing spaces) so updateEntity would always return 0. That was the reason why only inserts were made, instead of updates. Thank you very much for your help.

Related

Update by JPA native query

I am using spring boot repository with native query. While I am trying to update few fields by query and supply wrong values it is not throwing any exception.
here is my code,
#Modifying(clearAutomatically = true)
#Query(value = "update tbl_user set is_phone_Verified=true, mobile_verification_otp='XXX', updated_At=:updatedAt where " +
"phone_number=:registeredMobile and " +
"mobile_verification_otp=:phoneVerificationOtp", nativeQuery = true)
void updateMobileVerified(#Param("registeredMobile") String registeredMobile,
#Param("phoneVerificationOtp") String phoneVerificationOtp,
#Param("updatedAt") Date updatedAt);
Now, the issue is if I supply even wrong otp value, it is not throwing any exception. From service I am calling this method. and if there is no exception then I am returning true. Please find the service method.
#Override
public Boolean mobileVerification(String registeredMobile, String phoneVerificationOtp) {
try{
Date date = new Date();
userRepository.updateMobileVerified(registeredMobile,phoneVerificationOtp, date);
return true;
} catch(Exception e) {
return false;
}
}
Can someone please suggest me some way how can I determine if the update is successful.
Thanks.
If you would like to determine whether any rows were updated, the updateMobileVerified method can be defined to return int instead of void to indicate the number of rows updated
It seems that you are updating an unknown entity in the database. I highly recommend looking for a tbl_user entity by a unique id, like user_id or something like that. If the user doesn't exist, you either throw an exception or return false. In the case the given tbl_user was found, you basically update your desired attributes. This way, you force a strong control over updating users' data. I hope this might clarify your vision.

When I use insert, update(DML statements) in mybatis, how can I be sure that they are executed successfully?

I have seen two common ways
one checks return like this
int returnCount = userMapper.insert(user);
if (returnCount == 0) {
return "insert fails";
}
another way checks if function throws exception
try {
userMapper.insert(user);
} catch (Exception e) {
return "insert fails";
}
Actually, it also confused me when I study PrepareStatement executeUpdate method in JDBC.
In my view, if a method like that executed successfully, it returns a int value(no matter if it equals zero),or it will throw exception, but I'm not sure for that.
Thanks for the comment above, I just figured it out.I read some other blogs and thought about the comment carefully.Actually, success is a very subjective concept. A statement may execute normally(which means no exception thrown), it also can be unsuccess if the result does not meet our expectation(like updating a record that we thought existed but did not actually exist).That's why we check returns.

Should closing a resource always be a responsibility of the function that opens it?

public ResultSet executeQuery(String query) {
return session.call(query)
}
public List<Record> findRecords(String name) {
String query = prepareQuery(name);
return mapResultToRecord(executeQuery(query));
}
public List<Record> mapResultToRecord(ResultSet rs) {
try {
List<Record> list = new ArrayList<>();
while(rs.hasNext()) {
list.add(new Record(rs.next()));
}
} catch (Exception e) {
logger.warn("exception while iterating over the recall set", e);
} finally {
try {
rs.close();
} catch (Exception ex) {
logger.warn("exception while closing the result set", ex);
}
}
}
In the above code the ResultSet was opened by executeQuery but is being closed by mapResultToRecord. Is that the ideal place to close it or should the responsibility be taken up by the findRecords function instead?
My short answer is "When possible, Yes".
I think it makes more sense to close the ResultSet in findRecords . It has to be understood that executeQuery returns a resource that has to be closed. There's no way around that. Since it is findRecords that calls executeQuery, I think it's cleaner to have it be responsible for closing the ResultSet that comes back from doing so, in fitting with the idea behind your question.
A general rule is to have each function have a single purpose as much as possible. findRecords's purpose is to utilize a query to retrieve and process some data. mapResultToRecord's purpose is to create a list of records by pulling them from a ResultSet. This all reads very nicely:
public List<Record> findRecords(String name) {
String query = prepareQuery(name);
ResultSet resultSet = executeQuery(query);
List<Record> result = mapResultToRecord(resultSet);
resultSet.close();
return result;
}
Look at it the other way (the way you have it). findRecords's purpose it to prepare and execute a query, then hand it off to mapResultsToRecord for continued processing. mapResultsToRecord's purpose is to create a list of records from the results of a query made elsewhere, and then to dispose of the results of the query.
Doesn't the first story read a lot better than the second? Aren't the concerns better separated and defined by the first one than the second? Just my humble $.02 worth.
To further stress my point, I'd say that if you were going to leave it the way it is, you'd want to rename mapResultsToRecord to mapResultsToRecordAndCloseResultSet.
UPDATE: #JohnKugelman's great idea provides yet another reason that in findRecords is the place to close the ResultSet. Only in here does it make sense to use a try-with-resource block, to get this alternative:
public List<Record> findRecords(String name) throws SQLException {
String query = prepareQuery(name);
try (ResultSet resultSet = executeQuery(query)) {
return mapResultToRecord(resultSet);
}
}
This version is guaranteed to close the ResultSet, even if mapResultToRecord throws an exception. Note the throws SQLException on the method signature. This is necessary because, even though you don't see the <ResultSet>.close() call, it's there implicitly, and it can throw a SQLException.
If you open it, close it. Whenever different code is responsible for closing a resource than is responsible for opening it, it's very hard to make sure the resource gets closed in all cases. If the same code opens and closes, it can use try-with-resources to make sure things get closed.
It would be an improvement for the executeQuery method to pass the mapper in as a callback. That is what Spring-jdbc does, it defines an interface, RowMapper, with this method:
T mapRow(ResultSet rs, int rowNum) throws SQLException
which is passed into query methods of the JdbcTemplate. The mapper is responsible solely for using a ResultSet row to populate an object and is not involved in closing anything. If the mapper's responsibility is limited to mapping a single row, and we can pass in the rownumber, then the same mapper can be used for looking up an individual row and for getting multiple rows.
Meanwhile the query-executing code becomes very general purpose, the query and the mapper are the only parts specific to a particular query, and they are both passed in from outside.
The findRecords method should execute the query and call the mapper to get the results from within a try-block that closes the resultSet, maybe like this:
List<T> findRows(String queryName, RowMapper<T> mapper) throws SQLException {
List<T> list = new ArrayList<>();
String sql = prepareQuery(queryName);
try (ResultSet resultSet = session.call(sql);) {
for (int i = 1; resultSet.hasNext(); i += 1) {
list.add(mapper.mapRow(resultSet, i));
}
}
return list;
}
There isn't any detail given about what session.call does. It would probably be good for this code to be given the PreparedStatement as well so that it can make sure it gets closed.

how to overwrite using spring jdbctemplate batchupdate while inserting records?

Need to insert records using spring jdbctemplate batch update. while inserting if duplicate record is found, the record needs to be updated else inserted. how do i do that?
Below is my code.
Note:have not included exception handling.
result = jdbcTemplate.batchUpdate(
"insert ignore xxx set yy = ?, zz = ? where aa = ?",
new BatchPreparedStatementSetter() {
public void setValues(PreparedStatement ps, int i) throws SQLException {
ps.setDouble(1, Double.parseDouble(new JSONObject(jsonArray.get(i).toString()).get("aa").toString()));
ps.setDouble(2, Double.parseDouble(new JSONObject(jsonArray.get(i).toString()).get("bb").toString()));
ps.setString(3, new JSONObject(jsonArray.get(i).toString()).get("cc").toString());
}
public int getBatchSize() {
return jsonArray.length();
}
} );
}
You could use one of the following choices:
as #fbokovikov said you can use merge sql command.
you can check if the record is exist or not. In case of performance you can first get all the keys from db and then generate correct insert and update queries using those keys. In this scenario you should be aware of poor performance if you have big data in your table.
you can delete records first and then insert them all. This is very bad in performance. :-)
Hope that helps
Need to insert records using spring jdbctemplate batch update. while
inserting if duplicate record is found, the record needs to be updated
else inserted
For this purpose you should use standard SQL-2003 command merge.
In MySQL this command has following syntax:
INSERT...ON DUPLICATE KEY UPDATE

Get Primary Key Column from ResultSet Java

I am trying to get Primary Key Column(s) of table from ResultSet. Following are the Steps
I followed:
1. SQL QUERY: Select id,subid,email,empname from Employee
2. Executed this from java.sql.Statement and got the Results in ResultSet.
Here is the Interesting Part.
3. ResultSetMetadata rsmd = rs.getMetadata();
Now, if i watch this variable "rsmd" , it is showing primary key flags for relevant column names but I am not able to access it or get it into any variable.
I need help regarding the same.
NOTE: I do not want to use DatabaseMetadata and its getPrimaryKeys() function as it will take an additonal hit into External Database. Also, the ResultSetMetadata object is already having the primary key Information which i just need to fetch.
Some thoughts about your question and also a solution (hopefully helpful):
It didn't occur to me in my life time experience working with result sets having primary key information in the results set meta data.
It seems to me even strange because in principal a result set is not limited to show rows organized in columns of only one table and it is not forced to show all columns of one table and even the columns might be no table columns.
For example we might issue a query like
select X.a, X.b, Y.n, Y.m, 'bla'||X.c||'blub'||Y.l from X, Y;
In this case we may have or may not have primary columns from one or from both tables or from none of them.
As you already know the standard ResultSetMetaData-Interface doesn't provide primary key information access. What you see in the debugger is an instance of a class which implements that interface.
There are several ways to deal with your task:
(Not my preferred way)
Cast to the specific implementing ResultSetMetaData-class and access
primary key information if its available. But be aware that not every
ResultSetMetaData implementation provides this information.
(A bit more architectural approach, also not proposed from my side, but needed
if we deal with an incomplete JDBC-driver)
Take advantage of the system tables of the different databases you use
but hiding it of course in an abstraction, for example a bridge pattern.
Depending on the grants you have its normally not a big deal
(including testing up to 4 person days work for the base architecture part and ca. 1 person day for
each database system you want to access)
Then you get any desired meta data information from there including about foreign key relations.
(What I do)
Just use java.sql.DatabaseMetaData-class
It provides among others your desired primary key information for every accessible table.
Here a code-snippet (in Java 7):
public static Set<String> getPrimaryKeyColumnsForTable(Connection connection, String tableName) throws SQLException {
try(ResultSet pkColumns= connection.getMetaData().getPrimaryKeys(null,null,tableName);) {
SortedSet<String> pkColumnSet = new TreeSet<>();
while(pkColumns.next()) {
String pkColumnName = pkColumns.getString("COLUMN_NAME");
Integer pkPosition = pkColumns.getInt("KEY_SEQ");
out.println(""+pkColumnName+" is the "+pkPosition+". column of the primary key of the table "+tableName);
pkColumnSet.add(pkColumnName);
}
return pkColumnSet;
}
I have an idea to check whether a Column in table is Primary key or not using ResultSet.
In MySql JDBC driver, if you take a closer look, the real implementation class of java.sql.ResultSetMetaData would be com.mysql.jdbc.ResultSetMetaData class. This class provides a protected method to get information about each field
protected Field getField(int columnIndex) throws SQLException {
This method can give you the Field instance for every column index. Using the Field instance, you can get to the properties of the Field. To check whether it is a primary key, you can invoke
Field.isPrimaryKey()
Use FQN of com.mysql.jdbc.ResultSetMetaData in your type cast like ((com.mysql.jdbc.ResultSetMetaData) rsmd).getField(i).isPrimaryKey(). This is because you cannot import two class files with the same name and use them across the file
Please read the documentation of Field from MySql JDBC API to learn more about it. Hope this helps!
with #Keerthivasan Approach here is the complete working code. only problem was that answer cannot use the method like that as it is protected method.
here is the working code.
ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
int count = resultSetMetaData.getColumnCount();
for (int x = 1; x <= count; x++) {
Method method = null;
try {
method = com.mysql.jdbc.ResultSetMetaData.class.getDeclaredMethod("getField", int.class);
method.setAccessible(true);
com.mysql.jdbc.Field field = (com.mysql.jdbc.Field) method.invoke(resultSetMetaData, x);
if (field.isPrimaryKey()) {
System.out.println("-----------PK---------------------");
} else {
System.out.println("+++++++++++++++NPK++++++++++++++++++");
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
// find primary keys
try {
ResultSet rs = conn.getMetaData().getPrimaryKeys(null, conn.getSchema(), table);
while (rs.next()) {
System.out.println(rs.getString("COLUMN_NAME") + ":" + rs.getString("KEY_SEQ"));
}
} catch (SQLException e) {
e.printStackTrace();
}
This will work for all RDBMS, not just MSSQL

Categories