spring jpa 行级锁的实现

最近要做一个新项目,需要借助MySQL的行级锁机制,又由于是第一次使用jpa去实现行级锁,所以遇到了一丢丢问题,昨天晚上用了1个多小时解决了。。分享下。。

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

1.这是spring配置文件的内容,相信大多数人也都能从网上search到:


[java] view plain copy
  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.   
  29.     <bean id="transactionManagerjpa" class="org.springframework.orm.jpa.JpaTransactionManager">  
  30.         <property name="entityManagerFactory" ref="entityManagerFactory" />  
  31.     </bean>  
  32.     <tx:annotation-driven transaction-manager="transactionManagerjpa"  />  




2.简单的贴一个Entity对象和一个Dao


[java] view plain copy
  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.   
  10. @Entity  
  11. @Table(name = "job_info")  
  12. public class JobInfo implements Serializable {  
  13.   
  14.     @Id  
  15.     @GeneratedValue(strategy = GenerationType.IDENTITY)  
  16.     private Long id;  
  17.     @Column(name = "job_name")  
  18.     private String jobName;  
  19.     @Column(name = "job_desc")  
  20.     private String jobDesc;  
  21.   
  22. .... get/set方法不再赘述  
  23. }  


[java] view plain copy
  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. public interface 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.     public void 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.     public void getJobByNameForUpdate(@Param("jobname") String jobname);  
  18. }  


3.service层,此为测试代码


[java] view plain copy
  1. import org.springframework.transaction.annotation.Transactional;  
  2.   
  3. public class JobService implements IJobService {  
  4. @Autowired  
  5.     private JobInfoDao jobInfoDao;  
  6. @Transactional // 这个是需要标注的,因为Dao层有for update 的机制,那么这边就要开启事务了,否则会报错的。。。  
  7.     public JobInfo getJobForUpdate(Long id) {  
  8.         jobInfoDao.getJobByIdForUpdate(id);  
  9.         try {  
  10.             Thread.sleep(100000);  
  11.         } catch (InterruptedException e) {  
  12.         }  
  13.         return null;  
  14.     }  
  15. }  



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 这个方法千万不要用哦!它可是会导致锁表的哦! 

收藏  | 打印  | 字体:  -缩小  放大+    
[ x ] 请正确填写下面信息


是否保存此网页快照 是否公开此收藏

查看全部评论(1)我来说两句