1

I'm developing a spring mvc webapp with spring data and hibernate.

I've an entity composed by a boolean field and by an Integer field.

At beginning the boolean field is false and Integer field is null.

When boolean field become true I need to assign to the Integer field a unique value equals to MAX(seq) +1

To do it, I write my service method in this way:

@Override
public synchronized Obj save(Obj entry) {
    if (entry.getBool() && entry.getSeq() == null){
        Integer seq = objRepository.getLastSeq();
        if (seq == null){
            seq = 1;
        }
        entry.setSeq(seq);
    }
    return entry.save(entry);
}

And in my Reposiroty:

@Query("select MAX(seq)+1 FROM Obj")
Integer getLastSeq();

I put synchronized keyword to service method in order to be sure that only a thread at a time can get an unique MAX+1 number.. but I'm not sure if it works in all situation and if it is the right way.

Can I be sure that it guarantee unicity of seq?

Thank you Marco

gipinani
  • 14,038
  • 12
  • 56
  • 85
  • Only UK to `seq` column will guarantee you unicity. – user1516873 Mar 21 '13 at 08:34
  • I cannot set it UK because some entity must have integer value null. Integer become not null only when boolean become true! – gipinani Mar 21 '13 at 08:37
  • Ok, can user explicity call `obj.setSeq()`? – user1516873 Mar 21 '13 at 08:46
  • No.. it is passed to and from the view with an input type hidden.. – gipinani Mar 21 '13 at 08:52
  • 1
    Syncronizing at the method is basically syncronizing on "this", which in your case is the service class. Although I usually prefer not to do that, what you are essentially doing is saying that only one thread will execute that method for each instance of your service class. If you are guaranteed only one copy of the service class exists, then I believe you are OK. However, if you have multiple application servers, each with its own copy, or you are creating multiple references to your service object, then you are not. – CodeChimp Mar 21 '13 at 12:20

2 Answers2

2

It seems like your Integer is entry id or isn't it ? So why not to use database sequence, with @id adnotation for example :

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
private Long id;
Damian0o
  • 653
  • 5
  • 15
  • No.. it isn't the entity id. Is an integer given only to the entity for which the boolean propery become true. To be more rehalistic this entity represent an Exam.. I give a sequence number only when I successfully pass the Exam! Thank you for your answer – gipinani Mar 21 '13 at 08:35
  • So check this workaround in answer to [this](http://stackoverflow.com/questions/277630/hibernate-jpa-sequence-non-id) question. – Damian0o Mar 21 '13 at 08:49
  • Meybe better soultion is to add initialazingbean interface to your service and check max sequence number in table and then use AtomicInteger in your save method. Every time you want next integer you query database witch is not optimal solution. – Damian0o Mar 21 '13 at 08:57
  • It is one of solutions that I've thinked about after post this question! But before I will try to find something less intricate. – gipinani Mar 21 '13 at 08:58
  • Can you tell me where I can find an example or something about your comment? – gipinani Mar 21 '13 at 09:03
  • [initializing bean](http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/beans/factory/InitializingBean.html) or [Post Construct](http://www.mkyong.com/spring/spring-postconstruct-and-predestroy-example/) – Damian0o Mar 21 '13 at 09:31
1

Example how to use initializing bean for that purpose.

@Service
public class SequenceInitializer implements InitializingBean{

    @Autowired
    private ObjRepository objRepository;

    @Autowired
    private Service service;

    @Override
    public void afterPropertiesSet() throws Exception {

        try {
           Integer max = objRepository.getLastSeq();
           service.setLastSeq(max);
        } catch(Exception e){...}

 }

In your service setLastSeq will set AtomicInteger field.

Damian0o
  • 653
  • 5
  • 15
  • I don't understand if this method could be useful in my case. Could it work if in a firts attempt I create the entity and save it with boolean false (so integer is null). Then I update the entity setting boolean true (so integer = maxValue +1)? – gipinani Mar 21 '13 at 10:03
  • So lets be clear. You want to have a field which has naxt integer value on save if some condition is true. In your solution every time you save entity you make additional query to database(It is not good idea). In solution above You make that query only once on application started and write max value to AtomicInteger field in your service. Now when next time you invoke save method you could get next integer value from atiomicInteger..getAndIncrement(). That solution is much faster and synchronized block is out. – Damian0o Mar 21 '13 at 10:09