I am having issues with MySQL's SELECT .. FOR UPDATE, here is the query I am trying to run:
SQL = "SELECT * " +
"FROM " + TableName + " " +
"WHERE out_status IN ("+outSStatus+") AND queued <= NOW() " +
"ORDER BY out_status, id_queue ASC "+ limitSql+
"FOR UPDATE";
After this, the thread will do an UPDATE and change the out_status to 99, which is then it should unlock the row.
I am running a multi-threaded java application, so 3 threads are running this SQL statement, but when thread 1 runs this, it doesn't lock (hide) its results from thread 2 & 3. Therefore threads 2 & 3 are getting the same results.
Also each thread is on its own mysql connection.
Can anybody please help me with this? OR perhaps have a better solution?
Much Appreciated.
You can use transactions
I had a similar issue. Both answers, from #Vikash and #Adeel Ansari, are valid suggestions. However, I solved the issue by using InnoDB engine instead of MyISAM. MyISAM only allows locking on table level, not on row level. You still need transactions, when using InnoDB.
It might be working, and you are unable to notice. Say thread 1 executed the statement, and committed the transaction automatically, because auto-commit was on. Then of course thread 2 would be able to run that too.
Try setting auto-commit off, using this method of Connection class and then see the result.
Related
I have to write a sql update trigger statement for Apache Derby. I am usually working with Sql Server and T-SQL. But now I have to use Derby. Unfortunately I am very new to Derby and I couldn't find a proper solution in the Derby manual.
My Problem is that I have to check for a condition in the update trigger and based on the result of this condition I would do either an UPDATE or an INSERT, so in T-SQL I would use an IF-ELSE-condition. Can somebody tell me what the equivalent is in Derby or an alternative way? I already considered the WHEN-clause, but this seems the wrong direction.
I have following code till now:
CREATE TRIGGER UPDATE_EVENTS
AFTER UPDATE
ON ACCIDENTS
REFERENCING OLD AS oldRow NEW AS newRow
FOR EACH ROW MODE DB2SQL
-- In the following, I would usually use an IF-ELSE Statement,
-- but I can't use this in Derby. So I tried the optional WHEN Statement,
-- but there I could not have an else "path", right?
-- This should be the If-Case
WHEN((SELECT COUNT(*) FROM VIEW_EVENTS WHERE ID_DATE = newRow.ID_DATE) > 0)
UPDATE VIEW_EVENTS
SET DETAILS = newRow.DETAILS,
PARTICIPANTS = newRow.PARTICIPANTS
WHERE ID_DATE = newRow.ID_DATE
-- And this should be the else case
WHEN((SELECT COUNT(*) FROM VIEW_EVENTS WHERE ID_DATE = newRow.ID_DATE) <= 0)
INSERT INTO VIEW_EVENTS
( ID_KEY,
ID_DATE,
DETAILS,
PARTICIPANTS
)
VALUES
( newRow.ID_KEY,
newRow.ID_DATE,
newRow.DETAILS,
newRow.PARTICIPANTS
);
This Statement is just a mini example to show you my problem. I hope you can help me :).
Best regards,
Yalcin
Do not tag indiscriminately. Your question has nothing to do with sql server.
But it seems that your goal is not directly achievable - as has been discussed (did you search?) here. Derby does not support multi-statement triggers. It seems that you need to use multiple triggers.
I am trying to execute an SQL query into Hibernate because of the complexity of it. To do so, I am using the following method:
session.createSQLQuery(sSql).list();
And the SQL query is:
String sSql = "select timestamp, value, space_name, dp_id, dp_description from "+sTable+
" inner join space_datapoint on id = dp_id and timestamp between "+
" (select max(timestamp)-30 day from "+sTable+") and (select max(timestamp) day from "+sTable+")"+
" order by space_name";
The SQL query tries to retrieve a set of values by means of cross references between multiple table/views. The result is a list of objects (different fields from the tables). I have tested the query in the SQL manager of the database and it works. However, when I run it inside the Hibernate framework, it takes a lot of time (I had to stopped the debugger after some minutes, whereas it should take over 5 seconds according to the tests). Do you know what could be the mistake? Or a possible solution?
Thanks a lot in advance,
I am trying to update a Db2 database using Java and the following code:
String sSqlString = "UPDATE P6DEVCDB00.P6OSTAPF SET STATVAL = '" + sStatVal + "' WHERE OPIID = '" + sOperationsitemid + "' AND CONGRPC = '" + sConfigGrpCode + "'";
// Do your select on a Db table.
//statement = con.createStatement();
statement = con.prepareStatement(sSqlString);
int RowsAffected = statement.executeUpdate();
con.commit();
System.out.println(RowsAffected);
I then get the following error :
DB2 SQL Error: SQLCODE=-7008, SQLSTATE=55019, SQLERRMC=P6OSTAPF ;
P6DEVCDB00;3, DRIVER=3.58.81
I have printed out the sql that it's going to run :
UPDATE P6DEVCDB00.P6OSTAPF SET STATVAL = 'ON'
WHERE OPIID = 'B20120707000681531' AND CONGRPC = 'STKLSTSTAT
When I run this sql directly with a SQLUI tool it works and the record gets updated...
Your problem is that you're attempting to use transactions over tables that are not 'journaled' - that is, setup for transactions.
Ideally, you should set up all tables (that will be run under a transaction) as journaled, specifically to test that property; regardless of being able to simulate failures, you need to make sure that your code can handle being under transactions.
Also, depending on your situation, you may not need to explicitly manage transactions. If you're using a framework like Spring, they can usually manage transactions for you, although this will usually mean that you still need journaling on your iSeries tables.
If you're just trying to test basic code behavior, look into using an in-memory database, such as HSQLDB (can emulate some LUW DB2 behavior, but not library lists, unfortunately) - this will absolve you of the need to have a connection to your box, and to set up journaling.
I see there are two ways to create update query in Hibernate. First you can go with the standard approach where we have hql like:
Query q = session.createQuery("update" + LogsBean.class.getName() + " LogsBean " + "set LogsBean.jobId= :jobId where LogsBean.jobId= :oldValue ");
q.setLong("jobId", jobId);
q.setLong("oldValue", 0);
return q.executeUpdate();
or we can go and run
getHibernateTemplate.saveorupdate(jobId);
Now am getting java.lang.IllegalArgumentException: node to traverse cannot be null! on running first query and am not sure hwo to provide condition in getHibernateTemplate example, i want to update jobIds in log table whose value matches 0 and so i want to run something like
Update logs set jobId = 23 where jobId = 0
Above is the simple sql query that I am trying to run but I want to run this via hibernate, tried couple ways but it is not working, any suggestions?
Update:
As noted by Jeff, issue was not having space after update and so that issue got resolved but still values are not updated, i have updated show_sql true for hibernate and checking what could be the cause of the issue, will be running query generated by hibernate to run again db and see if records are updated.
Just a few things that might help you to resolve this:
What does .executeUpdate() return, 0 (as it did not update any
rows)?
Does it throw a HibernateException that you are
silently catching or rethrowing?
Which FlushMode do you have configured?
Does the update get to the DB? You could switch on the query log for your DB server.
I work at a gaming cybercafe, and we've got a system here (smartlaunch) which keeps track of game licenses. I've written a program which interfaces with this system (actually, with it's backend MySQL database). The program is meant to be run on a client PC and (1) query the database to select an unused license from the pool available, then (2) mark this license as in use by the client PC.
The problem is, I've got a concurrency bug. The program is meant to be launched simultaneously on multiple machines, and when this happens, some machines often try and acquire the same license. I think that this is because steps (1) and (2) are not synchronised, i.e. one program determines that license #5 is available and selects it, but before it can mark #5 as in use another copy of the program on another PC tries to grab that same license.
I've tried to solve this problem by using transactions and table locking, but it doesn't seem to make any difference - Am I doing this right? Here follows the code in question:
public LicenseKey Acquire() throws SmartLaunchException, SQLException {
Connection conn = SmartLaunchDB.getConnection();
int PCID = SmartLaunchDB.getCurrentPCID();
conn.createStatement().execute("LOCK TABLE `licensekeys` WRITE");
String sql = "SELECT * FROM `licensekeys` WHERE `InUseByPC` = 0 AND LicenseSetupID = ? ORDER BY `ID` DESC LIMIT 1";
PreparedStatement statement = conn.prepareStatement(sql);
statement.setInt(1, this.id);
ResultSet results = statement.executeQuery();
if (results.next()) {
int licenseID = results.getInt("ID");
sql = "UPDATE `licensekeys` SET `InUseByPC` = ? WHERE `ID` = ?";
statement = conn.prepareStatement(sql);
statement.setInt(1, PCID);
statement.setInt(2, licenseID);
statement.executeUpdate();
statement.close();
conn.commit();
conn.createStatement().execute("UNLOCK TABLES");
return new LicenseKey(results.getInt("ID"), this, results.getString("LicenseKey"), results.getInt("LicenseKeyType"));
} else {
throw new SmartLaunchException("All licenses of type " + this.name + "are in use");
}
}
You must do two things:
Wrap your code in a transaction (to avoid autocommit releasing locks immediately)
Use SELECT ... FOR UPDATE and mysql will give you the lock you need (released on commit)
SELECT ... FOR UPDATE is better than LOCK TABLE as it can possibly get by with row-level locking, instead of automatically locking the whole table
According to the online manual, the correct syntax for locking is:
LOCK TABLES ...
and you have
LOCK TABLE ...
but you don't have any error checking. Hence you're probably failing to get the lock and it's silently ignoring that.
FWIW, I'd put your cleanup code (UNLOCK TABLES, conn.commit(), etc) in a finally block to ensure that you always clean up properly in the event of an exception.
As it is, you appear to be potentially leaking database connection handles, and never releasing the lock if there's no free license.
I would like to suggest just doing an update statement and checking how many rows where updated. i will write it out in psudo code.
int uniqueId = SmartLaunchDB.getCurrentPCID();;
int updatedRows = execute('UPDATE `licensekeys` SET `InUseByPC` = uniqueId WHERE `InUseByPC` NOT null LIMIT1')
if (updatedRows == 1)
SUCCESS
else
FAIL
If it succeeds you can then get the licence key/ID by doing a select.
As is so often the case, OP is an idiot. The code I posted was actually working, but I've just discovered a duplicate row in the database - I guess someone entered the same license twice by mistake. This led me to believe that a concurrency bug I had fixed (by introducing table locks) was still unfixed.
Thanks for the general advice, I've introduced better exception handling to this method.