All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jn.sqlhelper.common.transaction.definition.RuleBasedTransactionDefinition Maven / Gradle / Ivy

/*
 * Copyright 2021 the original author or authors.
 *
 * Licensed under the Apache, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at  http://www.gnu.org/licenses/lgpl-2.0.html
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.jn.sqlhelper.common.transaction.definition;


import com.jn.langx.util.Preconditions;
import com.jn.langx.util.collection.Collects;
import com.jn.langx.util.function.Consumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.util.LinkedList;
import java.util.List;

/**
 * TransactionAttribute implementation that works out whether a given exception
 * should cause transaction rollback by applying a number of rollback rules,
 * both positive and negative. If no rules are relevant to the exception, it
 * behaves like DefaultTransactionAttribute (rolling back on runtime exceptions).
 * 

* 这部分是从Spring迁移过来的,为了兼容Spring 事务定义的配置 */ public class RuleBasedTransactionDefinition extends DefaultTransactionDefinition implements Serializable { /** * Prefix for rollback-on-exception rules in description strings */ public static final String PREFIX_ROLLBACK_RULE = "-"; /** * Prefix for commit-on-exception rules in description strings */ public static final String PREFIX_COMMIT_RULE = "+"; /** * Static for optimal serializability */ private static final Logger logger = LoggerFactory.getLogger(RuleBasedTransactionDefinition.class); private List rollbackRules; /** * Create a new RuleBasedTransactionAttribute, with default settings. * Can be modified through bean property setters. * * @see #setName * @see #setRollbackRules */ public RuleBasedTransactionDefinition() { super(); } /** * Create a new DefaultTransactionAttribute with the given * propagation behavior. Can be modified through bean property setters. * TransactionDefinition interface * * @param rollbackRules the list of RollbackRuleAttributes to apply */ public RuleBasedTransactionDefinition(List rollbackRules) { this.rollbackRules = rollbackRules; } /** * Set the list of {@code RollbackRuleAttribute} objects * (and/or {@code NoRollbackRuleAttribute} objects) to apply. * * @see RollbackRuleAttribute * @see NoRollbackRuleAttribute */ public void setRollbackRules(List rollbackRules) { this.rollbackRules = rollbackRules; } /** * Return the list of {@code RollbackRuleAttribute} objects * (never {@code null}). */ public List getRollbackRules() { if (this.rollbackRules == null) { this.rollbackRules = new LinkedList(); } return this.rollbackRules; } /** * Winning rule is the shallowest rule (that is, the closest in the * inheritance hierarchy to the exception). If no rule applies (-1), * return false. */ @Override public boolean rollbackOn(Throwable ex) { if (logger.isTraceEnabled()) { logger.trace("Applying rules to determine whether transaction should rollback on " + ex); } RollbackRuleAttribute winner = null; int deepest = Integer.MAX_VALUE; if (this.rollbackRules != null) { for (RollbackRuleAttribute rule : this.rollbackRules) { int depth = rule.getDepth(ex); if (depth >= 0 && depth < deepest) { deepest = depth; winner = rule; } } } if (logger.isTraceEnabled()) { logger.trace("Winning rollback rule is: " + winner); } // User superclass behavior (rollback on unchecked) if no rule matches. if (winner == null) { logger.trace("No relevant rollback rule found: applying default rules"); return super.rollbackOn(ex); } return !(winner instanceof NoRollbackRuleAttribute); } @Override public String toString() { StringBuilder result = new StringBuilder(256); if (this.rollbackRules != null) { for (RollbackRuleAttribute rule : this.rollbackRules) { String sign = (rule instanceof NoRollbackRuleAttribute ? PREFIX_COMMIT_RULE : PREFIX_ROLLBACK_RULE); result.append(',').append(sign).append(rule.getExceptionName()); } } return result.toString(); } /** * Rule determining whether or not a given exception (and any subclasses) * should cause a rollback. * *

Multiple such rules can be applied to determine whether a transaction * should commit or rollback after an exception has been thrown. * * @see NoRollbackRuleAttribute */ public static class RollbackRuleAttribute implements Serializable { /** * The {@link RollbackRuleAttribute rollback rule} for * {@link RuntimeException RuntimeExceptions}. */ public static final RollbackRuleAttribute ROLLBACK_ON_RUNTIME_EXCEPTIONS = new RollbackRuleAttribute(RuntimeException.class); /** * Could hold exception, resolving class name but would always require FQN. * This way does multiple string comparisons, but how often do we decide * whether to roll back a transaction following an exception? */ private final String exceptionName; /** * Create a new instance of the {@code RollbackRuleAttribute} class. *

This is the preferred way to construct a rollback rule that matches * the supplied {@link Exception} class (and subclasses). * * @param clazz throwable class; must be {@link Throwable} or a subclass * of {@code Throwable} * @throws IllegalArgumentException if the supplied {@code clazz} is * not a {@code Throwable} type or is {@code null} */ public RollbackRuleAttribute(Class clazz) { Preconditions.checkNotNull(clazz, "'clazz' cannot be null"); if (!Throwable.class.isAssignableFrom(clazz)) { throw new IllegalArgumentException( "Cannot construct rollback rule from [" + clazz.getName() + "]: it's not a Throwable"); } this.exceptionName = clazz.getName(); } /** * Create a new instance of the {@code RollbackRuleAttribute} class * for the given {@code exceptionName}. *

This can be a substring, with no wildcard support at present. A value * of "ServletException" would match * {@code javax.servlet.ServletException} and subclasses, for example. *

NB: Consider carefully how specific the pattern is, and * whether to include package information (which is not mandatory). For * example, "Exception" will match nearly anything, and will probably hide * other rules. "java.lang.Exception" would be correct if "Exception" was * meant to define a rule for all checked exceptions. With more unusual * exception names such as "BaseBusinessException" there's no need to use a * fully package-qualified name. * * @param exceptionName the exception name pattern; can also be a fully * package-qualified class name * @throws IllegalArgumentException if the supplied * {@code exceptionName} is {@code null} or empty */ public RollbackRuleAttribute(String exceptionName) { Preconditions.checkNotEmpty(exceptionName, "'exceptionName' cannot be null or empty"); this.exceptionName = exceptionName; } /** * Return the pattern for the exception name. */ public String getExceptionName() { return exceptionName; } /** * Return the depth of the superclass matching. *

{@code 0} means {@code ex} matches exactly. Returns * {@code -1} if there is no match. Otherwise, returns depth with the * lowest depth winning. */ public int getDepth(Throwable ex) { return getDepth(ex.getClass(), 0); } private int getDepth(Class exceptionClass, int depth) { if (exceptionClass.getName().contains(this.exceptionName)) { // Found it! return depth; } // If we've gone as far as we can go and haven't found it... if (exceptionClass == Throwable.class) { return -1; } return getDepth(exceptionClass.getSuperclass(), depth + 1); } @Override public boolean equals(Object other) { if (this == other) { return true; } if (!(other instanceof RollbackRuleAttribute)) { return false; } RollbackRuleAttribute rhs = (RollbackRuleAttribute) other; return this.exceptionName.equals(rhs.exceptionName); } @Override public int hashCode() { return this.exceptionName.hashCode(); } @Override public String toString() { return "RollbackRuleAttribute with pattern [" + this.exceptionName + "]"; } } /** * Tag subclass of {@link RollbackRuleAttribute} that has the opposite behavior * to the {@code RollbackRuleAttribute} superclass. */ public static class NoRollbackRuleAttribute extends RollbackRuleAttribute { /** * Create a new instance of the {@code NoRollbackRuleAttribute} class * for the supplied {@link Throwable} class. * * @param clazz the {@code Throwable} class * @see RollbackRuleAttribute#RollbackRuleAttribute(Class) */ public NoRollbackRuleAttribute(Class clazz) { super(clazz); } /** * Create a new instance of the {@code NoRollbackRuleAttribute} class * for the supplied {@code exceptionName}. * * @param exceptionName the exception name pattern * @see RollbackRuleAttribute#RollbackRuleAttribute(String) */ public NoRollbackRuleAttribute(String exceptionName) { super(exceptionName); } @Override public String toString() { return "No" + super.toString(); } } public static final List buildRules(Class[] rollbackFor, Class[] noRollbackFor) { final List rules = Collects.emptyArrayList(); Collects.forEach(noRollbackFor, new Consumer() { @Override public void accept(Class aClass) { rules.add(new RuleBasedTransactionDefinition.NoRollbackRuleAttribute(aClass)); } }); Collects.forEach(rollbackFor, new Consumer() { @Override public void accept(Class aClass) { rules.add(new RuleBasedTransactionDefinition.RollbackRuleAttribute(aClass)); } }); return rules; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy