2017-06-13 134 views
6

以下是兩個表及其實體類。休眠與sqlserver死鎖問題

tbl_rules

| rule_id | rule_name |

@Entity 
    @Table(name = "db_user_name.tbl_rules") 
    public class Rule implements Serializable { 
     private static final long serialVersionUID = 1L; 

     @Id 
     @Basic(optional = false) 
     @GenericGenerator(name = "incre", strategy = "increment") 
     @GeneratedValue(generator = "incre") 
     @Column(name = "rule_id", unique = true, nullable = false) 
     private long ruleId; 

     @Column(name = "rule_name", length = 250) 
     private String ruleName; 

     @OneToMany(fetch = FetchType.LAZY, mappedBy = "rules") 
     private Set<Benchmark> benchmarks = new HashSet<Benchmark>(0); 
     ... getters and setters 
    } 

tbl_benchmark

| benchmark_id | rule_id |

@Entity 
@Table(name = "tbl_benchmark", catalog = "db_user_name") 
@DynamicUpdate(value = true) 
public class Benchmark implements Serializable { 
    private static final long serialVersionUID = 1L; 

    @Id 
    @Basic(optional = false) 
    @Column(name = "benchmark_id", unique = true, nullable = false) 
    private Long benchmarkId; 

    @ManyToOne(fetch = FetchType.LAZY) 
    @JoinColumn(name = "rule_id", nullable = false) 
    private Rule rules; 
    .. getter and setters 
} 

在以下情況下

  • HibernateSessionManager.beginTransaction()面對與sql server db僵局問題;
  • 呼叫saveRule(rule) //在後端規則和基準兩個表 被鎖定(使用SQL Server鎖定表查詢)
  • 呼叫saveBenchmark(benchmark) //死鎖在此方法
  • HibernateSessionManager.commit();

代碼,其中死鎖發生:

HibernateSessionManager.beginTransaction(); 
      UserIdManager.setCurrentGroupId(2); 
      if (savingObjects.get(AmlConstants.USERCREDENTAILS_STRING) != null){ 
        userCredentials = (UserCredentials) savingObjects.get(AmlConstants.USERCREDENTAILS_STRING); 
        Util.setAuditLogField(AmlConstants.USERIDSTRING); 
        this.getAmlDAOFactory().getUserCredentialsDAO().updateUserDetails(userCredentials); 
       if (savingObjects.get(AmlConstants.USERBRANCHMAPPING_STRING) != null){ 
        userBranchMapping = (UserBranchMapping) savingObjects.get(AmlConstants.USERBRANCHMAPPING_STRING); 
         Util.setAuditLogField(AmlConstants.BRANCH_STRING); 
         this.getAmlDAOFactory().getUserBranchMappingDAO().saveUserBranchMapping(userBranchMapping); 
       } 
      } 
      HibernateSessionManager.commit(); 

saveRule:

@Override 
    public Rule saveRule(Rule rule) throws Exception { 
     try { 
      getSession().saveOrUpdate(rule); 
      getSession().flush(); 
     } catch (RuntimeException e) { 
      e.printStackTrace(); 
      throw e; 
     } 
     return rule; 
    } 

saveBenchmark:

@Override 
    public Benchmark saveBenchMark(Benchmark benchmark) throws Exception { 
     try { 
      if (benchmark.getBenchmarkId() == null) 
       benchmark.setBenchmarkId(getBenchmarkCount() + 1); 
      getSession().clear(); 
      getSession().saveOrUpdate(benchmark); 
      getSession().flush(); 
     } catch (RuntimeException e) { 
      // logger.error("Runtime error while saving benchmark", e); 
      e.printStackTrace(); 
     } catch (Exception e) { 
      logger.error("Exception while saving benchmark " + e.getMessage(), e); 
     } 
     return benchmark; 
    } 

春季型流感嗜血桿菌CONFG文件:

<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource" 
destroy-method="close"> 
      <property name="driverClassName" value="${jdbc.driverClassName}" /> 
    .. 
    <property name="hibernateProperties"> 
       <props> 
        <prop key="hibernate.dialect">com.aml.hibernate.SQLServerCustomeDialect</prop> 
        <prop key="hibernate.character_encoding">UTF-8</prop> 
        <prop key="hibernate.connection.useUnicode">true</prop> 
        <prop key="hibernate.show_sql">true</prop> 
        <prop key="hibernate.generate_statistics">false</prop> 
       </props> 
      </property> 
    .. 

HibernateSessionManager.java

public class HibernateSessionManager { 

    public static Logger logger = Logger.getLogger(HibernateSessionManager.class); 
    public static final ThreadLocal<Session> currentSession = new ThreadLocal<Session>(); 
    public static final ThreadLocal<java.util.List<Session>> sessionList = new ThreadLocal<java.util.List<Session>>(); 

    /** Store transaction object on thread local 
    * this helps to make a request processing transactional */ 
    public static final ThreadLocal<Transaction> transaction = new ThreadLocal<Transaction>(); 
    public static final ThreadLocal<Map<String, Transaction>> transactionMap = new ThreadLocal<Map<String, Transaction>>(); 

    /** keep the beginner method path which helps to commit on the same method only 
    * we are not supposed to make nested commits under a single request */ 
    public static final ThreadLocal<String> callerXPath = new ThreadLocal<String>(); 



    /** 
    * Returns existing hibernate session binded with current request thread, 
    * if no session already bind with current thread then it will open a new session, bind to current thread 
    * and returns the session object 
    * 
    * @param sessionFactory 
    * @return 
    * @throws HibernateException 
    */ 
    public static Session currentSession(SessionFactory sessionFactory) throws HibernateException { 

     Session s = (Session) currentSession.get(); 
     // Open a new Session, if this Thread has none yet 
     if (s == null || !s.isOpen()) 
     { 
      s = sessionFactory.openSession(); 
      currentSession.set(s); 
      if(sessionList.get()==null) 
       sessionList.set(new LinkedList<Session>()); 
      sessionList.get().add(s); 
      logger.debug("Opened new session:"+currentSession.get().hashCode()); 
     }else{ 
      logger.debug("returning existing session:"+currentSession.get().hashCode()); 
     } 
     return s; 
    } 


    /** 
    * Closes all the sessions binded with current request thread 
    * @throws HibernateException 
    */ 
    public static void closeSession() throws HibernateException { 
     currentSession.set(null); 
     transaction.set(null); 
     callerXPath.set(null); 

     try{ 
      if(sessionList.get()!=null) 
       for (int i = 0; i < sessionList.get().size(); i++) { 
        Session s = sessionList.get().get(i); 
        try{ 
         if (s != null && s.isOpen()) 
          s.close(); 
         logger.debug("Closed session - session local:"+ (s!=null?s.hashCode(): "")); 
        }catch (Exception e) { logger.debug("Error while closing session: ", e); } 
       } 
      transactionMap.get().clear(); 
     }catch (Exception e) { logger.debug("Error while closing session: ", e); } 
     sessionList.set(null); 
     transactionMap.set(null); 
    } 




    // ------------------- Transaction management ------------------ 
    /** 
    * Starts a new hibernate transaction on the session binded to current request thread 
    * if there is already a transaction started on this thread, ignores creation of another transaction 
    * all the db calls on a single request thread has to come under a single transaction 
    * @return 
    */ 
    public static boolean beginTransaction(){ 
     try{ 
      logger.debug("beginTransaction............... "); 

      Transaction t = transaction.get(); 
      if(t == null && callerXPath.get()==null){ 
       Session s = currentSession.get(); 
       t = s.beginTransaction(); 
       t.registerSynchronization(new Synchronization() { 

        @Override 
        public void beforeCompletion() { 
         logger.debug("Transaction-beforeCompletion............... "); 

        } 

        @Override 
        public void afterCompletion(int status) { 
         logger.debug("Transaction-afterCompletion............... "+status); 

        } 
       }); 
       transaction.set(t); 
       callerXPath.set(getCallerMethodInvolvedinTransaction()); 

       if(transactionMap.get()==null) 
        transactionMap.set(new HashMap<String, Transaction>()); 

       transactionMap.get().put(callerXPath.get(), t); 
       logger.debug("Started new hibernate transaction:"+t); 
      } 
     }catch (Exception e) { 
      logger.error("Error while starting new transaction: ", e); 
      return false; 
     } 
     return true; 
    } 


    /** 
    * if we already have a hibernate transaction created on the current request thread and some thing is committed on it 
    * it will rollback the changes done after the transaction initialization 
    */ 
    public static void rollback(){ 
     try{ 
      Transaction t = transactionMap.get().get(callerXPath.get()); 
      if(t != null){ 
       t.rollback(); 
       logger.debug("Roll back success on transaction:"+t); 
      } 
     }catch (Exception e) { 
      logger.error("Exception while trying to rollback", e); 
     } 
    } 


    /** 
    * Commits all the changes done after the transaction started on the current request thread 
    * this accepts the commit command from the only method which started the transaction 
    * This will unlink the current session and then currentSession() method can give another session as existing one is unlinked on the thread local 
    */ 
    public static void commit(){ 
     try{ 
      logger.debug("commit............... "); 

      Transaction t = transaction.get(); 
      if(t != null /*&& !t.wasCommitted()*/ 
        && callerXPath.get()!=null && callerXPath.get().equals(getCallerMethodInvolvedinTransaction())){ 
       t.commit(); 

       currentSession.get().clear(); 
       currentSession.set(null); 
       transaction.set(null); 
       callerXPath.set(null); 

       logger.debug("Commit success on transaction:"+t); 
      } 
     }catch (Exception e) { 
      logger.error("Exception while trying to commit", e); 
     } 
    } 









    /** 
    * get the caller method xpath: <package>.<classname>.<methodname> 
    * @return 
    */ 
    public static String getCallerMethodInvolvedinTransaction() { 
     try{ 
      StackTraceElement[] stElements = Thread.currentThread().getStackTrace(); 
      return stElements[3].toString().split("\\(")[0]; 
      /*for (int i = 3; i < stElements.length; i++) { 
       String rawFQN = stElements[i].toString().split("\\(")[0]; 
       String className = rawFQN.substring(0, rawFQN.lastIndexOf('.')); 
       String methodName = rawFQN.substring(rawFQN.lastIndexOf('.')+1); 
       Object carObj = Class.forName(className).newInstance(); 

       ClassPool pool = ClassPool.getDefault(); 
       CtClass cc = pool.get(className); 
       CtMethod methodX = cc.getDeclaredMethod(methodName); 
       int xlineNumber = methodX.getMethodInfo().getLineNumber(0); 

       Method method = carObj.getClass().getMethod(methodName); 
       if (method.isAnnotationPresent(JCTransaction.class)) 
       { 
        return rawFQN; 
       } 
      }*/ 
     }catch (Exception e) { 
      logger.error("" , e); 
     } 
     return null; 
    } 
} 

但同樣工作的罰款與oracle db(與Oracle HIB屬性)。

+2

提供死鎖圖(可從SQL SQL system_health擴展事件會話中獲得),執行計劃和表DDL。 –

+3

您沒有提供任何死鎖證據,包括任何顯示死鎖正在發生的錯誤消息,堆棧跟蹤或日誌輸出。如果沒有更多信息,任何人都不可能幫助你。 –

+0

@JimGarrison,在這個階段我沒有收到任何錯誤,因爲基準表被鎖定(從數據庫觀察),所以服務器很簡單。在此之後,我只是重新啓動服務器。我已經通過添加Hibernate會話管理器類 – TSKSwamy

回答

3

你的代碼一定有問題,或者你永遠不應該鎖定自己。兩個不同的連接可以彼此阻塞,但是一個連接不應該阻塞自己的鎖。我沒有詳細查看代碼,我將重點討論爲什麼您會遇到SQL Server問題,而不是Oracle問題。

Oracle總是對行使用版本控制,所以行不會因爲讀取而被鎖定。另一方面,SQL Server通常需要讀取鎖定,而讀取鎖定將防止來自其他會話的寫入。您可能可能會將SQL Server隔離級別更改爲READ_COMMITED_SNAPSHOT以隱藏問題,但它仍然存在。

我不明白你爲什麼要在幾個地點清除會話,這應該幾乎從來沒有完成。我也不理解處理HibernateSessionManager中的事務的所有代碼,這可能是問題的根源。不知何故,你正在運行多個交易。保持簡單,而且問題可能會消失!

+0

註釋'session.flush()'後解決了死鎖問題。這很好,或將來會影響到任何地方。 – TSKSwamy

+0

但是,如果我評論session.flush(),對象不會保存在oracle數據庫中:-( – TSKSwamy

+0

刷新並不危險,清除是。如果刪除清除,對象將在會話中等待並自動刷新。如果多個事務競爭,就會出現鎖定問題,當你調用clear時,任何等待被保存的(即未被刷新的)都會丟失 – user2612030

2

根據我的理解,您已經定義了規則& BenchMark之間的OneToMany關係。因此,在構建規則Entitiy/Object時,您也可以構建Benchmark。我對嗎?

分析: 我假設Benchmark對象也被填充並保存在Set Benchmarks中。所以現在你正在保存規則,因爲冬眠會試圖節省基準。在同一次交易中,您正試圖再次節省Benchmark,並且由於此交易管理器陷入僵局。

解決方案: 之前在規則對象設定基準嘗試填充benchmarkid並保存唯一的規則,這將節省您的behcmark對象了。

@Override 
    public Rule saveRule(Rule rule) throws Exception { 
     try { 
      //Get BenchMark from rule... 
      // Your code to get Benchmark from rule. 
      // Populate benchmarkId 
      if (benchmark.getBenchmarkId() == null) { 
       benchmark.setBenchmarkId(getBenchmarkCount() + 1); 
      } 
      getSession().saveOrUpdate(rule); 
      getSession().flush(); 
     } catch (RuntimeException e) { 
      e.printStackTrace(); 
      throw e; 
     } 
     return rule; 
    } 

PS:請參考Hibernate文檔。

+0

謝謝,我會檢查它 – TSKSwamy

+0

我已通過創建與我的主應用程序相同的兩個實體,通過一個簡單的應用程序對它進行了測試。它在保存兩個對象在同一個事務中工作正常。 – TSKSwamy

+0

很高興知道您的問題已解決。 – Nikhil