1. 程式人生 > >Spring JPA 行級鎖的實現

Spring JPA 行級鎖的實現

最近要做一個新專案,需要藉助mysql的行級鎖機制,又由於是第一次使用jpa去實現行級鎖,所以遇到了一丟丟問題,昨天晚上用了1個多小時解決了。。分享下。。

1.這是spring配置檔案的內容,相信大多數人也都能從網上search到:

  1. <bean id="hibernateJpaVendorAdapter"
  2.           class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />  
  3.     <bean id="entityManagerFactory"
  4.     class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
    >  
  5. <!-- 指定下資料來源 -->  
  6.         <property name="dataSource" ref="<strong>dataSource</strong>" />   
  7.         <property name="jpaVendorAdapter" ref="hibernateJpaVendorAdapter" />  
  8.         <!-- 指定Entity實體類包路徑 -->  
  9. <property name="packagesToScan">  
  10.             <array>  
  11.                 <value>com.xxx.xx.xxx.core.entity</value>  
  12.             </array>  
  13.         </property>  
  14.         <property name="jpaProperties">  
  15.             <props>  
  16.                 <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>  
  17.                 <prop key="hibernate.ejb.naming_strategy"
    >org.hibernate.cfg.ImprovedNamingStrategy</prop>  
  18.                 <prop key="hibernate.cache.provider_class">org.hibernate.cache.NoCacheProvider</prop>  
  19.                 <prop key="hibernate.show_sql">true</prop> <!-- 指是否顯示SQL,可以根據需要 -->  
  20.                 <prop key="hibernate.format_sql">false</prop>  
  21.             </props>  
  22.         </property>  
  23.     </bean>  
  24. <!-- 指定下Dao層的包路徑-->  
  25.     <jpa:repositories base-package="com.xxx.xx.xxx.core.dao"
  26.                       entity-manager-factory-ref="entityManagerFactory"
  27.                       transaction-manager-ref="transactionManagerjpa" />  
  28.     <bean id="transactionManagerjpa"class="org.springframework.orm.jpa.JpaTransactionManager">  
  29.         <property name="entityManagerFactory" ref="entityManagerFactory" />  
  30.     </bean>  
  31.     <tx:annotation-driven transaction-manager="transactionManagerjpa"  />  


2.簡單的貼一個Entity物件和一個Dao

  1. import javax.persistence.Column;  
  2. import javax.persistence.Entity;  
  3. import javax.persistence.GeneratedValue;  
  4. import javax.persistence.GenerationType;  
  5. import javax.persistence.Id;  
  6. import javax.persistence.Table;  
  7. import java.io.Serializable;  
  8. import java.util.Date;  
  9. @Entity
  10. @Table(name = "job_info")  
  11. publicclass JobInfo implements Serializable {  
  12.     @Id
  13.     @GeneratedValue(strategy = GenerationType.IDENTITY)  
  14.     private Long id;  
  15.     @Column(name = "job_name")  
  16.     private String jobName;  
  17.     @Column(name = "job_desc")  
  18.     private String jobDesc;  
  19. .... get/set方法不再贅述  
  20. }  
  1. import javax.persistence.LockModeType;  
  2. import org.springframework.data.jpa.repository.JpaRepository;  
  3. import org.springframework.data.jpa.repository.Lock;  
  4. import org.springframework.data.jpa.repository.Query;  
  5. import org.springframework.data.repository.query.Param;  
  6. import org.springframework.stereotype.Repository;  
  7. import com.xxx.xx.core.entity.JobInfo;  
  8. @Repository
  9. publicinterface JobInfoDao extends JpaRepository<JobInfo, Long> {  
  10.     @Query(value = "select j from JobInfo j where j.jobName = :jobname ")  
  11.     public JobInfo getJobForUpdate(@Param("jobname") String jobname);  
  12.     @Lock(value = LockModeType.PESSIMISTIC_WRITE)  
  13.     @Query(value = "select j from JobInfo j where j.id = :id ")  
  14.     publicvoid getJobByIdForUpdate(@Param("id") Long id);  
  15. @Lock(value = LockModeType.PESSIMISTIC_WRITE) // TODO 千萬不要用這個哦!
  16.     @Query(value = "select j from JobInfo j where j.jobName = :jobname ")  
  17.     publicvoid getJobByNameForUpdate(@Param("jobname") String jobname);  
  18. }  

3.service層,此為測試程式碼

  1. import org.springframework.transaction.annotation.Transactional;  
  2. publicclass JobService implements IJobService {  
  3. @Autowired
  4.     private JobInfoDao jobInfoDao;  
  5. @Transactional// 這個是需要標註的,因為Dao層有for update 的機制,那麼這邊就要開啟事務了,否則會報錯的。。。
  6.     public JobInfo getJobForUpdate(Long id) {  
  7.         jobInfoDao.getJobByIdForUpdate(id);  
  8.         try {  
  9.             Thread.sleep(100000);  
  10.         } catch (InterruptedException e) {  
  11.         }  
  12.         returnnull;  
  13.     }  
  14. }  
4.完成。

當呼叫JobService中的 getJobByIdForUpdate時,就可以達到行級鎖的目的了! 

----------------------------------------------------------------------------------------------------------------------------

如果你只是需要通過jpa實現行級鎖,那麼好,以上的東東,已經夠了。但是呢,我實際開發中,並不是那麼順的,現在我來說下我遇到了什麼鬼。。。

1.行級鎖,大家一定都聽到過,也肯定比較喜歡。對於mysql,InnoDB預設的是Row-level Lock,但是,需要明確的指定主鍵,才會執行行級鎖,否則執行的為表鎖

比如:

select * from job_info where id = 1 for update;  

那麼上面這句,為行級鎖。

而 select * from job_info where job_name = 'test' for update;

這句,就變成了表鎖了。。。。(我當時淚也流乾了,各種查DB引擎,命令列測試,多虧了(http://blog.sina.com.cn/s/blog_88d2d8f501011sgl.html  MySQL中select * for update鎖表的問題) 這篇文章)

那麼好,現在關於如何才能讓mysql執行行級鎖的問題解決了。。。

2.jpa如何搞 select for update。

也是醉了,在Dao層的方法上,要配置Lock的註解。並且要加上LockModeType.PESSIMISTIC_WRITE ,這個就相當於for update了。大家也可以在程式執行時,打印出的sql中看到。 這個東東,得益於 (http://suene.iteye.com/blog/1756295  Spring Data JPA,基礎學習筆記.)  該文章。

至此呢,終於解決掉了行級鎖和jpa註解實現  for update 的問題。。。

注:這裡寫注呢,是因為前面的demo程式碼裡,也有坑的。。大家應該也能注意到了,在Dao層中,getJobByNameForUpdate 這個方法千萬不要用哦!它可是會導致鎖表的哦!