@Slf4j
public abstract class SerialNumberBaseService implements SerialNumberService {
@Override
public String generate(SerialNumberIdEnum serialNumberIdEnum) {
List<String> generateList = this.generate(serialNumberIdEnum, 1);
if (generateList == null || generateList.isEmpty()) {
throw new BusinessException("cannot generate : " + serialNumberIdEnum.toString());
}
return generateList.get(0);
}
@Override
public List<String> generate(SerialNumberIdEnum serialNumberIdEnum, int count) {
SerialNumberInfoBO serialNumberInfoBO = serialNumberMap.get(serialNumberIdEnum.getSerialNumberId());
if (serialNumberInfoBO == null) {
throw new BusinessException("cannot found SerialNumberId : " + serialNumberIdEnum.toString());
}
return this.generateSerialNumberList(serialNumberInfoBO, count);
}
}
@Slf4j
@Service
public class SerialNumberMysqlService extends SerialNumberBaseService {
@Override
@Transactional(rollbackFor = Exception.class,transactionManager = "secondaryTransactionManager")
public List<String> generateSerialNumberList(SerialNumberInfoBO serialNumberInfo, int count) {
// 获取当前事务状态
TransactionStatus status = TransactionAspectSupport.currentTransactionStatus();
log.info("当前事务是否是新事务: {}", status.isNewTransaction());
log.info("当前事务是否有保存点: {}", status.hasSavepoint());
}
}
解释一下为什么这个事务会失效
在你的代码中,SerialNumberMysqlService
继承了 SerialNumberBaseService
,并且在 SerialNumberMysqlService
中重写了 generateSerialNumberList
方法,并为其添加了 @Transactional
注解。然而,事务可能会失效,原因在于 Spring 的事务管理机制和代理机制的特性。
Spring 的事务是通过动态代理实现的。只有通过 Spring 代理调用的方法,事务注解才会生效。如果直接在类内部调用方法(绕过代理),事务注解不会生效。
在你的代码中,SerialNumberBaseService
中的 generate
方法调用了 generateSerialNumberList
方法:
return this.generateSerialNumberList(serialNumberInfoBO, count);
这里直接通过 this
调用了 generateSerialNumberList
方法,绕过了 Spring 的代理机制。因此,事务注解不会生效。
虽然 SerialNumberMysqlService
继承了 SerialNumberBaseService
,但事务注解(@Transactional
)不会被继承。事务注解的作用范围仅限于它被定义的方法上。因此,即使 SerialNumberMysqlService
重写了父类的方法并添加了事务注解,父类中的调用仍然不会触发事务管理。
即使事务注解生效,事务传播行为(Propagation.REQUIRED
)决定了事务的共享或独立性。在你的代码中,事务注解被定义在 generateSerialNumberList
方法上,而不是在 generate
方法上。这意味着事务管理仅适用于 generateSerialNumberList
方法,而 generate
方法的调用不会触发事务。
在你的代码中:
@Override
public List<String> generate(SerialNumberIdEnum serialNumberIdEnum, int count) {
SerialNumberInfoBO serialNumberInfoBO = serialNumberMap.get(serialNumberIdEnum.getSerialNumberId());
if (serialNumberInfoBO == null) {
throw new BusinessException("cannot found SerialNumberId : " + serialNumberIdEnum.toString());
}
return this.generateSerialNumberList(serialNumberInfoBO, count); // 直接调用,绕过代理
}
generateSerialNumberList
方法被直接调用,绕过了 Spring 的代理机制。因此,事务注解不会生效。
generate
方法上将事务注解放在父类的 generate
方法上,确保事务管理生效:
@Slf4j
public abstract class SerialNumberBaseService implements SerialNumberService {
@Override
@Transactional(rollbackFor = Exception.class, transactionManager = "secondaryTransactionManager")
public String generate(SerialNumberIdEnum serialNumberIdEnum) {
List<String> generateList = this.generate(serialNumberIdEnum, 1);
if (generateList == null || generateList.isEmpty()) {
throw new BusinessException("cannot generate : " + serialNumberIdEnum.toString());
}
return generateList.get(0);
}
@Override
@Transactional(rollbackFor = Exception.class, transactionManager = "secondaryTransactionManager")
public List<String> generate(SerialNumberIdEnum serialNumberIdEnum, int count) {
SerialNumberInfoBO serialNumberInfoBO = serialNumberMap.get(serialNumberIdEnum.getSerialNumberId());
if (serialNumberInfoBO == null) {
throw new BusinessException("cannot found SerialNumberId : " + serialNumberIdEnum.toString());
}
return this.generateSerialNumberList(serialNumberInfoBO, count);
}
}
这样可以确保事务管理生效,因为事务注解直接作用于父类的方法。
如果事务注解必须放在子类中,可以通过 Spring 的代理机制调用方法:
@Override
public List<String> generate(SerialNumberIdEnum serialNumberIdEnum, int count) {
SerialNumberInfoBO serialNumberInfoBO = serialNumberMap.get(serialNumberIdEnum.getSerialNumberId());
if (serialNumberInfoBO == null) {
throw new BusinessException("cannot found SerialNumberId : " + serialNumberIdEnum.toString());
}
return ((SerialNumberBaseService) AopContext.currentProxy()).generateSerialNumberList(serialNumberInfoBO, count);
}
这里使用了 AopContext.currentProxy()
来获取当前的代理对象,从而确保事务注解生效。注意,使用 AopContext.currentProxy()
需要启用 expose-proxy
:
@EnableAspectJAutoProxy(exposeProxy = true)
将事务逻辑直接放在子类中,避免内部调用:
@Slf4j
@Service
public class SerialNumberMysqlService extends SerialNumberBaseService {
@Override
@Transactional(rollbackFor = Exception.class, transactionManager = "secondaryTransactionManager")
public String generate(SerialNumberIdEnum serialNumberIdEnum) {
List<String> generateList = generate(serialNumberIdEnum, 1);
if (generateList == null || generateList.isEmpty()) {
throw new BusinessException("cannot generate : " + serialNumberIdEnum.toString());
}
return generateList.get(0);
}
@Override
@Transactional(rollbackFor = Exception.class, transactionManager = "secondaryTransactionManager")
public List<String> generate(SerialNumberIdEnum serialNumberIdEnum, int count) {
SerialNumberInfoBO serialNumberInfoBO = serialNumberMap.get(serialNumberIdEnum.getSerialNumberId());
if (serialNumberInfoBO == null) {
throw new BusinessException("cannot found SerialNumberId : " + serialNumberIdEnum.toString());
}
return generateSerialNumberList(serialNumberInfoBO, count);
}
}
这样可以确保事务管理生效,因为事务注解直接作用于子类的方法。
generate
方法上。
2. 通过 Spring 的代理机制调用方法(如使用 AopContext.currentProxy()
)。
3. 重构代码,将事务逻辑直接放在子类中。根据你的需求选择合适的解决方案,以确保事务管理生效。
解释一下 事务注解的作用范围仅限于它被定义的方法上
在 Spring 中,@Transactional
注解的作用范围仅限于它被定义的方法上,这意味着事务管理器只会对被 @Transactional
注解直接修饰的方法生效。这一特性与 Java 的注解继承规则以及 Spring 的事务代理机制有关。
在 Java 中,注解默认不会被继承。即使一个子类方法重写了父类的注解方法,子类也不会自动继承父类的注解。例如:
public abstract class Parent {
@Transactional
public abstract void method();
}
public class Child extends Parent {
@Override
public void method() {
// 实现逻辑
}
}
在上述代码中,虽然 Parent
类的 method
方法被 @Transactional
注解修饰,但 Child
类的 method
方法并不会自动继承这个注解。如果需要在子类中使用事务,必须显式地在子类方法上重新添加 @Transactional
注解。
Spring 的事务是通过动态代理实现的。当一个方法被 @Transactional
注解修饰时,Spring 会为该方法创建一个事务代理。这个代理会拦截对方法的调用,并在调用前后插入事务管理逻辑。然而,这种代理机制仅对通过代理调用的方法生效,而不会自动扩展到父类或其他未被注解的方法。
例如:
@Service
public class Child extends Parent {
@Override
@Transactional
public void method() {
super.method(); // 调用父类方法
}
}
在上述代码中:
Child.method()
被 @Transactional
注解修饰,因此事务管理器会为它创建代理。super.method()
的调用是通过 this
发生的,绕过了 Spring 的代理机制。因此,即使父类方法原本有事务注解,事务管理也不会生效。@Transactional
注解的作用范围仅限于它被定义的方法,这意味着:
假设你有以下代码:
public abstract class Parent {
@Transactional
public void parentMethod() {
// 父类方法逻辑
}
}
@Service
public class Child extends Parent {
@Override
public void parentMethod() {
super.parentMethod(); // 调用父类方法
}
}
在这种情况下:
Parent.parentMethod()
被 @Transactional
注解修饰,但事务管理器不会自动生效,因为 Child
类没有显式地添加事务注解。Child.parentMethod()
调用 super.parentMethod()
时,事务管理器不会介入,因为调用是通过 this
发生的,绕过了 Spring 的代理机制。为了确保事务管理生效,需要在子类方法上显式地添加 @Transactional
注解:
@Service
public class Child extends Parent {
@Override
@Transactional
public void parentMethod() {
super.parentMethod();
}
}
这样,Spring 的事务管理器会为 Child.parentMethod()
创建代理,并确保事务管理生效。
@Transactional
注解直接修饰的方法生效。因此,为了确保事务管理生效,需要在子类方法上显式地添加 @Transactional
注解,并确保方法调用通过 Spring 的代理机制进行。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。