JAVA/SQL - Best way to run this insert - java

I currently have a small database updater that'll insert/update a users IP address every time they enter the server. Since this is ran on an interval which empties a cache every 2 seconds, i really want to find a good quick method to update the table.
Here is what i currently use (MySQL only sadly :( )
INSERT INTO `iban_ipcache` VALUES (
'" + PlayerName + "',
'" + UpdatedTime + "', //This is just System.getCurrentMills
'" + PlayerIP + "')
ON DUPLICATE KEY UPDATE VALUES (
ip='" + PlayerIP + "',
time='" + System.currentTimeMills() + "';"
-- This was modifed so it can be read more easily, the query also uses ` instead of '--
The query works perfectly but.. Well i have to change the IP AND the time no matter what. So if the IP doesn't actually change, it'll still attempt to alter it. The time is used for both GUI reasons, and cross server updating reasons. So basically every time an old user logs in, the database will treat them like they just got a new IP.
My question is, is there any way to possibly check if BOTH the key and IP are the same without having to use Select?

Related

Replace a value in SQL instead of creating a second one with same name

sorry I'm a beginner (and French), I searched around the Internet but I did not find an answer.
Here's my problem (I'm working on Java) : I've a table with two columns (playername and id).
To add values I use this command :
INSERT INTO `database`.`table` (`playername`, `id`) VALUES ('" + player + "', '" + randomString + "')
player and randomString are variables. Every time the command is used by the player, a new (random) id is given, but instead of updating the id corresponding to the playername, it create a new row with the same playername.
What I want is to update the id if the playername is already in the table.
Would you help me ?
Okey so I found the solution.
I used INSERT ... ON DUPLICATE KEY UPDATE.
I thought that the 1st column was the primary key, but I had to set it with phpmyadmin, now it works !
Thanks for the help.

Parallel drop/create on postgres using DBA fails with: ERROR: tuple concurrently updated

We have N number of separate processes that can kick at the same time which all talk to the same postgres database. These "processes" are jdbc code which drop a user/schema and then recreate user/schema. We do this by connecting as a DBA user which is used across all processes. Each user/schema is unique so it's not as if all processes are attempting to drop the same. With that said 1 process will always succeed while the rest fail with:
ERROR: tuple concurrently updated
We are dropping with the following code:
String postgresRevokeUser = "REVOKE ALL ON DATABASE " + postgresDB + " FROM " + this.getUsername();
String postgresDropSchema = "DROP SCHEMA IF EXISTS " + this.getSchema() + " CASCADE";
String postgresDropUser = "DROP USER IF EXISTS " + this.getUsername();
connection = getDbaConnection();
connection.setAutoCommit(false);
statement = connection.createStatement();
statement.addBatch(postgresRevokeUser);
statement.addBatch(postgresDropSchema);
statement.addBatch(postgresDropUser);
statement.executeBatch();
connection.commit();
I can easily imagine what the issue is just don't know how to get around it (without putting in some synchronization between processes but that will be a fair amount of work). Any help would be great.

Test if a good query is being issued

It is quite usual to find myself writing unit tests against database calls and I always hit the same issue: How to validate if a good query is being sent to the database?
Example, I have this class that will send a final update to the database in the following form:
update credential set password_hash = ?, password_crypt = ?, password_plain = ? where id = ?
(this is a password migration tool, please dont mind the security issues with the password_plain field)
Writing the test class for this class, I have mocked the database access class (in this case I am using Spring JDBCTemplate) and captured the issued sql. After I have the sql, I do the following checks:
String space = "\\s+";
String optSpace = "\\s*";
String something = ".+";
String optSomething = ".*";
sql = sql.toLowerCase();
assertTrue(sql.matches(optSpace + "update" + space + "credential" + space + "set" + space + something));
assertTrue(sql.matches(something + space + "set" + space + optSomething + "password_hash" + optSpace + "=" + optSpace + "\\?" + something + "where" + something));
assertTrue(sql.matches(something + space + "set" + space + optSomething + "password_crypt" + optSpace + "=" + optSpace + "\\?" + something + "where" + something));
assertTrue(sql.matches(something + space + "set" + space + optSomething + "password_plain" + optSpace + "=" + optSpace + "\\?" + something + "where" + something));
assertTrue(sql.matches(something + space + "where" + space + optSomething + "id" + optSpace + "=" + optSpace + "\\?" + optSomething));
With those checks I am indeed validating if the issued SQL contains the most important parts of the update like:
correct table is being updated
all the 3 fields are being updated to values passed as parameters
the id is being used in the where statement, with its value as a parameter
I could simply validate if the issued query is exactly the expected query above, but that would make the test too restrict for future changes and would force a failure if any part of the query was changed, even if the update stays correct. As I think that tests are written to be used mostly in the future (when you are changing software and need more reassurance for that) and not in the present, this option would make the test kinda useless.
Well, finally, I declare my question: Which better options do we have for validating the issued SQL?
I see a lot of projects that create small embedded databases with a small amount of data for testing classes that deal with the database, but I wanted to write a more pure unit test alternative (if I can call that)
I don't think there's a good alternative to testing against a real database (even if it's embedded etc). At the moment you're testing that your SQL is syntactically valid, but will it actually work. e.g. do you know if it would violate constraints etc...
Mocking etc is all well and good but at some stage you have to test against the database. I would ensure that where possible you don't test against the database, and then bite the bullet and construct tests around a small database (with suitable rollback/rebuilds etc) to actually confirm correct db functionality.
I'd implore you to reconsider the benefits of these tests because
it is verifying implementation not behavior
when you change the SQL query in the future (or someone just adds a innocuous space by mistake), you'd have a failing test even if you have preserved behavior.
For the final DataAccessLayer, I'd recommend writing an integration test. One that runs against a real but minimal DB. Sure these tests would be slow but the confidence that they offer is worth it.
So write tests against GetCustomers() and verify the returned DTO contains the right data vs verifying that the SQL query you issued is X.
don't assert your sql. it's pointless. you will end up comparing passed sql string to another string (also created by you so there is no validation) or you'll have to implement your own database. instead just use existing one. check is query returned correct data or correctly changed data in database. use dbunit or sth similar.

What's wrong with trying to insert this dateTime from my Java application?

For this Java code:
stmt.addBatch(
"INSERT INTO Bills (BillDateTime, Table, Item, NoAttended, Service, Payment, Total) " +
"VALUES('" + billDateTime + "', " + Integer.parseInt(createTableNumberOutput.toString()) + ", '" + null + "', '"
+ Integer.parseInt(createGuestNumberOutput.toString()) + "', " + "5" + ", '" +
createPaymentTypeOutput.toString() + "', '" + "')");
I get the following error:
java.sql.BatchUpdateException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'Table, Item, NoAttended, Service, Payment, Total) VALUES('2012-03-26 11:15:8', 1' at line 1
The issue is not apparent to me, as MySql requires the format 'YYYY-MM-DD HH:MM:SS' for dateTime, which I have, right?
Table is reserved keyword in mysql . use backticks(`) around it.
Like below:
stmt.addBatch("INSERT INTO Bills (BillDateTime,`Table`, Item,NoAttended,Service,Payment,Total..........")
Even when you fix the reserved keyword table issue, it still won't work. You are trying the insert date.toString() in the date field. (when concatenated, toString() gets called all non-string objects)
Instead, you should use a PreparedStatement and call stm.setDate(..). Get more familiar with the prepared statement, as it is most often the better way to go. (it also has addBatch(), which works in a slightly different way - you set all parameters, then add, then set again.

Issue with a JPA query

I am trying to execute the following JPA query:
public static final String UPDATE_INVENTORY_CUSTOMER_FOR_AMS_MAPPING = "UPDATE Inventory inventory SET"
+ " inventory.customer.id = :" + DataAccessConstants.PARAM_CUSTOMER_ID
+ " ,inventory.lastUpdateUserId = :" + DataAccessConstants.PARAM_USER_ID
+ " where inventory.amsConsignorName = :" + DataAccessConstants.PARAM_AMS_CONSIGNOR_NAME
+ " and inventory.amsConsignorOrgCd = :" + DataAccessConstants.PARAM_AMS_CONSIGNOR_ORG_CD
+ " and inventory.amsConsignorTypeName = :" + DataAccessConstants.PARAM_AMS_CONSIGNOR_TYPE
+ " and inventory.status.code in (:" + DataAccessConstants.PARAM_STATUS + ")";
but it is seeing the following:
update ATL_INVENTORY, set CONSIGNOR_ID=?, LAST_UPDATE_USER_ID=? where AMS_CONSIGNOR_NAME=? and AMS_CONSIGNOR_ORG_CD=? and AMS_CONSIGNOR_TYPE_NAME=? and (CODE in (? , ? , ? , ?))
Any ideal as to why there is a comma after the table name?
Solution
I had to change the original query to the following:
update Inventory inv set "
+ "inv.customer.id = :" + DataAccessConstants.PARAM_CUSTOMER_ID + " "
+ "where inv.amsConsignorName =:" + DataAccessConstants.PARAM_AMS_CONSIGNOR_NAME + " "
+ "and inv.amsConsignorOrgCd =:" + DataAccessConstants.PARAM_AMS_CONSIGNOR_ORG_CD + " "
+ "and inv.amsConsignorTypeName =:" + DataAccessConstants.PARAM_AMS_CONSIGNOR_TYPE + " "
+ "and exists(select 1 from Code code where inv.status = code and code.code in (:" + DataAccessConstants.PARAM_STATUS + "))
Which then produced this:
update ATL_INVENTORY set CONSIGNOR_ID=? where AMS_CONSIGNOR_NAME=? and AMS_CONSIGNOR_ORG_CD=? and AMS_CONSIGNOR_TYPE_NAME=? and (exists (select 1 from ATL_CODE code1_ where ATL_INVENTORY.STATUS=CODE_ID and (code1_.CODE in (? , ? , ? , ?))))
Based on a clarification located here: Incorrect SQL generated for JPA QL Update statement involving multiple entities
Your query is code as UPDATE Inventory inventory SET, but the generated SQL says update ATL_INVENTORY, set. Why is the literal SQL string not what you coded? When I encounter mysteries like this, they're usually caused by assuming that one thing is being done when in fact another is in play.
This suggests that the SQL you coded isn't being used to generate that SQL the way you're assuming. See where else this query might be coming from. I'd bet that the real source has a misplaced comma in it.
Which JPA implementation are you using? If I'm incorrect about a bad assumption, it says that there's a bug in the implementation. Have you used it before? Have you had success with UPDATE? If yes, it's definitely buried somewhere in your code base.
You have an interface with a bunch of constants in it. Personally, I don't care for a design like that. It's an anti-pattern with a name.
A bug in the JPA provider is very unlikely in my opinion so, as #duffymo said, are you sure you're using the right constant, that the code or maybe dependencies are up-to-date? I'd dig in that direction.
That being said, I really wonder why you're not using named queries (that are most of time pre-compiled by the persistence implementation at deployment time), either in the Java code or in meta-data mapping files (the fun part is that people didn't find having EJB-QL queries externalized in XML very manageable in EJB 2.x, hence the #NamedQuery annotation of JPA).

Categories