java - Update counter in DB using JPA, avoiding manual synchronization -
i have counter entity below : each prefix (current year , month), maintaining counter need increment.
@entity @table(name = "t_counter") @sequencegenerator(allocationsize = 1, name = "s_counter", sequencename = "s_counter") public class codecounter implements serializable { private static final long serialversionuid = 6431190800245592165l; @id @generatedvalue(strategy = sequence, generator = "s_counter") @column(name = "id") private long id; @column(name = "prefix", nullable = false,unique = true) private string prefix; @column(name = "counter", nullable = false) private integer counter;
this simple jpa repository, using spring data :
public interface codecounterrepository extends jparepository<codecounter, long> { @transactional codecounter findbyprefix(string prefix); }
now, whenever service called, need right counter prefix (and create if doesn't exist yet, first time of month), increment , save back. how have implemented far :
//entry point in service public string generateuniquerequestcode() { system.out.println("generating new request code consolidated request"); integer counter = getuniquecodeandupdatecounter(calendarprovider); string requestcode = format("%s_%04d_%02d_%06d", request_code_prefix, calendarprovider.getcurrentyear(), calendarprovider.getcurrentmonth(), counter); system.out.println("new request code consolidated request:: " + requestcode); return requestcode; } @transactional private synchronized integer getuniquecodeandupdatecounter(calendarprovider calendarprovider) { system.out.println("entering.."); string prefix = format("%04d_%02d", calendarprovider.getcurrentyear(), calendarprovider.getcurrentmonth()); codecounter codecounter = codecounterrepository.findbyprefix(prefix); if (codecounter != null) { codecounter.setcounter(codecounter.getcounter() + 1); } else { codecounter = new codecounter(); codecounter.setprefix(prefix); codecounter.setcounter(1); } codecounter counter=codecounterrepository.save(codecounter); int result=counter.getcounter(); system.out.println("..exiting"); return result; }
i've added multithreaded unit test (using tempus fugit library ) h2 db, shows it's working when 2 threads try generate unique code @ same time, i'm not happy code : i rid of synchronized
method , rely solely on proper transaction configuration.
if remove synchronized
keyword, both threads execute method @ same time , fails because generate same prefix, shouldn't happen (unique index or primary key violation). here's log :
generating new request code consolidated request
generating new request code consolidated request
entering..
entering..
hibernate: select codecounte0_.id id1_4_, codecounte0_.counter counter2_4_, codecounte0_.prefix prefix3_4_ t_counter codecounte0_ codecounte0_.prefix=?
hibernate: select codecounte0_.id id1_4_, codecounte0_.counter counter2_4_, codecounte0_.prefix prefix3_4_ t_counter codecounte0_ codecounte0_.prefix=?
hibernate: call next value s_counter hibernate: call next value s_counter
hibernate: insert t_counter (counter, prefix, id) values (?, ?, ?)
hibernate: insert t_counter (counter, prefix, id) values (?, ?, ?)
..exiting
new request code consolidated request:: cr_2016_09_000001
17:02:39.853 warn sqlexceptionhelper - sql error: 23505, sqlstate: 23505
17:02:39.854 error sqlexceptionhelper - unique index or primary key violation...
any idea of how implement without synchronizing myself in code ?
is writing method in entity annotated @prepersist or @preupdate looking for?
Comments
Post a Comment