Spring JPA 行級鎖的實現
最近要做一個新專案,需要藉助mysql的行級鎖機制,又由於是第一次使用jpa去實現行級鎖,所以遇到了一丟丟問題,昨天晚上用了1個多小時解決了。。分享下。。
1.這是spring配置檔案的內容,相信大多數人也都能從網上search到:
- <bean id="hibernateJpaVendorAdapter"
- class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
- <bean id="entityManagerFactory"
- class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
- <!-- 指定下資料來源 -->
- <property name="dataSource" ref="<strong>dataSource</strong>" />
- <property name="jpaVendorAdapter" ref="hibernateJpaVendorAdapter" />
- <!-- 指定Entity實體類包路徑 -->
- <property name="packagesToScan">
- <array>
- <value>com.xxx.xx.xxx.core.entity</value>
- </array>
- </property>
- <property name="jpaProperties">
- <props>
- <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
- <prop key="hibernate.ejb.naming_strategy"
- <prop key="hibernate.cache.provider_class">org.hibernate.cache.NoCacheProvider</prop>
- <prop key="hibernate.show_sql">true</prop> <!-- 指是否顯示SQL,可以根據需要 -->
- <prop key="hibernate.format_sql">false</prop>
- </props>
- </property>
- </bean>
- <!-- 指定下Dao層的包路徑-->
- <jpa:repositories base-package="com.xxx.xx.xxx.core.dao"
- entity-manager-factory-ref="entityManagerFactory"
- transaction-manager-ref="transactionManagerjpa" />
- <bean id="transactionManagerjpa"class="org.springframework.orm.jpa.JpaTransactionManager">
- <property name="entityManagerFactory" ref="entityManagerFactory" />
- </bean>
- <tx:annotation-driven transaction-manager="transactionManagerjpa" />
2.簡單的貼一個Entity物件和一個Dao
- import javax.persistence.Column;
- import javax.persistence.Entity;
- import javax.persistence.GeneratedValue;
- import javax.persistence.GenerationType;
- import javax.persistence.Id;
- import javax.persistence.Table;
- import java.io.Serializable;
- import java.util.Date;
- @Entity
- @Table(name = "job_info")
- publicclass JobInfo implements Serializable {
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- private Long id;
- @Column(name = "job_name")
- private String jobName;
- @Column(name = "job_desc")
- private String jobDesc;
- .... get/set方法不再贅述
- }
- import javax.persistence.LockModeType;
- import org.springframework.data.jpa.repository.JpaRepository;
- import org.springframework.data.jpa.repository.Lock;
- import org.springframework.data.jpa.repository.Query;
- import org.springframework.data.repository.query.Param;
- import org.springframework.stereotype.Repository;
- import com.xxx.xx.core.entity.JobInfo;
- @Repository
- publicinterface JobInfoDao extends JpaRepository<JobInfo, Long> {
- @Query(value = "select j from JobInfo j where j.jobName = :jobname ")
- public JobInfo getJobForUpdate(@Param("jobname") String jobname);
- @Lock(value = LockModeType.PESSIMISTIC_WRITE)
- @Query(value = "select j from JobInfo j where j.id = :id ")
- publicvoid getJobByIdForUpdate(@Param("id") Long id);
- @Lock(value = LockModeType.PESSIMISTIC_WRITE) // TODO 千萬不要用這個哦!
- @Query(value = "select j from JobInfo j where j.jobName = :jobname ")
- publicvoid getJobByNameForUpdate(@Param("jobname") String jobname);
- }
3.service層,此為測試程式碼
- import org.springframework.transaction.annotation.Transactional;
- publicclass JobService implements IJobService {
- @Autowired
- private JobInfoDao jobInfoDao;
- @Transactional// 這個是需要標註的,因為Dao層有for update 的機制,那麼這邊就要開啟事務了,否則會報錯的。。。
- public JobInfo getJobForUpdate(Long id) {
- jobInfoDao.getJobByIdForUpdate(id);
- try {
- Thread.sleep(100000);
- } catch (InterruptedException e) {
- }
- returnnull;
- }
- }
當呼叫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 這個方法千萬不要用哦!它可是會導致鎖表的哦!