当前使用版本(必填,否则不予处理)
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 中处理