Update by JPA native query - java

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.

Related

How to return useful information in JPA custom query whose return value is not 1?

I have several custom queries in an interface that extends JpaRepository. The interface is analogous to the below (note: the below is just for illustration, and may have errors, but don't concern yourself with that).
public interface MyRepo extends JpaRepository<SMark, String> {
#Transactional
#Modifying
#Query(value = "INSERT INTO my_table(id, col_1, col_2) " +
"values(:col_1, :col_2))", nativeQuery = true)
int insertRecord(#Param("col_1") String col_1, #Param("col_2") String col_2);
So, my issue is that I am finding it difficult to do anything useful with the int return type for anything other than a successful query (which will return a 1). Is there a way to do anything useful with the return other than sending the value as part of the response? In other words, if the response is not a 1, and an exception is not thrown, can the non-1 response be translated into something more informative to the user?
For example, I am currently doing the following, which I would like to improve upon if I was a confident about the not-1 status:
if(status == 1) {
StatusResponse statusResponse = new StatusResponse("Successful Delete ", null);
return new ResponseEntity<>(statusResponse, HttpStatus.OK);
}
else {
StatusResponse statusResponse = new StatusResponse("Delete not successful (lacking details from response) ", null);
return new ResponseEntity<>(statusResponse, HttpStatus.NOT_ACCEPTABLE);
}
Grateful for any response. Thanks!
I would not recommend the approach of using return type as valid operation or not. I would prefer having database level constraint like unique constraint, check constraint or trigger check for insert/update/delete.
Nevertheless, we can use default method inside interface and throw database exception and in Spring you can configure ExceptionHandler to wrap the exception and return with some valid error code ?
public interface MyRepo extends JpaRepository<SMark, String> {
#Transactional
#Modifying
#Query(value = "INSERT INTO my_table(id, col_1, col_2) " +
"values(:col_1, :col_2))", nativeQuery = true)
int insertRecord(#Param("col_1") String col_1, #Param("col_2") String col_2);
default void insert(String col1, String col2) {
if (insertRecord(col1, col2) != 1) {
throw new DataIntegrityViolationException("Unable to perform DML operation.");
}
}
}
Since the return value of this methods indicates the number of modified objects (in your can it can only be 0 or 1) I would just use it to translate it into a more understandable response.
The simplest case is a REST Api:
When the Method returns 1 the POST/PUT/PATCH call was successful (if there was no other error) and you would return 201 Created and maybe a little more like a Location header.
And when it returns 0 it means no object got modified and because it's an insert this shouldn't happen and therefore you would return 500 InternalServerError
But I would say in your case this is redundant because as I said it's an insert , you only have those two return options and I guess if something doesn't work during the insert you already get an exception by spring boot and therefore when you called the method without getting an error I would say it was successful.
But ofcourse you can double check to be sure and maybe even use it for tests to enforce the expected behavior or something else.

Filter for duplicate values.

I am trying to add a filter to check for duplicate values that a user might input. I am not sure where I am going going wrong in my query.
My query doesnot enter the loop to check if the name already exists.
I am fairly new to google-could. If someone can tell me on how I can fix my problem or if there is a better solution.
else if ( commandEls[0].equals( "add_director" ) ) {
String name = commandEls[1];
String gender = commandEls[2];
String date_of_birth = commandEls[3];
boolean duplicate = false;
//add a director record with the given fields to the datastore, don't forget to check for duplicates
Entity addDirectorEntity = new Entity("Director");
// check if the entity already exits
// if !duplicate add, else "Already exisits"
Query directorExists = new Query("Movies");
// Director Name is the primary key
directorExists.addFilter("directorName",Query.FilterOperator.EQUAL, name);
System.out.print(name);
PreparedQuery preparedDirectorQuery = datastore.prepare(directorExists);
System.out.print("outside");
for(Entity directorResult : preparedDirectorQuery.asIterable()){
// result already exists in the database
String dName = (String) directorResult.getProperty(name);
System.out.print(dName);
System.out.print("finish");
duplicate = true;
}
if(!duplicate){
addDirectorEntity.setProperty("directorName",name);
addDirectorEntity.setProperty("directorGender",gender);
addDirectorEntity.setProperty("directorDOB",date_of_birth);
try{
datastore.put(addDirectorEntity);
results = "Command executed successfully!";
}
catch(Exception e){
results = "Error";
}
}
else {
results = "Director already exists!";
}
}
Non-ancestor queries (like the one in your example) are eventually consistent, so they cannot reliably detect duplicate property values. Ancestor queries are fully consistent, but they require structuring your data using entity groups, and that comes at the cost of write throughput.
If the directorName property in your example is truly unique, you could use it as the name in the key of your Director entities. Then, when you are inserting a new Director entity, you can first check if it already exists (inside of a transaction).
There's no general, built-in way in Datastore to ensure the uniqueness of a property value. This related feature request contains discussion of some possible strategies for approximating a uniqueness constraint.
I'd also recommend reading up on queries and consistency in the Datastore.
That is a valid thing to do but i figured out my problem.
I am making an Entity for Director where as That should be for movies.

How to process ResultSet you know has only one record in it

I'm struggling with a homework assignment and am getting hung up on some SQL queries.
My query is interrogating an inventory database for the quantity of some item. The query requests the column with the name quantity_in_stock from the table, given the primary key.
I have initialized some prepared statements. This is the one I'm using here:
stmtFindColumn = Database.getConnection().prepareStatement(String.format("select ? from %s where %s = ?",
INVENTORY_TABLE_NAME, SKU) );
Now a separate method is called. I pass it a static const QTY_IN_STOCK, which is defined as "quantity_in_stock" and the item's SKU number, which is the primary key in the table.
private int getIntegerFromTable(String column, String key) {
int toReturn = 0;
try {
// Complete the prepared statement
stmtFindColumn.setString(1, column);
stmtFindColumn.setString(2, key);
ResultSet result = stmtFindColumn.executeQuery();
toReturn = result.getInt(column);
} catch (SQLException e) {
LOG.error(e.getMessage());
e.printStackTrace();
}
return toReturn;
}
When I run the query I get an sql exception that tells me: Invalid column name quantity_in_stock.
I have tried using a while loop processing result.next() and get the same error. I can't find any examples of how to properly get the results when you know only a single record is being returned.
Help!
EDIT: OK, I've found that part of my problem is I'm not getting a result set, where I should expect one. Any ideas?
UPDATE: I've tested my code, using a garden variety statement and a plain string query instead and it works just fine. So the problem is in my use of the prepared statement. Can someone check if I'm using the ? wildcards correctly? Thanks!
as far as i know, the column name may not be a parameter ...
DarkSquirrel42 is right -- you can't replace the column list of the select using a ? parameter marker. Instead, you can String.format that into place too, for example.
bad:
*select ? from INVENTORY_TABLE_NAME where SKU = ?
good:
select QUANTITY_IN_STOCK from INVENTORY_TABLE_NAME where SKU = ?

AliasToBeanResultTransformer and Hibernate SQLQuery

I have a rather complex query (too many nested levels for either HQL or Criteria queries), so I've written it as a SQLQuery. I'd really like to use the AliasToBeanResultTransformer to transform my results into a List, but I'm having some issues. I've included code snippets below of what I've got right now.
When I log the results for the transformed query, I can see that the transformer does create a List, however, all the fields in each AdvancedClauseSearchResultDTO are null. I assume that means that I'm doing something wrong with aliasing ... that the AliasToBeanResultTransformer can't find the correct setters to call. However, the AdvancedClauseSearchResultDTO class does have public setters for each of the columns that I've aliased in my sql string. If this was a Criteria query, I'd use projections to define an alias for each column to be returned, but I'm unsure of how to accomplish the same thing using a SQLQuery.
Any advice on how to get the aliases set so that the ResultTransformer can use them? I've seen some limited documentation that suggested that using the 'as aliasName' method should work, but it doesn't seem to be for me.
Beginning snippet of query string definition, note the 'as' alias definitions
StringBuffer clauseBaseQuery = new StringBuffer();
clauseBaseQuery.append("select ");
clauseBaseQuery.append(" clauseDetail.clause_detail_id as clauseDetailId,");
clauseBaseQuery.append(" clauseDetail.clause_id as clauseId,");
clauseBaseQuery.append(" providers.provider_name as provider, ");
clauseBaseQuery.append(" products.product_name as product, ");
SQLQuery creation & setting of resultTransformer
Query query = session.createSQLQuery(clauseBaseQuery.toString());
query.setResultTransformer(new AdvancedClauseSearchResultTransformer());
return (List<AdvancedClauseSearchResultDTO>)query.list();
AdvancedClauseSearchResultTransformer class (uses AliasToBeanResultTransformer and then does some extra processing):
class AdvancedClauseSearchResultTransformer implements ResultTransformer {
//Use the aliasTransformer to do most of the work
ResultTransformer aliasTransformer = Transformers.aliasToBean(AdvancedClauseSearchResultDTO.class);
#Override
public List transformList(List list) {
log.debug("transforming CLAUSE results");
List<AdvancedClauseSearchResultDTO> result = aliasTransformer.transformList(list);
//for each row, set the status field
for (AdvancedClauseSearchResultDTO dto : result) {
log.debug("dto = " + dto);
String status = null;
Date effectiveDate = dto.getEffectiveDate();
Date terminationDate = dto.getTerminationDate();
Date now = new Date(System.currentTimeMillis());
if (now.before(effectiveDate)) {
status = "Pending";
} else if (now.after(terminationDate)) {
status = "Terminated";
} else {
status = "Active";
}
dto.setStatus(status);
if (StringUtils.isNotEmpty(dto.getReasonForAmendment())){
dto.setAmended(Boolean.TRUE);
}else{
dto.setAmended(Boolean.FALSE);
}
}
return result;
}
#Override
public Object transformTuple(Object[] os, String[] strings) {
Object result = aliasTransformer.transformTuple(os, strings);
return result;
}
}
This depends on the backend you're using, which you don't mention in your post.
Various DB backends use case insensitive naming for the columns unless you properly escape them, so they end being retrieved as CLAUSEDETAILID or clausedetailid, even when you specify the column result name with the proper case.
With PostgreSQL (and I believe Oracle, too), you have to write your query like this (note the column quoting):
StringBuffer clauseBaseQuery = new StringBuffer();
clauseBaseQuery.append("select ");
clauseBaseQuery.append(" clauseDetail.clause_detail_id as \"clauseDetailId\",");
clauseBaseQuery.append(" clauseDetail.clause_id as \"clauseId\",");
clauseBaseQuery.append(" providers.provider_name as \"provider\", ");
clauseBaseQuery.append(" products.product_name as \"product\", ");
Query query = session.createSQLQuery(clauseBaseQuery.toString());
So that will allow Hibernate to properly recognize the property and map the result to a bean, provided you also specfied the tranformation:
query.setResultTransformer(Transformers.aliasToBean(AdvancedClauseSearchResultDTO.class));
as was suggested by #zinan.yumak.
I did some more research on this today, and finally noticed a good stack trace of the underlying error I was getting, and a hibernate forum entry that helped me get past this.
The Exception I was getting is:
Caused by: org.hibernate.PropertyNotFoundException: Could not find setter for CLAUSEDETAILID
It appears as if Hibernate is taking my camel case aliases & turning them into all uppercase, so it can't find the matching setters in my AdvancedClauseSearchResultDTO class.
Here's the forum entry that pointed me in the right direction:
https://forum.hibernate.org/viewtopic.php?f=1&t=1001608
I ended up using the approach detailed in that post for my own ResultTransformer, and that's working for me.
I think it is not a good method to write a result transformer to solve
your problem. Try something like this,
Query query = session.createSQLQuery(clauseBaseQuery.toString());
query.setResultTransformer(Transformers.aliasToBean(AdvancedClauseSearchResultDTO.class));
And in AdvancedClauseSearchResultDTO class, modify setter methods to set required
fields for you. For example,
class AdvancedClauseSearchResultDTO {
private Date effectiveDate;
private String status;
.
.
public void getEffectiveDate() {
return effectiveDate;
}
public void setEffectiveDate(Date aDate) {
Date now = new Date(System.currentTimeMillis());
if (now.before(effectiveDate)) {
this.status = "Pending";
} else if (now.after(terminationDate)) {
this.status = "Terminated";
} else {
this.status = "Active";
}
this.effectiveDate = aDate;
}
}
You got the idea...
The easiest fix is to put quotation marks for the column alias like:
select first_name as "firstName" from employee

Insert or update table using JDBC

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.

Categories