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

Popular posts from this blog

angular - Is it possible to get native element for formControl? -

unity3d - Rotate an object to face an opposite direction -

javascript - Why jQuery Select box change event is now working? -