Spring忽略了@Transactional注解。

4
在我们的一个项目中,我们遇到了Spring忽略@Transactional注解并出现以下错误的问题。

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled. 2018-09-13 15:05:18,406 ERROR [main] org.springframework.boot.SpringApplication Application run failed org.springframework.dao.InvalidDataAccessApiUsageException: No EntityManager with actual transaction available for current thread - cannot reliably process 'remove' call; nested exception is javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread - cannot reliably process 'remove' call at com.my.service.CacheAService.deleteShortTermCache(CacheAService.java:70) ~[classes/:na]

我找到了类似的问题,但这些解决方案都不适用于此案例。
  • @EnableTransactionManagement已经存在
  • Transactional类实现了一个接口
  • Transactional方法是public的
  • Transactional方法没有在内部调用
当我在CacheService上使用@Transactional注解时,一切都重新开始工作了。但我仍然想知道为什么Spring会忽略CacheAService上的@Transactional
我尝试记录Spring的事务拦截器,但没有提及CacheA。 这是唯一相关的日志内容。

2018-09-13 15:05:18,242 TRACE [main] org.springframework.transaction.interceptor.TransactionInterceptor Don't need to create transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.deleteByValidity]: This method isn't transactional.

以下是简化的代码。 代码由Spring的ContextRefreshedEvent在应用程序启动期间调用。
@Service
public class CacheService implements Cache {

    @Autowired
    private CacheA cacheAService;
    @Autowired
    private CacheB cacheBService;

    @Override
    public void clearCache() {
        cacheAService.deleteShortTermCache();
        cacheBService.deleteAll();
    }
}

public interface CacheA {
    void deleteShortTermCache();
}

@Service
@Transactional(readOnly = true)
public class CacheAService implements CacheA {

    @Autowired
    private CacheARepository cacheARepository;

    @Override
    @Transactional
    public void deleteShortTermCache() {
        cacheARepository.deleteByValidity(CacheValidity.SHORT_TERM);
    }
}

public interface CacheB {
    void deleteAll();
}

@Service
@Transactional(readOnly = true)
public class CacheBService implements CacheB {

    @Autowired
    private CacheBRepository cacheBRepository;

    @Override
    @Transactional
    public void deleteAll {
        cacheBRepository.deleteAll();
    }
}

public enum CacheValidity {
    SHORT_TERM,
    LONG_TERM
}

@Repository
public interface CacheARepository extends JpaRepository<CacheItem, Integer> {
    void deleteByValidity(CacheValidity validity);
}

public enum CacheItemKey {
    AVAILABLE,
    FUTURE,
    AVAILABLE_UTM,
    FUTURE_UTM,
    REGION
}

@Entity
@Table(name = "cache_item")
public class CacheItem {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "cache_item_id_seq")
    @SequenceGenerator(name = "cache_item_id_seq", sequenceName = "cache_item_id_seq", allocationSize = 1)
    private Integer id;

    @Column(nullable = false, unique = true)
    @Enumerated(EnumType.STRING)
    private CacheItemKey key;

    @Column(nullable = false)
    private String value;

    @Column(name = "date_modified", nullable = false)
    @Temporal(TemporalType.TIMESTAMP)
    private Date dateModified;

    @Column(nullable = false)
    @Enumerated(EnumType.STRING)
    private CacheValidity validity;

    public Integer getId() {
        return id;
    }

    public void setId(final Integer id) {
        this.id = id;
    }

    public CacheItemKey getKey() {
        return key;
    }

    public void setKey(final CacheItemKey key) {
        this.key = key;
    }

    public String getValue() {
        return value;
    }

    public void setValue(final String value) {
        this.value = value;
    }

    public Date getDateModified() {
        return dateModified;
    }

    public void setDateModified(final Date dateModified) {
        this.dateModified = dateModified;
    }

    public CacheValidity getValidity() {
        return validity;
    }

    public void setValidity(final CacheValidity validity) {
        this.validity = validity;
    }

}

编辑: 经过一番调查,我在日志中找到了以下内容。

2018-09-14 06:24:11,174 INFO [localhost-startStop-1] org.springframework.context.support.PostProcessorRegistrationDelegate$BeanPostProcessorChecker Bean 'cacheAService' of type [com.my.service.CacheAService] 不符合所有 BeanPostProcessors 的处理条件(例如:不符合自动代理)


CacheACacheB的代码是什么? - NiVeR
我添加了接口。 - Januson
  1. deleteByValidity 应该加上 @Transactional 注解,因为它触发了一个修改操作。
  2. 尝试使用 @EnableTransactionManagement(proxyTargetClass = true),因为被 @Transactional 注解的类方法实现了接口方法,而这些接口方法本身并没有被注解为 @Transactional。或者,将 @Transactional 移动到接口 CacheACacheB 的方法声明中。
- manish
3个回答

2
我们发现这个问题是由Spring Boot的自动配置引起的。由于自动配置已经设置了事务管理,我们自定义的@EnableTransactionManagement配置破坏了事务顾问的实例化。从我们的配置中删除@EnableTransactionManagement可以解决这个问题。

{5小时后...} 在我的情况下确认解决方案(更复杂的设置,使用Kotlin / Spring Boot - 但已经通过Spring AOP Utils扫描到了几种方法)。 - wendro

1
尝试只使用一个Transactional注释(在类或方法中)。也许问题出在@Transactional(readOnly = true)上,因为您的事务不是只读的,我无法确定Spring更喜欢哪个Transactional注释。尝试使用:
@Service
public class CacheAService implements CacheA {

    @Autowired
    private CacheARepository cacheARepository;

    @Override
    @Transactional
    public void deleteShortTermCache() {
        cacheARepository.deleteByValidity(CacheValidity.SHORT_TERM);
    }
}

@Service
@Transactional
public class CacheAService implements CacheA {

    @Autowired
    private CacheARepository cacheARepository;

    @Override
    public void deleteShortTermCache() {
        cacheARepository.deleteByValidity(CacheValidity.SHORT_TERM);
    }
} 

谢谢您的回答。不幸的是,这并没有起作用。我尝试了两种变体。奇怪的是只有CacheA不起作用。当我将其注释掉时,缓存B按预期工作。 - Januson
你能否在这个Repository中添加代码CacheARepository、CacheBRepository以及使用它的代码实体?在CacheARepository中的实体是否有“fetchtype.lazy”或级联删除,例如“@OneToMany(cascade = CascadeType.ALL, ...)”? - Slava Vedenin
我添加了代码。现在只有值,没有其他实体。 - Januson

0

我遇到了一个熟悉的情况。对于那些有相同问题但答案不起作用的人来说。

@Transactional注释默认仅在public方法上工作,而不是在privatepackage-private上。

有关更多信息和解决方法,请参见此问题 -> Spring @Transactional属性适用于私有方法吗?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接