当前使用版本(必填,否则不予处理)

3.2.0

该问题是如何引起的?(确定最新版也有问题再提!!!)

关于引用service实现类的事务管理器问题,使用多数据源后无法引用用户指定的事物管理器

重现步骤(如果有就写完整)

必现

报错信息

解决方式

采用以下的方式可以优先选择接口上指定的事物管理器,即指定优先级。忘采纳

@Component public class AnnotationTransactionAttributeSourceReplacer implements InstantiationAwareBeanPostProcessor, PriorityOrdered /this is important/ {

public AnnotationTransactionAttributeSourceReplacer() {
    // to check that the replacer is created before instantiation of the "transactionAttributeSource" bean
    Loggers.BIZ.info("AnnotationTransactionAttributeSourceReplacer - constructor");
}

@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
    // log.trace("postProcessBeforeInstantiation - beanName: {}, beanClass: {}", beanName, beanClass);
    if (beanName.equals("transactionAttributeSource") && TransactionAttributeSource.class.isAssignableFrom(beanClass)) {
        Loggers.BIZ.info(String.format("instantiating bean {%s} as {%s}", beanName, MergeAnnotationTransactionAttributeSource.class.getName()));
        return new MergeAnnotationTransactionAttributeSource();
    } else {
        return null;
    }
}

@Override
public int getOrder() {
    return 0;
}

}

public class MergeAnnotationTransactionAttributeSource extends AnnotationTransactionAttributeSource {

public MergeAnnotationTransactionAttributeSource() {
    Loggers.BIZ.info("MergeAnnotationTransactionAttributeSource constructor init");
}

@Override
@Nullable
protected TransactionAttribute computeTransactionAttribute(Method method, @Nullable Class<?> targetClass) {
    // Don't allow no-public methods as required.
    if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
        return null;
    }

    // The method may be on an interface, but we also need attributes from the target class.
    // If the target class is null, the method will be unchanged.
    Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);

    // 1st priority is the specific method.
    TransactionAttribute txAttr = findTransactionAttribute(specificMethod);

    // 2nd priority is the declaring class of the specific method.
    Class<?> declaringClass = specificMethod.getDeclaringClass();
    boolean userLevelMethod = ClassUtils.isUserLevelMethod(method);
    if (userLevelMethod) {
        txAttr = merge(txAttr, findTransactionAttribute(declaringClass));
    }

    // 3rd priority is the target class
    if (targetClass != null && !targetClass.equals(declaringClass) && userLevelMethod) {
        txAttr = merge(txAttr, findTransactionAttribute(targetClass));
    }

    if (method != specificMethod) {
        // 4th priority is the method in the declaring class/interface.
        txAttr = merge(txAttr, findTransactionAttribute(method));

        // 5th priority is the declaring class/interface.
        txAttr = merge(txAttr, findTransactionAttribute(method.getDeclaringClass()));
    }

    return txAttr;
}

/**
 * Set empty and default properties of "primary" object from "secondary" object.
 * <p>Parameter objects should not be used after the call to this method,
 * as they can be changed here or/and returned as a result.
 */
@Nullable
private TransactionAttribute merge(@Nullable TransactionAttribute primaryObj, @Nullable TransactionAttribute secondaryObj) {
    if (primaryObj == null) {
        return secondaryObj;
    }
    if (secondaryObj == null) {
        return primaryObj;
    }

    if (primaryObj instanceof DefaultTransactionAttribute && secondaryObj instanceof DefaultTransactionAttribute) {
        DefaultTransactionAttribute primary = (DefaultTransactionAttribute) primaryObj;
        DefaultTransactionAttribute secondary = (DefaultTransactionAttribute) secondaryObj;

        if (primary.getQualifier() == null || primary.getQualifier().isEmpty()) {
            primary.setQualifier(secondary.getQualifier());
        }
        if (primary.getDescriptor() == null || primary.getDescriptor().isEmpty()) {
            primary.setDescriptor(secondary.getDescriptor());
        }
        if (primary.getName() == null || primary.getName().isEmpty()) {
            primary.setName(secondary.getName());
        }

        // The following properties have default values in DefaultTransactionDefinition;
        // we cannot distinguish here, whether these values have been set explicitly or implicitly;
        // but it seems to be logical to handle default values like empty values.
        if (primary.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED) {
            primary.setPropagationBehavior(secondary.getPropagationBehavior());
        }
        if (primary.getIsolationLevel() == TransactionDefinition.ISOLATION_DEFAULT) {
            primary.setIsolationLevel(secondary.getIsolationLevel());
        }
        if (primary.getTimeout() == TransactionDefinition.TIMEOUT_DEFAULT) {
            primary.setTimeout(secondary.getTimeout());
        }
        if (!primary.isReadOnly()) {
            primary.setReadOnly(secondary.isReadOnly());
        }
    }

    if (primaryObj instanceof RuleBasedTransactionAttribute && secondaryObj instanceof RuleBasedTransactionAttribute) {
        RuleBasedTransactionAttribute primary = (RuleBasedTransactionAttribute) primaryObj;
        RuleBasedTransactionAttribute secondary = (RuleBasedTransactionAttribute) secondaryObj;

        if (primary.getRollbackRules().isEmpty()) {
            primary.setRollbackRules(secondary.getRollbackRules());
        }
    }

    return primaryObj;
}

}

Comment From: miemieYaho

采纳个啥?

Comment From: ppx-build-code

采纳个啥? mp默认的service的实现类里配置spring Transactional的注解,导致使用多数据源的时候无法指定事物管理器

Comment From: miemieYaho

不用 spring Transactional 用什么?

Comment From: ppx-build-code

不用 spring Transactional 用什么?

我没说不用spring Transactional, 我上面说的方案是可以支持用户自定义事物管理器

Comment From: ppx-build-code

用了Transactional这个注解后相当于指定了默认的事物管理器

Comment From: miemieYaho

那你的意思是什么?

Comment From: ppx-build-code

那你的意思是什么?

首先:这个问题一直存在,我不知道你是否知道。 第二:这个问题是否愿意当成一个问题,我看之前的issues有人提过,你们给出的方案是自己copy一份实现类,个人觉得不够优雅。如果你们不认为这是一个问题我可以马上关掉,也不用继续往下看了。 第三:这个方式是通过实现spring的InstantiationAwareBeanPostProcessor集成,来修改寻找事务管理器、并绑定,对于业务代码侵入性更低 第四:如果你还是不明白我的意思,直接关掉即可 (ps:没有必要摆出这种态度,我只是提个建议)

Comment From: miemieYaho

那你是想把你的解决方法写入文档的常见问题里?

Comment From: ppx-build-code

那你是想把你的解决方法写入文档的常见问题里?

我觉得这可以通过开关或者别的方式集成到框架里

Comment From: miemieYaho

那你给出一个方案吧

Comment From: ppx-build-code

那你给出一个方案吧

ok

Comment From: qmdx

https://github.com/baomidou/dynamic-datasource-spring-boot-starter 中处理