title: spring boot and jpa repository dealing with multi-thread
body: <p>I was studying spring boot and jpa repository and encountered one issue when dealing with multi-thread.</p>
  
  <p>There are 500 OfferCode in the database, by default, the allocatedFlag is set to null and the model is "ABC", and the code is unique in all 500 records.  The goal I am trying to achieve here is to call the register method to consume the OfferCode and set the allocatedFlag to 1.
  The problem I got here is this line</p>
  
  <pre><code>System.out.println(Thread.currentThread().getName()+" offer code "+offerCode.getCode()+" "+offerCode.getAllocatedFlag());
  </code></pre>
  
  <p>Sometimes it prints the different thread name but with same code and allocatedFlag is null. Since I am using synchronized, I think ideally it should not happen.
  If findTop1ByAllocatedFlagIsNullAndModel is executed correctly, once the OfferCode is returned, the allocatedFlag will be set to 1, and then second thread comes in it should not get the same code as last thread.
  But now I got
  For thread A, I got for example,  </p>
  
  <pre><code>Thread-A 0000-0000-00001 null
  Thread-A 0000-0000-00001 1
  </code></pre>
  
  <p>And for thread B, I got for example,</p>
  
  <pre><code>Thread-B 0000-0000-00001 null
  Thread-B 0000-0000-00001 1
  </code></pre>
  
  <p>I think it should get </p>
  
  <pre><code>Thread-B 0000-0000-00002 null
  Thread-B 0000-0000-00002 1
  </code></pre>
  
  <p>So I am not sure whether I use synchronized correctly or there are some issue with JpaRepository</p>
  
  <p>I also found if I change the OfferCodeRepository to below, then it is working.  So anybody know what is the issue here?</p>
  
  <pre><code>@Repository
  public interface OfferCodeRepository extends JpaRepository&lt;OfferCode, Integer&gt;{
      @Lock(LockModeType.PESSIMISTIC_READ)
      Optional&lt;OfferCode&gt; findTop1ByAllocatedFlagIsNullAndModel(String model);
  }
  </code></pre>
  
  <p>My code</p>
  
  <pre><code>@Entity
  public class OfferCode {
  
      @Id
      @GeneratedValue
      private int id;
      @Version
      private int version;    
      private String model;
      private String code;
      private String allocatedFlag;
  }
  
  @Repository
  public interface OfferCodeRepository extends JpaRepository&lt;OfferCode, Integer&gt;{
      Optional&lt;OfferCode&gt; findTop1ByAllocatedFlagIsNullAndModel(String model);    
  }
  
  @Service
  public class OfferCodeServiceImpl implements OfferCodeService {
      @Autowired
      private OfferCodeRepository offerCodeRepository;
      @Override
      public Optional&lt;OfferCode&gt; findTop1ByAllocatedFlagIsNullAndModel(String model) {
          return offerCodeRepository.findTop1ByAllocatedFlagIsNullAndModel(model);
      }
      @Override
      public OfferCode update(OfferCode code) {
          return offerCodeRepository.saveAndFlush(code);
      }
  }
  
  @Service
  public class ProductServiceImpl implements ProductService {
      @Autowired
      private OfferCodeService offerCodeService;
  
      @Transactional
      public synchronized Product register(Product product) {
          if(product!=null){
              Optional&lt;OfferCode&gt; optional =  offerCodeService.findTop1ByAllocatedFlagIsNullAndModel(product.getModel());
              optional.ifPresent(offerCode-&gt;{
                  System.out.println(Thread.currentThread().getName()+" offer code "+offerCode.getCode()+" "+offerCode.getAllocatedFlag());
                  offerCode.setAllocatedFlag("1");
                  offerCodeService.update(offerCode); 
                  System.out.println(Thread.currentThread().getName()+" offer code "+offerCode.getCode()+" "+offerCode.getAllocatedFlag());
              });
  
          }
          return product;
      }
  }
  </code></pre>