1. 程式人生 > 實用技巧 >Spring JPA 自定義刪改

Spring JPA 自定義刪改

Spring JPA 更新建立

​ 之前介紹的方法,基本都是隻讀方法,查詢建立沒有對資料庫中儲存的實體進行任何修改,但是對於更新和刪除來說,如果繼續保持只讀屬性,那麼改刪功能是難以完成的。通過使用@modify註釋查詢方法,您可以修改只需要引數繫結的查詢,如下面的示例所示:

例:修改查詢例項

@Modifying
@Query("update User u set u.firstname = ?1 where u.lastname = ?2")
int setFixedFirstnameFor(String firstname, String lastname);

​ 這樣做被註釋的方法的查詢將會作為更新查詢而不是選擇查詢,由於EntityManager

在執行了修改的查詢之後可能返回之前的查詢結果,如果您希望EntityManager被自動清除,您可以將@ modify註釋的clearautomatic屬性設定為true。該註解中有兩個屬性:flushAutomaticallyclearAutomatically,從字面理解是自動重新整理和自動清除。

  自動重新整理,即執行完語句後立即將變化內容重新整理到磁碟,如果是insert語句操作,則與JPA的<S extends T> S saveAndFlush(S entity);方法效果相同;

  自動清除,即執行完語句後自動清除掉已經過期的實體,比如,我們刪除了一個實體,但是在還沒有執行flush

操作時,這個實體還存在於實體管理器EntityManager中,但這個實體已經過期沒有任何用處,直到flush操作時才會被刪除掉。如果希望在刪除該實體時立即將該實體從實體管理器中刪除,則可以將該屬性設定為true,如:

@Modifying(clearAutomatically = true)
@Transactional
@Query(value = "delete from pro_user where id = ?1",nativeQuery = true)
void deleteUserById(Long id);

派生刪除

Spring Data JPA還支援派生的delete查詢,使您不必顯式宣告JPQL查詢,如下面的示例所示:

例:使用派生刪除查詢

interface UserRepository extends Repository<User, Long> {

  void deleteByRoleId(long roleId);

  @Modifying
  @Query("delete from User u where u.role.id = ?1")
  void deleteInBulkByRoleId(long roleId);
}

​ 儘管deleteByRoleId()方法看起來基本上會產生與deleteInBulkByRoleId()相同的結果,但就執行方式而言,這兩個方法宣告之間有一個重要的區別。顧名思義,後一種方法對資料庫發出單個JPQL查詢(在註釋中定義的查詢)。這意味著即使當前載入的User例項也沒有看到該命週期回撥被觸發。

​ 為了確保實際呼叫生命週期查詢,deleteByRoleId()的呼叫執行一個查詢,然後逐個刪除返回的例項,這樣永續性提供者就可以對這些實體實際呼叫@PreRemove回撥。

​ 實際上,派生的delete查詢是執行查詢並在結果上呼叫CrudRepository.delete(Iterable<User> users)並保持行為與CrudRepository中其他delete()方法的實現同步的快捷方式。

​ 事實上,如果直接執行以上自定義的的方法,可能會出現如下錯誤:

org.springframework.dao.InvalidDataAccessApiUsageException: Executing an update/delete query; nested exception is javax.persistence.TransactionRequiredException: Executing an update/delete query
    at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:402)
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:255)
    ......
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: javax.persistence.TransactionRequiredException: Executing an update/delete query
    at org.hibernate.internal.AbstractSharedSessionContract.checkTransactionNeededForUpdateOperation(AbstractSharedSessionContract.java:398)
    at org.hibernate.query.internal.AbstractProducedQuery.executeUpdate(AbstractProducedQuery.java:1585)
    .......

​ 這是因為,預設情況下,repository 介面中的CRUD方法都是被@Transactional註解修飾了的,對於讀的操作方法,@Transactional註解的readOnly屬性是被設定為true的,即只讀;CRUD中的其他方法被@Transactional修飾,即非只讀。如果你需要修改repository 介面中的某些方法的事務屬性,可以在該方法上重新加上@Transactional註解,並設定需要的屬性。

例:修改後程式碼

@Modifying
@Transactional
@Query("update User u set u.firstname = ?1 where u.lastname = ?2")
int setFixedFirstnameFor(String firstname, String lastname);

參考文件

[1] https://www.cnblogs.com/wuhenzhidu/p/jpa.html

[2] https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.modifying-queries