Spring JDBC Data Driven Update - java

I have a Spring application with a update API endpoint for a Postgres database. The user can submit information and updates will be reflected in the database. The user only submits what they have to update. For example consider the following object:
class Dog {
String name;
int age;
String breed;
// Attributes and getters/setters...
}
When the user submits a update request, they only send the information they wish to update, such as only name and breed. I have the following function that updates the database with information:
public void update(String name, int age, String breed, JdbcTemplate template) {
UpdateBuilder query = new UpdateBuilder();
query.from("DogTable");
boolean updated = false;
if (name != null) {
query.set("name" + " = '" + name + "'");
updated = true;
}
if (age != null) {
query.set("age" + " = '" + age + "'");
updated = true;
}
if (breed != null) {
query.set("breed" + " = '" + breed + "'");
updated = true;
}
// And so on...
if (updated) {
query.set("UpdatedTime" + " = '" + new Date() + "'");
}
query.where("someKey" + " = '" + someId + "'");
template.update(query.toString());
}
(The query.set() stuff is just a helper class that builds a query string)
As you can see, this gets messy with all the "is the name given, is the age given?" checks. That leads to my question: Is there a data driven approach to do this? What I would like to be able to do is:
myJdbcTemplate.update(ListOfObjectsToUpdate, "TableName");
Simply, the JDBC template would see what I have provided it, and would proceed to update the provided table with that information. Is this even possible? I realize that building queries using strings is bad, but PreparedStatements don't look much better in code (not to mention, they don't solve this issue).

You can use the COALESCE function for this purpose - add user value as well and existing value and if the user value is not null (intended update) it well be set as the new value.
Similar to this -
UPDATE "user" SET alternate_contact_name = COALESCE(<user value>, alternate_contact_name)
This is a MySQL query but COALESCE works same way in Postgresql
The user value will be set and new value if it is not null. When it is null and original value of column is not null, the original value if picked. If both are null then it doesn't matter.
WIth this you can simply pass all parameters and avoid building query in an untidy way.

Related

How to compare LocalDateTime values in JPQL query?

It is necessary to select dataset using JPQL query with optional condition - comparing the field value (LocalDateTime type) with a user-specified parameter (also LocalDateTime type).
First I made a well working code:
return entityManager.createQuery(
"SELECT new com.******.*******.*******.****.models.dto.SomeDto " +
"(s.id, " +
"s.userId) " +
"s.persistDate) " +
"FROM Some s WHERE s.userId = :userId
AND s.persistDate >= :userDateTime", SomeDTO.class)
.setParameter("userId", userId)
.setParameter("userDateTime", userDateTime)
This code works but there is one problem:
this condition may exist or may not exist - dependent on app logic. Therefore, there is a need not to use injection using .setParameter (for this condition), but to form a string (which may be empty) depending on the logic and then add to the request:
String extraCondition = (userDateString.equals("false")) ? "" :
"AND s.persistDateTime >= " + userDateString;
return entityManager.createQuery(
"SELECT new com.******.*******.*******.****.models.dto.SomeDto " +
"(s.id, " +
"s.userId) " +
"s.persistDate) " +
"FROM Some s WHERE s.userId = :userId " + extraCondition, SomeDTO.class)
.setParameter("userId", userId)
But the problem is that no matter how I tried to format the userDateString variable, I get an Internal Server Error.I even tried using just a text string instead of variable (tried with different formatting):
String extraCondition = (userDateString.equals("false")) ? "" :
"AND s.persistDateTime >= 2023-01-27T23:30:50";
But the result is also bad - Internal Server Error.
I also tried using the .isAfter method instead of the ">=" operator, but that didn't help either.
How to inject LocalDateTime values comparing into query as String?
even if the date string may or may not be necesssary, you can (and should!) still use parameter injection, not formatted values.
Basically, your code should look like this:
String queryStr = ....;
boolean someCondition = <expensive_test_here>;
if(someCondition) {
queryStr += " AND s.persistDate >= :userDateTime";
}
Query q = em.createQuery(queryStr).setParameter("userId", userId);
if(someCondition) {
q.setParameter("userDateTime", userDateTime);
}

[mybatis]+ null check using StringTypeHandler not woking

I am trying to perform a null check for the lastName column in my user table using StringTypeHandler provided by MyBatis.
But when I am passing a null value for lastName, no null check, for lastName is being done by StringTypeHandler before updating the table.
I want to know the purpose of ***TypeHandler provided by MyBatis.
public String buildUserUpdateQuery(User user, Long userId) {
return new SQL() {
{
UPDATE(UserTable.NAME);
if (user.getFirstName() != null)
SET(ProgramTable.Column.FIRST_NAME + " = #{program.departmentId,jdbcType=INTEGER,typeHandler=org.apache.ibatis.type.IntegerTypeHandler}");
//if (program.getLastName() != null)
SET(ProgramTable.Column.LAST_NAME + " = #{program.programName,jdbcType=VARCHAR,typeHandler=org.apache.ibatis.type.StringTypeHandler}");
if (program.getMiddleName() != null)
SET(ProgramTable.Column.MIDDLE_NAME + " = #{program.programStatus,jdbcType=VARCHAR,typeHandler=org.apache.ibatis.type.StringTypeHandler}");
WHERE(ProgramTable.Column.USER_ID + " = #{userId}");
}
}.toString();
}
Looking at the official documentation of MyBatis typeHandlers:
Whenever MyBatis sets a parameter on a PreparedStatement or retrieves
a value from a ResultSet, a TypeHandler is used to retrieve the value
in a means appropriate to the Java type.
Therefore, TypeHandler are used to transform/map the data read from the database from a database type to its java corresponding type and vice-versa. The TypeHandler are not used to do any null checks.
Considering the example of the StringTypeHandler, it converts any data from a database column type CHAR or VARCHAR and converts it to java.lang.String and vice-versa.

How to test if the data entered through registration page of a web application is stored into database or not with Selenium?

I am new to Selenium. I am testing a user registration form in which i have to verify if the data is inserted into database or not. I am using following code to check. Is there any better option than this? I am first checking if registration ID is inserted and it it is then checking other details.
#Test
public void testVerifyListOfRecords() throws InterruptedException {
String query = "SELECT ID FROM usermaster WHERE RegistrationNumber = '" + REGNO + "' ";
String ID = DatabaseConnector.executeSQLQuery("QA", query);
if(ID == ""){
STATUS = "FALSE";
System.out.println("REGISTRATION NUMBER IS NOT INSERTED..!! TEST FAILED..!!");
} else STATUS = "TRUE";
String sqlQuery = "SELECT FirstName,LastName,SpecialityCode,QualificationCode FROM usermaster WHERE ID = '" + ID + "' ";
List<String> listOfDBValues = new ArrayList<String>();
listOfDBValues = DatabaseConnector.executeSQLQuery_List("QA", sqlQuery);
List<String> Branch = new ArrayList<>();
Branch.add(listOfDBValues.get(0));
List<String> list1 = new ArrayList<>(Arrays.asList("testfirstname testlastname ORP DM"));
Assert.assertEquals(STATUS, "TRUE");
System.out.println("STATUS IS " + STATUS);
Assert.assertEquals(Branch, list1);
}
This is not really a Selenium question... it's more of an automation and/or database question. Selenium only interacts with a browser. If you have no other way to verify if the user was registered and that is a requirement, then querying the database seems like the most straightforward way to verify it.
There are several things I would change to make this function better/more efficient.
You seem to be using a lot of global variables, e.g. STATUS, REGNO. This is generally not a good practice. Pass into the function what it needs.
Unless I'm missing something, you can be more efficient with your variable usage. You have a couple cases in this short function where you have a value already in a variable, store in it another variable, and then assert it's value. There's no need to put it into a new variable, just assert it's value from the initial variable. This minimizes lines of code and gets rid of unneeded variables which makes the code easier to read and understand.
Pick the best data type for each variable. For example, you are storing "TRUE"/"FALSE" in a String when you should be using boolean. Don't create a List<String> and only add one item... just use a String variable.
Assert early and often. You don't assert the value of STATUS until the end of the function but you knew the value of STATUS in the first few lines of code and you hit the database afterwards. You should have asserted the value of STATUS right when you got it to fail the test as soon as possible and avoid extra hits to the db, additional code, etc.
You have several sysouts that are redundant with your asserts. There's no need to sysout the value of STATUS if it's right after an assert that would fail if it's anything but "TRUE".
Asserts have an optional message parameter. Use it. Add a comment on what the assert is testing, e.g. "Verifying ID is not empty" so that when it fails one day you will know from the logs what it was verifying. Make it descriptive and useful so that if all you had was that message, you would know what it was testing and possibly why it might have failed.
Here's how I would clean this up
#Test
public void testVerifyListOfRecords(String regNo) throws InterruptedException
{
String query = "SELECT ID FROM usermaster WHERE RegistrationNumber = '" + regNo + "' ";
String ID = DatabaseConnector.executeSQLQuery("QA", query);
Assert.assertFalse(ID == "", "Verify ID is not empty");
String sqlQuery = "SELECT FirstName,LastName,SpecialityCode,QualificationCode FROM usermaster WHERE ID = '" + ID + "' ";
List<String> listOfDBValues = DatabaseConnector.executeSQLQuery_List("QA", sqlQuery);
List<String> list1 = new ArrayList<>(Arrays.asList("testfirstname testlastname ORP DM"));
Assert.assertEquals(listOfDBValues.get(0), list1, "Verify user registration data is correct");
}

Retrieving a database value and assigning to a string value

I am making a app that incorporates login/register functionalities and I'm making a issue that I have been trying to solve.
When a user logins and the login is successful, I'd like to use the email that they signed in with to pass to the next activity using Intent (I checked that the email is in fact getting passed by displaying what is being passed through the intent) and then passing that email to a function in the Dbhelper that uses that email to look for the name of the person that signed in then displaying "Welcome (name of person)" in the current activity but I keep getting a null returned in the function which ultimately leads to the app crashing.
Here is where I'm calling the function in the activity where I want to display the name.
if(!session.loggedIn())
{
Logout();
}
else
{
Intent in = getIntent();
String email = in.getStringExtra("email");
Name.setText("Welcome " + db.findName(email));
}
And this is the function in my DbHelper.java where I'm looking for the name with a query and such.
public String findName(String user_email)
{
String query = "SELECT " + COLUMN_NAME + " FROM " + USER_TABLE + " WHERE " + COLUMN_EMAIL + " = " + "'" + user_email + "'";
SQLiteDatabase db = this.getWritableDatabase();
//reads for database
Cursor c = db.rawQuery(query, null);
c.moveToFirst();
if(c.getCount() > 0) // if cursor is not empty
{
String n = c.getString(0);
return n;
}
else
{
return null;
}
}
As you can see, it's returning null. And yes there is entries in the database already. Also, I tried just passing the email to the function and returning what was passed and it still gave me an error.
Normally, to check for a value in a text column, you do not use the equal = sign, but rather WHERE Column LIKE '%text%'. Also, when saving to a database you should escape and "sanitize" strings. If you did this, then you should also be doing the same process when looking for them, else you won't find them.
I am telling you this since, even if you are sure there are entries in your table, the result of the query may be empty. You could just debug by printing the result of the c.getCount() call or something.

Android SQLite specific table row deletion

I have some trouble removing specific users from my SQLite database on my Android device. I made a simple method to delete a table row where table.name equals first input and table.surname equals second input.
Here is my method:
void deleteUser(db_operations opt, String name, String surname) {
SQLiteDatabase sdb = opt.getWritableDatabase();
if(validate(name, surname) == true) {
name = name.replaceAll("\\s+",""); surname = surname.replaceAll("\\s+","");
try {
String DELETE_USER = "DELETE FROM " + tb_users.tb_name + " WHERE " + tb_users.name + "='" + name + "' AND " + tb_users.surname + "='" + surname + "'";
sdb.execSQL(DELETE_USER);
sdb.close();
System.out.println("Deletion SUCCESS!");
} catch (SQLException e) {
System.out.println("Deletion FAILED!");
}
}
}
If I execute a DELETE FROM myTableName statement, every user is removed from the table and from my ScrollView which is ok, but if I execute the above method to remove a specific user, output gives:
Deletion SUCCESS!
but my table still has the record. The record also remains in my ScrollView list (made with LinearLayouts). The list is built dynamically. I've already checked if the data is good or not before my SQLite execution starts and it looks ok. I can't figure out why my method doesn't work. Maybe I've missed something.
(Posted on behalf of the question author).
I got it working. I found out that my validation method was returning false output all the time (I made a typo in one of the conditions), that's why my method never got a chance to execute. Thanks for a reminder to use logs. Was able to track my typo.

Categories

Resources