java - Hibernate + Spring 的意外缓存

标签 java spring hibernate caching

当我使用 Spring + Hibernate 时,我得到了一些奇怪的缓存。

A 行将 2012 年的新数据行插入数据库。
B线从DB获取,发现年份是2012年。
C 线将年份更新为 1970 年。
D行发现年份仍然是2012年,我不明白为什么会发生这种情况?但如果我注释掉 B 行,D 行就会得到 1970,这似乎是某种缓存。或者如果在 findLock() 中,我使用 openSession() 而不是 getCurrentSession(),则 D 行也会得到 1970。有人可以解释一下这种行为吗?

试驾

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/applicationContext-Test.xml")
@TransactionConfiguration(defaultRollback = true,transactionManager="transactionManager")
@Transactional
public class OperationLockDAOTest {

    @Autowired
    private OperationLockDAO operationLockDAO;
    @Autowired
    private APIOperationDAO apiOperationDAO;

    private APIOperation operation;

    @Before
    public void setup() {
        operation = apiOperationDAO.findOperationById(Constants.OP_CREATE_SUBSCRIBER);  
    }

    @Test
    public void testAddNewLockAndReleaseLock() throws DBException{
        String userKey = "testUserKey1" + Math.random();

        boolean bGetLock = operationLockDAO.lockOperationByUser(userKey, operation);//line A, insert 2012
        List<OperationLock> locks1 = operationLockDAO.findLock(userKey, operation);//line B
        OperationLock lock1 = locks1.get(0);
        Calendar cb = Calendar.getInstance();
        cb.setTime(lock1.getModifytime());//get 2012


        operationLockDAO.unlockOperationByUser(userKey, operation);//line C, update to 1970

        List<OperationLock> locks2 = operationLockDAO.findLock(userKey, operation);//line D
        OperationLock lock2 = locks2.get(0);

        Calendar cr = Calendar.getInstance();
        cr.setTime(lock2.getModifytime());
        int crYear = cr.get(Calendar.YEAR);// still get 2012

        assertTrue(crYear == Constants.LongBeforeYear);//assert time stamp of lock is set to 1970 after release 
    }
}

OperationLockDAOImpl.java

@Repository("operationLockDAOImpl")
public class OperationLockDAOImpl implements OperationLockDAO{

    protected static final Logger logger = LoggerFactory.getLogger(OperationLockDAOImpl.class);

    /**
     * return true - this operation is not locked by another thread, lock behavior is successful this time
     *        false - this operation is locked by another thread, lock behavior failed this time
     */
    @SuppressWarnings("unchecked")
    @Override
    @Transactional(isolation = Isolation.SERIALIZABLE)
    public boolean lockOperationByUser(String userKey, APIOperation lockoperation) {

        String selecSsql = "select * from operation_lock where userKey=:userKey and lockoperationId=:lockoperationId";
        Session session = this.factory.getCurrentSession();
        Query selectQuery = session.createSQLQuery(selecSsql).addEntity(OperationLock.class);
        selectQuery.setString("userKey", userKey);
        selectQuery.setLong("lockoperationId", lockoperation.getId());
        List<OperationLock> operationLockList = selectQuery.list();

        if(operationLockList == null || operationLockList.size() == 0){//no record for this userKey and operation, insert one anyway
            String insertSql = "insert into operation_lock(`userKey`,`lockoperationId`,`modifytime`) values (:userKey, :lockoperationId, :updateTime)";
            Query insertQuery = session.createSQLQuery(insertSql);
            insertQuery.setString("userKey", userKey);
            insertQuery.setLong("lockoperationId", lockoperation.getId());
            insertQuery.setTimestamp("updateTime", new Date());
            insertQuery.executeUpdate();
            return true;
        } else {
            return false;
        }
    }

    @Override
    @Transactional(isolation = Isolation.SERIALIZABLE)
    public void unlockOperationByUser(String userKey, APIOperation lockoperation) {

        Date currentTime = new Date();
        Calendar time = Calendar.getInstance();
        time.setTime(currentTime);
        time.set(Calendar.YEAR, Constants.LongBeforeYear);//it's long before
        String sql = "update operation_lock set modifytime=:updatetime where userKey=:userKey and lockoperationId=:lockoperationId";
        Session session = this.factory.getCurrentSession();
        Query query = session.createSQLQuery(sql);
        query.setTimestamp("updatetime", time.getTime());
        query.setString("userKey", userKey);
        query.setLong("lockoperationId", lockoperation.getId());
        query.executeUpdate();
    }

    @SuppressWarnings("unchecked")
    @Transactional(isolation = Isolation.SERIALIZABLE)
    @Override
    public List<OperationLock> findLock(String userKey, APIOperation lockoperation) {

        String sql = "select * from operation_lock where userKey=:userKey and lockoperationId=:lockoperationId";
//      Session session = this.factory.openSession();
        Session session = this.factory.getCurrentSession();
        Query query = session.createSQLQuery(sql).addEntity(OperationLock.class);
        query.setString("userKey", userKey);
        query.setLong("lockoperationId", lockoperation.getId());

        List<OperationLock> result =  query.list();
//      session.close();
        return result;

    }
}

最佳答案

我认为问题在于 Hibernate(和 JPA)并不是真正旨在作为 native SQL(尤其是 SQL 更新查询)的接口(interface)。 Hibernate 将维护 session 级缓存。通常,它知道实体何时更新,以便 session 缓存中的条目不会过时(至少不会在同一线程内)。但是,由于您正在使用 SQL 更新查询更新实体,Hibernate 不知道您正在更改其缓存中的实体,因此它无法使其无效。

总之,Hibernate 缓存不适用于 native SQL 更新查询。要使其正常工作,您必须在其自己的独立事务中维护每个操作(从测试类中删除 @Transactional),但这最终会导致性能问题。 Hibernate 中实体修改的首选方法类似于...

Entity foo = session.get(Entity.class,entityId);
foo.x = y;
Entity fooModified = session.get(Entity.class,entityId);
//fooModified.x == y

关于java - Hibernate + Spring 的意外缓存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13357182/

相关文章:

java - 任何像 jboss 或 tomcat 这样的服务器,我想从已部署的应用程序中实例化一个类

java - 无法将字符串解析为正确的日期格式

macos - 如何在我的 Mac 上安装 JavaFx 2.0?

Java版本的TinyGP,其输出和测试数据

Javaconfig bean 重写因 List 注入(inject)而失败

java - hibernate中的依赖事务

java - 多线程应用程序中的spring + SQLite

java - 使用 AbstractTransactionalJUnit4SpringContextTests : changes in DB not reverted after test method 测试纯 JDBC DAO 方法

java - 如何使用 Hibernate 连接两个表的字段?

java - Hibernate:添加一个对象(如果尚不存在)