2012-11-13 3 views
1

Spring + Hibernate를 사용할 때 이상한 캐시가 있습니다.Hibernate + Spring에 의한 예기치 않은 캐시

라인 A는 2012 년
라인 B는 올해 찾는 DB에서 그것을 얻을 올해와 데이터베이스에 데이터의 새로운 행을 삽입하다 2012 년
선 C 업데이트 해 1970 년에
라인 D는 올해 여전히 2012입니다 발견 , 나는 이것이 왜 일어날 지 이해하지 못한다. 그러나 B 행을 주석으로 처리한다면 D 행은 1970 년이되고 어떤 종류의 캐시처럼 보입니다. 또는 findLock()에서 getCurrentSession() 대신 openSession()을 사용하면 D 행도 1970이됩니다. abybody가이 동작을 설명 할 수 있습니까?

테스트 드라이브

@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 
    } 
} 

나는 문제가 최대 절전 모드 (및 JPA가) 정말 네이티브 SQL, 특히 SQL 업데이트 쿼리에 대한 인터페이스로 구성되지 않는 것입니다 믿는 것

@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; 

    } 
} 

답변

1

OperationLockDAOImpl.java . Hibernate는 세션 - 레벨 캐시를 유지할 것이다. 일반적으로 엔티티가 갱신되어 엔트리가 세션 캐시에서 (적어도 같은 스레드 내에서는) 오래 가지 않도록 업데이트 된 것을 알 수 있습니다. 그러나 SQL 업데이트 쿼리를 사용하여 엔티티를 업데이트하고 있기 때문에 Hibernate는 캐시에있는 엔티티를 변경하고있어이를 무효화 할 수 없다는 것을 알지 못합니다.

요약하면 최대 절전 모드 캐시는 기본 SQL 업데이트 쿼리에서 작동하지 않습니다. 이를 작동 시키려면 각 작업을 자체 트랜잭션 (@Transactional을 테스트 클래스에서 제거)으로 유지해야하지만 결국 성능 문제가 발생합니다. Hibernate에서 엔티티 수정에 대한 선호되는 접근 방식은 다음과 같다.

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

오, openSession()은 새로운 1 차 캐시를 생성하기 때문에 작동한다. – Pace

관련 문제