Spring Hibernate Transaction Isolation不能产生不可重复的读取

时间:2014-04-03 07:34:01

标签: mysql spring hibernate spring-transactions

我正在研究一个学校项目,用read_committed隔离级别演示不可重复的读错误。我正在使用MySql,并使用hibernate。不知怎的,我不能产生不可重复的问题。

public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-hibernate.xml");

        WarehouseService service = ctx.getBean("warehouseServiceImpl", WarehouseService.class);
        int prodId = 3;             
        IncreaseInventoryThread thrdIncInvent = new IncreaseInventoryThread(service, prodId, 10);
        thrdIncInvent.start();  

        for(int i=0; i<30; i++){

            service.checkProduct(prodId);
        }
    }

我的主题只是不断增加产品库存

public class IncreaseInventoryThread extends Thread {

    WarehouseService service;
    int prodId;
    int iteration;


    public IncreaseInventoryThread(WarehouseService service, int prodId, int iteration) {
        this.service = service;
        this.prodId = prodId;
        this.iteration = iteration;
    }


    @Override
    public void run() {
        for(int i=0; i<iteration; i++){
            service.increaseInventory(prodId);

        }
    }
}

这是我的服务类

@Service
@Transactional(propagation=Propagation.SUPPORTS, readOnly=false)
public class WarehouseServiceImpl implements WarehouseService {


    static final int INITIAL_TOTAL_ORDER = 40;
    static final int DEFAULT_MAX_INVENTORY = 100;
    static final int DEFAULT_INC_INVENTORY = 5;


    @Autowired
    private ProductDao productDao;

    @Override
    @Transactional(propagation=Propagation.REQUIRED, isolation=Isolation.READ_COMMITTED)
    public void checkProduct(int prodId){
        Product product = productDao.getProductById(prodId);
            System.out.println("current product "+product.getName() +
                        " is "+ product.getStock());
            if(stock != productDao.getProductById(prodId).getStock()){
                System.out.println("Error: Unrepeatable read");
            }else{
                System.out.println("Read value consistent");
            }
    }
    @Override
    @Transactional(propagation=Propagation.REQUIRED, readOnly=false)
    public void increaseInventory(int prodId) {
        productDao.addInvetory(prodId, DEFAULT_INC_INVENTORY);
        Product product = productDao.getProductById(prodId);
        System.out.println(product.getName()+" now has " + product.getStock());     

    }

My Dao Class没有交易注释

@Repository
public class HibernateProductDao implements ProductDao {

    @Autowired 
    private SessionFactory sessionFactory;

    @Override
    public Session currentSession() {
        return this.sessionFactory.getCurrentSession();
    }

    @Override
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    @Override
    public void addProduct(Product product) {
        currentSession().saveOrUpdate(product);
    }

    @Override
    public Product getProductById(int id) {
        Product product = (Product) currentSession().get(Product.class, id);
        return product;
    }


    @Override
    public void addInvetory(int id, int quantity) {

        Product product = getProductById(id);
        int newStock = product.getStock()+quantity;
        product.setStock(newStock);
        addProduct(product);

    }

}

这个想法是使用两个线程。一个用于继续增加产品库存,另一个线程在一个事务中读取产品库存两次。由于read事务(service.checkProduct(prodId))被读取提交,我应该在某个时候看到一个不可重复的读取。但由于某种原因,我无法生产它。我甚至在事务中的两次读取之间添加延迟无济于事。任何建议表示赞赏。在调试打印之后:

current product TV Set is 100
TV Set now has 105
Read value consistent
current product TV Set is 100
Read value consistent
current product TV Set is 105
Read value consistent
TV Set now has 110
current product TV Set is 110
Read value consistent
TV Set now has 115
current product TV Set is 115
Read value consistent
TV Set now has 120
current product TV Set is 120
Read value consistent
TV Set now has 125
current product TV Set is 125
Read value consistent
TV Set now has 130
current product TV Set is 130
Read value consistent
current product TV Set is 130
Read value consistent
current product TV Set is 130
Read value consistent
TV Set now has 135
current product TV Set is 135
TV Set now has 140
Read value consistent
TV Set now has 145

1 个答案:

答案 0 :(得分:1)

在这种情况下,Hibernate隐式实现了可重复的读取隔离。即使您的事务具有读取提交的隔离,一旦将实体加载到会话中,它就会缓存在那里。该实体的任何其他get操作都会返回会话中缓存的副本,并且不会向数据库发出另一个查询。

所以这个

 @Override
    public Product getProductById(int id) {
        Product product = (Product) currentSession().get(Product.class, id);
        return product;
    }

第一次访问数据库,第二次从缓存中返回实体。缓存的副本不会包含来自其他线程的更改。

要获得所需的行为,您需要清除会话。在第一次和第二次读取之间调用currentSession().clear()