如何在Spring Data中使用@Transactional?

9 浏览
0 Comments

如何在Spring Data中使用@Transactional?

我刚开始做一个使用Spring-data、Hibernate、MySQL、JPA的项目。我转用spring-data是为了不必手动创建查询语句。

我注意到在使用spring-data时不需要使用@Transactional注解,因为我也尝试了没有注解的查询。

是否使用@Transactional注解有特定的原因吗?

可行:

@Transactional
public List listStudentsBySchool(long id) {
    return repository.findByClasses_School_Id(id);
}

同样可行:

public List listStudentsBySchool(long id) {
    return repository.findByClasses_School_Id(id);
}

0
0 Comments

使用@Transactional注解的原因是可以将未检查的SQL异常转换为Spring异常,而你只需要处理的异常是DataAccessException。通常情况下,这在使用Spring时是正确的做法,但是由于Spring Data存储库已经由Spring代理支持,使用@Transactional不会有任何区别。

解决方法是使用Spring Data提供的事务注解@Transactional。这样可以确保在持久化操作中发生异常时,事务可以正确地回滚。使用@Transactional注解时,Spring会自动为你管理事务的提交和回滚。

0
0 Comments

在Spring Data中如何使用@Transactional?

在你的示例中,这取决于你的repository是否有@Transactional注解。

如果有,那么在你的情况下,service应该不使用@Transactional(因为没有使用它的意义)。如果你计划在service中添加与其他表/仓库处理的逻辑,那么稍后可以添加@Transactional,那么这样做是有意义的。

如果没有,则如果要确保没有隔离问题,也就是说你不会读取尚未提交的内容,那么你的service应该使用@Transactional。

如果谈论一般的repository(作为crud的集合接口):

  1. 我会说:不应该使用@Transactional。

为什么不应该使用:如果我们相信repository是在业务上下文之外的,并且它不应该知道传播或隔离级别(锁的级别),它无法猜测它可能涉及的事务上下文。

repository是"无业务逻辑"的(如果你这样认为)。

假设你有一个repository:

class MyRepository
   void add(entity) {...}
   void findByName(name) {...}

还有一个业务逻辑,比如MyService:

 class MyService() {
   (propagation=Propagation.REQUIRED, isolation=Isolation.SERIALIZABLE)
   void doIt() {
      var entity = myRepository.findByName("some-name");
      if(record.field.equal("expected")) {
        ... 
        myRepository.add(newEntity)
      }
   }
 }

也就是说,在这种情况下,MyService决定是否要将repository涉及其中。

在这种情况下,使用propagation="Required"可以确保findByName()和add()两个repository方法都涉及到同一个事务中,而isolation="Serializable"可以确保没有人可以干扰它。它将为get()和add()涉及到的表保持锁定。

但是其他的Service可能希望以不同的方式使用MyRepository,不涉及任何事务,比如它使用findByName()方法,对于此刻它可以找到的任何内容都不感兴趣。

  1. 如果你将repository视为始终返回有效实体的repository(没有脏读等),那么我会说是的,(防止用户使用它不正确)。也就是说,你的repository应该解决隔离问题(并发和数据一致性),就像下面的例子一样:

我们希望(repository)确保当我们add(newEntity)时,首先检查是否已经存在具有相同名称的实体,如果存在-插入,在一个锁定的工作单元中完成。(与我们在上面的service级别上做的相同,但现在将此责任转移到repository)

比如说,不能有两个具有相同名称“进行中”状态的任务(业务规则)

 class TaskRepository
   (propagation=Propagation.REQUIRED, 
   isolation=Isolation.SERIALIZABLE)
   void add(entity) {
      var name = entity.getName()
      var found = this.findFirstByName(name);
      if(found == null || found.getStatus().equal("in-progress")) 
      {
        .. do insert
      }
   }
   void findFirstByName(name) {...}

第二种更像是DDD风格的repository。

我猜还有更多可以讨论的,如果:

  class Service {
    (isolation=.., propagation=...) // 其中..与taskRepository()中定义的不同
    void doStuff() {
      taskRepository.add(task);
    }
  }

0
0 Comments

如何在Spring Data中使用@Transactional?

在Spring Data中使用@Transactional的原因是为了确保数据库操作的一致性和完整性。当我们进行添加、编辑或删除操作时,需要在方法上添加@Transactional注解,以确保这些操作在一个事务中执行。而对于查询操作,我们可以在方法上添加(readOnly = true)注解,以提高查询性能并避免意外的写操作。

在Spring Data中,我们可以在接口上使用@Transactional注解来设置默认的事务属性。对于大部分的查询方法,我们可以使用(readOnly = true)注解来标记,以提高查询性能。而对于修改操作的方法,我们可以在方法上直接添加@Transactional注解。

同时,我们也需要注意一些性能优化方面的问题。例如,使用(readOnly = true)注解可以让Spring Data在查询时禁用JPA/Hibernate的缓存刷新操作,从而提高性能。另外,根据数据库的不同,该注解还可以避免表锁或拒绝写操作。

总之,我们应该根据具体情况在DAO方法中使用@Transactional注解。对于查询方法,我们可以使用(readOnly = true)注解来提高查询性能。而对于修改操作的方法,我们可以直接在方法上添加@Transactional注解。需要注意的是,如果使用(readOnly = true)注解,还可以考虑同时设置传播模式为SUPPORTS。

参考链接:

- [transaction-pit-falls](http://www.ibm.com/developerworks/java/library/j-ts1/index.html#listing8)

- [why do i need transaction in hibernate for read only operations](https://stackoverflow.com/questions/13539213)

- [Spring read-only transaction Hibernate optimization](https://vladmihalcea.com/spring-read-only-transaction-hibernate-optimization/)

0