Spring,JPA和Hibernate - 如何在没有并发问题的情况下增加计数器

时间:2015-05-09 18:31:14

标签: java spring hibernate jpa transactions

我正在玩Spring和JPA / Hibernate,我对在表中增加计数器的正确方法感到困惑。

我的REST API需要根据用户操作递增和递减数据库中的某些值(在下面的示例中,喜欢或不喜欢标记会使计数器在标记表中递增或递减1)

tagRepositoryJpaRepository(Spring-data) 我已经像这样配置了交易

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"/>

@Controller
public class TestController {

    @Autowired
    TagService tagService

    public void increaseTag() {
        tagService.increaseTagcount();
    }
    public void decreaseTag() {
        tagService.decreaseTagcount();

    }
}

@Transactional
@Service
public class TagServiceImpl implements TagService {


    public void decreaseTagcount() {
        Tag tag = tagRepository.findOne(tagId);
        decrement(tag)
    }

    public void increaseTagcount() {
        Tag tag = tagRepository.findOne(tagId);
        increment(tag)
    }

    private void increment(Tag tag) {
        tag.setCount(tag.getCount() + 1); 
        Thread.sleep(20000);
        tagRepository.save(tag);
    }

    private void decrement(Tag tag) {
        tag.setCount(tag.getCount() - 1); 
        tagRepository.save(tag);
    }
}

正如你所看到的那样,我已经在.save()之前以增量JUST为目的进行了20秒的睡眠,以便能够测试并发场景。

初始标签计数器= 10;

  

1)用户调用increaseTag并且代码命中睡眠所以值   实体= 11,DB中的值仍为10

     

2)用户调用decreaseTag并浏览所有代码。该   value是数据库现在= 9

     

3)睡眠结束并且在具有a的实体击中.save   计数为11,然后点击.save()

当我检查数据库时,该标签的值现在等于11 ..实际上(至少我希望实现)它将等于10

这种行为是正常的吗?或者@Transactional注释不起作用?

2 个答案:

答案 0 :(得分:1)

例如,使用乐观锁定。 这应该是解决问题的最简单的解决方案。 有关详细信息,请参阅 - &gt; https://docs.jboss.org/hibernate/orm/4.0/devguide/en-US/html/ch05.html

答案 1 :(得分:0)

这里值得一提的是 spring-boot-mongodb JPA-如何在没有并发问题的情况下增加计数器:

在MongoDB中,根据官方文档:

修改单个文档时,db.collection.findAndModify()和update()方法都会自动更新文档。有关这些方法的交互作用和操作顺序的更多详细信息,请参见Atomicity and Transactions

对于Mongo shell,我们可以简单地如下运行findAndModify:

> db.idGenerator.findAndModify({query: {identifier: "identified_by_Id"}, update: {$inc: {counter:1}}  })
{
    "_id" : ObjectId("5e115560ff14992f34fd18c6"),
    "identifier" : "identified_by_Id",
    "counter" : 1
}
> db.idGenerator.find()
{ "_id" : ObjectId("5e115560ff14992f34fd18c6"), "identifier" : "identified_by_Id", "counter" : 2 }
> db.idGenerator.findAndModify({query: {identifier: "identified_by_Id"}, update: {$inc: {counter:1}}  })
{
    "_id" : ObjectId("5e115560ff14992f34fd18c6"),
    "identifier" : "identified_by_Id",
    "counter" : 2
}
>

findAndModify()总是递增/递减并返回先前为counter提供的实际值。

就JPA而言,首先创建一个Model,然后创建Repository和Service类,以获取唯一的ID,如下所示:

//模型类

import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

@Document
public class IdGenerator {

  @Id
  private String identifier;

  private long counter;

  public String getIdentifier() {
    return identifier;
  }

  public void setIdentifier(String identifier) {
    this.identifier = identifier;
  }

  public long getCounter() {
    return counter;
  }

  public void setCounter(long counter) {
    this.counter = counter;
  }
}

// Mongo存储库类:

import org.springframework.data.mongodb.repository.MongoRepository;
import sample.data.mongo.models.IdGenerator;


public interface IdGeneratorRepository extends MongoRepository<IdGenerator, String> {

}

// ID生成器服务类

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.FindAndModifyOptions;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import sample.data.mongo.models.IdGenerator;
import sample.data.mongo.repository.IdGeneratorRepository;

@Service
public class IdGeneratorService {

  @Autowired
  private IdGeneratorRepository idGeneratorRepository;

  @Autowired
  private MongoTemplate mongoTemplate;

  public long generateId(String key) {

    Query query = new Query();
    // key = identified_by_Id;
    Criteria criteria = new Criteria("identifier").is(key);
    query.addCriteria(criteria);

    Update update = new Update();
    update.inc("counter", 1);

    FindAndModifyOptions options = new FindAndModifyOptions();
    options.upsert(true);
    options.returnNew(true);

    IdGenerator idGenerator = mongoTemplate
        .findAndModify(query, update, options, IdGenerator.class);

    return idGenerator.getCounter();
  }
}

通过使用以上方法generateId,它将始终为特定键返回递增的数字。