SpringDataJPA中自定义的插入、更新、删除方法时需要添加@Modifying注解和@Transactional注解

SpringDataJPA中自定义的插入、更新、删除方法时需要添加@Modifying注解和@Transactional注解

今天在写CRM项目时,JPA默认的删除方法失效了,然后就自己写了个自定义方法。开始时我是这么写的:

public interface UserDao extends JpaRepository<User, Integer>{
	
	User findByName(String name);
	
	Page<User> findByRole(Role role,Pageable able);
	

	@Modifying
	@Query(value = "delete from user where id=?1")
	void deleteUserById(@Param("id")Integer id);
}

然后,后台就报错。通过JPA官方资料我得出了自定义的插入、更新、删除操作需要加@Modifying注解和@Transactional注解

一、@Modifying注解

在官方资料中,给出了这样几句说明:

As the queries themselves are tied to the Java method that executes them, you can actually bind them directly by using the Spring Data JPA @Query annotation 
rather than annotating them to the domain class.

You can modify queries that only need parameter binding by annotating the query method with @Modifying

The @Modifying annotation is only relevant in combination with the @Query annotation. Derived query methods or custom methods do not require this Annotation.

Doing so triggers the query annotated to the method as an updating query instead of a selecting one.

如下:

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

第一句话的意思是可以用@Query注解来将自定义sql语句绑定到自定义方法上。

第二句话的意思时,可以用@Modifying注解来标注只需要绑定参数的自定义的更新类语句(更新、插入、删除)。

第三名话的意思是说@Modifying只与@Query联合使用,派生类的查询方法和自定义的方法不需要此注解,如:

@Repository
 public interface UserRepository extends JpaRepository<User,Long> {

     // 父类的保存方法
     @Override
     User save(User entity); 

     // 按照JPA语法规则自定义的查询方法
     List<User> findFirst10ByLastname(String lastName, Pageable pageable);  
}

第四句话的意思是,当加上@Modifying注解时,JPA会以更新类语句来执行,而不再是以查询语句执行。

也就是说,当我们要通过自已写的更新、插入、删除SQL语句来实现更新、插入、删除操作时,至少需要用两个步骤:

1)@Query来注入我们自定义的sql,nativeQuery = true是可以执行原生sql语句,所谓原生sql,也就是说这段sql拷贝到数据库中;

2)使用@Modifying来标注是一个更新类的自定义语句。

按照这个规则,修改后的方法:

public interface UserDao extends JpaRepository<User, Integer>{
	
	User findByName(String name);
	
	Page<User> findByRole(Role role,Pageable able);
	
	@Modifying
	@Query(value = "delete from user where id=?1",nativeQuery = true)
	void deleteUserById(@Param("id")Integer id);

}

但是,此时,该方法还不完整,执行时程序会报以下错误:

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)

二、@Transactional注解

官方的说明:

By default, CRUD methods on repository instances are transactional. For read operations, the transaction configuration readOnly flag is set to true. All others are configured with a plain @Transactional so that default transaction configuration applies. For details, see JavaDoc of SimpleJpaRepository. If you need to tweak transaction configuration for one of the methods declared in a repository, redeclare the method in your repository interface, as follows:

这句话的意思是,默认情况下,repository 接口中的CRUD方法都是被@Transactional注解修饰了的,对于读的操作方法,@Transactional注解的readOnly属性是被设置为true的,即只读;CRUD中的其他方法被@Transactional修饰,即非只读。如果你需要修改repository 接口中的某些方法的事务属性,可以在该方法上重新加上@Transactional注解,并设置需要的属性。

通过阅读SimpleJpaRepository源码后可看出:
1)该类上注解了只读事务@Transactional(readOnly = true);
2)该类的所有查询类操作方法都与类相同,都拥有只读事务;
3)该类的所有保存、更新、删除操作方法都用@Transactional重新注解了(默认readOnly=false)。

说明JPA为我们提供的所有方法,包括JPA规则的自定义方法在其底层都为我们做好了事务处理,而我们自定义的方法需要自己来标注事务的类型是只读还是非只读。
  最后修改代码为:

  public interface UserDao extends JpaRepository<User, Integer>{
	
	User findByName(String name);
	
	Page<User> findByRole(Role role,Pageable able);
	
	@Transactional
	@Modifying
	@Query(value = "delete from user where id=?1",nativeQuery = true)
	void deleteUserById(@Param("id")Integer id);
}

你可能感兴趣的:(SpringDataJPA中自定义的插入、更新、删除方法时需要添加@Modifying注解和@Transactional注解)