乐观锁的观念就不再赘述了,不相识的伴侣请自行百度谷歌之,本日主要说的是在项目中如何利用乐观锁,做成一个小demo。
耐久层利用jpa时,默认提供了一个注解@Version先看看源码怎么描写这个注解的。
/** * Specifies the version field or property of an entity class that * serves as its optimistic lock value. The version is used to ensure * integrity when performing the merge operation and for optimistic * concurrency control. * * <p> Only a single <code>Version</code> property or field * should be used per class; applications that use more than one * <code>Version</code> property or field will not be portable. * * <p> The <code>Version</code> property should be mapped to * the primary table for the entity class; applications that * map the <code>Version</code> property to a table other than * the primary table will not be portable. * * <p> The following types are supported for version properties: * <code>int</code>, <code>Integer</code>, <code>short</code>, * <code>Short</code>, <code>long</code>, <code>Long</code>, * <code>java.sql.Timestamp</code>. * * <pre> * Example: * * @Version * @Column(name="OPTLOCK") * protected int getVersionNum() { return versionNum; } * </pre> * * @since Java Persistence 1.0 */ @Target({ METHOD, FIELD }) @Retention(RUNTIME) public @interface Version { }
简朴来说就是用一个version字段来充当乐观锁的浸染。
先来设计实体类
/** * Created by xujingfeng on 2017/1/30. */ @Entity @Table(name = "t_student") public class Student { @Id @GenericGenerator(name = "PKUUID", strategy = "uuid2") @GeneratedValue(generator = "PKUUID") @Column(length = 36) private String id; @Version private int version; private String name; //getter()... //setter()... }
Dao层
/** * Created by xujingfeng on 2017/1/30. */ public interface StudentDao extends JpaRepository<Student,String>{ @Query("update Student set name=?1 where id=?2") @Modifying @Transactional int updateNameById(String name,String id); }
Controller层充当单位测试的浸染,通过会见一个requestMapping来触发我们想要测试的要领。
/** * Created by xujingfeng on 2017/1/30. */ @Controller public class StudentController { @Autowired StudentDao studentDao; @RequestMapping("student.html") @ResponseBody public String student(){ Student student = new Student(); student.setName("xujingfeng"); studentDao.save(student); return "student"; } @RequestMapping("testVersion.html") @ResponseBody public String testVersion() throws InterruptedException { Student student = studentDao.findOne("6ed16acc-61df-4a66-add9-d17c88b69755"); student.setName("xuxuan"); new Thread(new Runnable() { @Override public void run() { studentDao.findOne("6ed16acc-61df-4a66-add9-d17c88b69755"); student.setName("xuxuanInThread"); studentDao.save(student); } }).start(); Thread.sleep(1000); studentDao.save(student); return "testVersion"; } @RequestMapping("updateNameById.html") @ResponseBody public String updateNameById(){ studentDao.updateNameById("xuxuan2","6ed16acc-61df-4a66-add9-d17c88b69755"); return "updateNameById"; } }
这内里三个要领,劳务派遣管理系统,主要是我们想用来测试的三个留意点。
第一个要领student.html我们想看看springdata如何对version字段举办增长的。就不贴图了,直接给结论,对付添加了@Version的注解,我们不需要手动去节制,每一次save操纵会在本来的基本上+1,假如初始为null,则springdata自动配置其为0。
第二个要领testVersion.html是乐观锁的焦点,当多个线程并发会见同一行记录时,添加了@Version乐观锁之后,措施会举办怎么样的节制呢?
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.example.jpa.Student#6ed16acc-61df-4a66-add9-d17c88b69755]
异常信息如上,主线程和新线程获取了同一行记录,而且新线程优先提交了事务,版本号一致,修改乐成。比及了主线程再想save提交事务时,便获得一个版本号纷歧致的异常,那么在项目开拓中就应该本身捕捉这个异常按照业务内容做对应处理惩罚,是重试照旧放弃etc…