Spring data jpa 支持註解式的讀寫鎖(悲觀鎖),實際上這個東西硬編碼也簡單,可是基於Jpa 命名方式定義的Sql,只能用註解添加支持讀寫鎖了,java
不瞭解讀寫鎖的能夠點這裏 mysql
而且推薦sql
PESSIMISTIC_READ,
PESSIMISTIC_WRITE,
而不是
READ,
WRITE,
可是官方文檔貌似沒有更新這個案例,踩了一些坑.
新建一個實體Book.javaapp
/** * User: laizhenwei * Date: 2018-04-18 Time: 9:04 * Description: */ @Entity @Table(name = "test_book") @Alias("Book") @Getter @Setter @NoArgsConstructor @AllArgsConstructor public class Book extends AbstractJbatisIdEntity{ private static final long serialVersionUID = -1L; private String name; private String author; }
BookRepository.java
/** * User: laizhenwei * Date: 2018-04-18 Time: 9:11 * Description: */ public interface BookRepository extends JpaRepository<Book,String> { @Lock(LockModeType.PESSIMISTIC_READ) Book findTop1ByName(String name); }
BookServiceImpl TimeUnit.SECONDS.sleep(20); 是爲了讓事務延遲提交,好測試save操做須要阻塞到讀寫釋放才能提交
public static final CountDownLatch readCount = new CountDownLatch(1); public static final CountDownLatch saveCount = new CountDownLatch(1); @Transactional(propagation = Propagation.REQUIRES_NEW) @Override public Book save(Book book){ Book book1 = null; try { readCount.await(); book1 = getRepository().save(book); saveCount.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } return book1; } @Transactional(propagation = Propagation.REQUIRES_NEW) @Override public Book findTop1ByName(String name){ Book book = getRepository().findTop1ByName(name); try { readCount.countDown(); TimeUnit.SECONDS.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); } return book; }
JunitTest 先添加一條數據,待會要鎖這個數據ide
@Test public void save(){ Book book = new Book(); book.setName("百年孤獨"); book.setAuthor("加西亞·馬爾克斯"); bookRepository.save(book); }
開啓兩條線程,一條先加上讀鎖,而後睡眠一會,另外一條線程去修改這個對象的時候,須要阻塞到讀事務提交之後纔會成功測試
第二個查詢動做不會阻塞,由於讀鎖只對寫操做限制(這裏用直接用bookRepository,是爲了不CountDownLatch 再一次阻塞而已)編碼
@Test @Transactional public void findByName() throws InterruptedException { new Thread(()->bookService.findTop1ByName("百年孤獨")).start(); BookServiceImpl.readCount.await(); Book book =bookRepository.findTop1ByName("百年孤獨"); book.setAuthor("加西亞·馬爾克斯5"); new Thread(()->bookService.save(book)).start(); BookServiceImpl.saveCount.await(); }
有個有趣的現象,若是直接運行第二次,會發現不用阻塞,就能save成功,由於數據並無作任何修改.spa
再註釋掉@Lock跑一次,修改 book.setAuthor("加西亞·馬爾克斯5");再保存也不須要等待..net
Mybatis下的實現,就是手動編碼而已線程
/** * User: laizhenwei * Date: 2018-04-18 Time: 9:12 */ @Mapper public interface BookMapper extends BaseMapper<Book> { @Select("select * from test_book where name=#{name} limit 1 lock in share mode") Book findTop1ByName(String name); }
Service TimeUnit.SECONDS.sleep(20); 是爲了讓事務延遲提交,好測試save操做須要阻塞到讀寫釋放才能提交
@Transactional(propagation = Propagation.REQUIRES_NEW) @Override public Book mapperFindTop1ByName(String name){ Book book = getMapper().findTop1ByName(name); try { readCount.countDown(); TimeUnit.SECONDS.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); } return book; }
JunitTest (這裏用直接用bookRepository,是爲了不CountDownLatch 再一次阻塞而已)
@Test @Transactional public void findByName() throws InterruptedException { new Thread(()->bookService.mapperFindTop1ByName("百年孤獨")).start(); BookServiceImpl.readCount.await(); Book book =bookRepository.findTop1ByName("百年孤獨"); book.setAuthor("加西亞·馬爾克斯3"); new Thread(()->bookService.save(book)).start(); BookServiceImpl.saveCount.await(); }
測試效果與Jpa同樣.