سؤال

I have a program that has 2 threads running, and each thread has its own database JDBC connection, and they will access/modify the same database table A like below. Table A only has 2 columns (id, name), and the primary key is the combination of id and name.

statement stmt;

// first delete it if the record has exist in table
stmt.addBatch("delete from A where id='arg_id' and name='arg_name';");

// then insert it to table
stmt.addBatch("insert into A values (arg_id, arg_name);");

stmt.executeBatch();

The 2 threads maybe insert the same data to the table, and i got the following exception,

java.sql.BatchUpdateException: Duplicate entry '0001-joey' for key 1
        at com.mysql.jdbc.Statement.executeBatch(Statement.java:708)
        at com.mchange.v2.c3p0.impl.NewProxyStatement.executeBatch(NewProxyStatement.java:743)
        at proc.Worker.norD(NW.java:450)

Do you have any idea how I can fix this issue? Thank you.

Regards, Joey

هل كانت مفيدة؟

المحلول

Why not introduce a simple optimistic locking mecanism on the database?

Add a version column and track the version number when performing delete or update transactions.

Your table would look like

create table test(
id int not null primary key,
name varchar,
rowversion int default = 0);

Every time you retrieve a row you should retrieve the row version so you can do

update test set name='new name' rowversion=rowversion+1 where id=id and rowversion=retrieved row version;

The same with delete

delete from test where id=id and rowversion=retrievedRowVersion;

This is a simple mechanism that will exploit the dbms concurency management features. Check this link for more information on optimistic locking http://en.wikipedia.org/wiki/Optimistic_concurrency_control#Examples

This is obviously only a very simple implementation of concurency management but your problem has to take these into account.

Also for the double insert the fact that your transaction is rejected is good that means that no duplicate keys are inserted. You should just handle the Exception and notify the user of what happen.

نصائح أخرى

Wrap both statements in a transaction:

BEGIN;
DELETE FROM a WHERE ...;
INSERT INTO a VALUES (...);
COMMIT;

Note that as long as the table consists of only the primary key, this conflict arises only when the table is unmodified at the end; I presume you want to add more columns, in which case you should use the UPDATE ... WHERE syntax to change values.

Are you using any kind of synchronization? First you will need to wrap the code that modifies the table in:

synchronized(obj)
{
    // code
}

where obj is an object that both threads can access. I don't know the exact semantics of your table modifications, but if they both insert ids, you will also need to hold a "global" id and atomically increment it in each thread, such that they don't both get the same value.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top