Предисловие: в предыдущей статьеРаспределенные транзакции на основе надежных схем обмена сообщениями: введение в лотереюПредставлено решение общих распределенных транзакций и реализованный автором принцип компонента распределенных транзакций Lottor на основе схемы надежных сообщений, а также показано консольное управление приложением. Прежде чем официально представить конкретную реализацию Lottor, в этой статье сначала будет представлено управление транзакциями в Java, в частности управление транзакциями Spring. PS: Многие читатели спрашивали, является ли Lottor открытым исходным кодом.Вот единый ответ: это открытый исходный код.В настоящее время Lotto используется во внутреннем проекте компании автора, и автор проводит рефакторинг связанного бизнес-кода, который будет обновляться. на GitHub синхронно в следующей статье, следите за обновлениями. Эта статья длинная и подходит для компьютерного чтения. Если вы уже знаете о транзакциях в Java, вы можете пропустить эту статью и присоединиться к этой серии статей.
Типы транзакций Java
Управление транзакциями является неотъемлемой частью разработки прикладной системы. Существует три типа транзакций Java: транзакции JDBC, транзакции JTA (Java Transaction API) и контейнерные транзакции. Общие контейнерные транзакции, такие как транзакции Spring, контейнерные транзакции в основном предоставляются серверами приложений J2EE, а контейнерные транзакции в основном выполняются на основе JTA, который представляет собой довольно сложную реализацию API на основе JNDI. Сначала введите две транзакции в J2EE-разработку: транзакцию JDBC и транзакцию JTA.
JDBC-транзакции
Все действия JDBC, включая транзакции, основаны на Connection, а в JDBC управление транзакциями осуществляется через объект Connection. В JDBC обычно используются методы, связанные с транзакциями: setAutoCommit, commit, rollback и т. д.
По умолчанию, когда мы создаем соединение с базой данных, оно работает в режиме автоматической фиксации. Это означает, что всякий раз, когда мы выполняем SQL, транзакция автоматически фиксируется. Таким образом, каждый выполняемый нами SQL является транзакцией, и если выполняется оператор DML или DDL, изменения сохраняются в базе данных в конце каждого оператора SQL. Иногда мы хотим сделать группу операторов SQL частью транзакции, чтобы мы могли зафиксировать, когда все операторы выполняются успешно, и если возникает какое-либо исключение, эти операторы являются частью транзакции, и мы можем откатить их все.
public static void main(String[] args) {
Connection con = null;
try {
con = DBConnection.getConnection();
con.setAutoCommit(false);
insertAccountData(con, 1, "Pankaj");
insertAddressData(con, 1, "Albany Dr", "San Jose", "USA");
}
catch (SQLException | ClassNotFoundException e) {
e.printStackTrace();
try {
con.rollback();
System.out.println("JDBC Transaction rolled back successfully");
}
catch (SQLException e1) {
System.out.println("SQLException in rollback" + e.getMessage());
}
}
finally {
try {
if (con != null)
con.close();
}
catch (SQLException e) {
e.printStackTrace();
}
}
}
Приведенный выше код реализует простую функцию вставки информации об учетной записи и адресе.Операция вставки управляется транзакциями, либо фиксацией, либо откатом.
JDBC обеспечивает самую базовую поддержку операций транзакций базы данных с использованием Java. Благодаря транзакциям JDBC мы можем поместить несколько операторов SQL в одну и ту же транзакцию, гарантируя, что транзакция JDBC не может охватывать несколько баз данных! Поэтому, если речь идет о операциях с несколькими базами данных или распределенных сценариях, транзакции JDBC бессильны.
JTA-транзакции
Обычно транзакции JDBC могут решить такие проблемы, как согласованность данных.Поскольку его использование относительно просто, многие люди знают транзакции JDBC только о транзакциях в Java, или некоторые люди знают транзакции в фреймворках (таких как Hibernate, Spring) и т. д. Однако, поскольку JDBC не может реализовать распределенные транзакции, а распределенных сценариев сегодня становится все больше и больше, JTA-транзакции появляются по мере необходимости.
JTA (API транзакций Java), API транзакций Java позволяет приложениям выполнять распределенные транзакции, то есть транзакции могут получать доступ к компьютерным ресурсам или обновлять их в двух или более сетях. JTA определяет стандартный интерфейс Java между менеджерами транзакций и сторонами, участвующими в системе распределенных транзакций: приложениями, серверами приложений и менеджерами ресурсов, которые контролируют доступ к общим ресурсам, затронутым транзакциями. Транзакция определяет логическую единицу работы, которая полностью завершается успешно или вообще не дает никакого результата.
Интерфейс программирования транзакций Java (JTA: Java Transaction API) и служба транзакций Java (JTS; служба транзакций Java) предоставляют службы распределенных транзакций для платформы J2EE. Распределенная транзакция (Distributed Transaction) включает диспетчер транзакций (Transaction Manager) и один или несколько диспетчеров ресурсов (Resource Manager), поддерживающих протокол XA. Мы можем думать о диспетчере ресурсов как о постоянном хранилище данных любого типа; диспетчер транзакций отвечает за координацию и контроль всех участвующих в транзакциях единиц. Транзакции JTA эффективно защищают базовые ресурсы транзакций, поэтому приложения могут участвовать в обработке транзакций прозрачным образом; однако по сравнению с локальными транзакциями системные накладные расходы протокола XA велики, и их следует тщательно учитывать в процессе разработки системы. действительно ли нужно распределять дела. Если вам действительно нужны распределенные транзакции для координации нескольких ресурсов транзакций, вам следует реализовать и настроить ресурсы транзакций, поддерживающие протокол XA, такие как JMS, пулы соединений с базой данных JDBC и т. д.
JTA предоставляет java.transaction.UserTransaction, который определяет следующие методы:
- begin() — запустить распределенную транзакцию (в фоновом режиме TransactionManager создаст объект транзакции Transaction и свяжет этот объект с текущим потоком через ThreadLocale)
- commit () — зафиксировать транзакцию (в фоновом режиме TransactionManager достанет объект транзакции из текущего потока и зафиксирует транзакцию, представленную этим объектом)
- rollback() - откатывает транзакцию (в фоновом режиме TransactionManager вынимает объект транзакции из текущего потока и откатывает транзакцию, представленную этим объектом)
- getStatus() - возвращает статус распределенной транзакции, связанной с текущим потоком (объект Status определяет все статусы транзакций)
- setRollbackOnly() — указывает, что распределенная транзакция, связанная с текущим потоком, будет отброшена.
Стоит отметить, что обычные операции JDBC не могут быть напрямую преобразованы в операции JTA с помощью UserTransaction.JTA предъявляет требования к DataSource, Connection и Resource.Участвуют только классы, соответствующие спецификации XA и реализующие соответствующие интерфейсы спецификации XA. в делах JTA.
Что касается спецификации XA, читатели могут дополнить ее согласно предыдущей статье, в настоящее время все основные базы данных поддерживают спецификацию XA. Используемый процесс выглядит следующим образом:
Чтобы использовать транзакции JTA, вам нужен драйвер JDBC, реализующий интерфейсы javax.sql.XADataSource, javax.sql.XAConnection и javax.sql.XAResource. Драйвер, реализующий эти интерфейсы, сможет участвовать в транзакциях JTA. Объект XADataSource является фабрикой для объектов XAConnection. XAConnection — это соединение JDBC, которое участвует в транзакциях JTA.
Чтобы использовать транзакции JTA, вы должны использовать XADataSource для создания соединения с базой данных, которое является соединением XA.
Разница между соединением XA (javax.sql.XAConnection) и соединением без XA (java.sql.Connection) заключается в том, что XA может участвовать в транзакциях JTA и не поддерживает автоматическую фиксацию.
В дополнение к базе данных многие компоненты, такие как JBoss и промежуточное программное обеспечение сообщений JMS ActiveMQ, соответствуют протоколу XA, а 2PC и 3PC также соответствуют спецификации XA. Более конкретное содержание JTA, эта статья не будет расширяться, и позже будет возможность подробно рассказать о JTA.
Весеннее управление транзакциями
Spring поддерживает как программное управление транзакциями, так и декларативное управление транзакциями. Все классы стратегии управления транзакциями Spring наследуются отPlatformTransactionManager
Интерфейс, диаграмма классов выглядит следующим образом:
public interface PlatformTransactionManager {
TransactionStatus getTransaction(TransactionDefinition var1) throws TransactionException;
void commit(TransactionStatus var1) throws TransactionException;
void rollback(TransactionStatus var1) throws TransactionException;
}
в,#getTransaction(TransactionDefinition)
Метод возвращает текущую активную транзакцию или создает новую транзакцию в соответствии с заданным поведением распространения.Параметры в этом методе:TransactionDefinition
класс, который определяет некоторые основные свойства транзакции. Два других метода, а именно фиксация и откат, передаются вTransactionStatus
Значение статуса транзакции.
Свойства транзакции
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
@AliasFor("transactionManager")
String value() default "";
@AliasFor("value")
String transactionManager() default "";
// 定义事务的传播规则
Propagation propagation() default Propagation.REQUIRED;
// 指定事务的隔离级别
Isolation isolation() default Isolation.DEFAULT;
// 对于长时间运行的事务定义超时时间
int timeout() default -1;
// 指定事务为只读
boolean readOnly() default false;
// rollback-for指定事务对哪些检查型异常应当回滚而不提交
Class<? extends Throwable>[] rollbackFor() default {};
// 导致事务回滚的异常类名字数组
String[] rollbackForClassName() default {};
// no-rollback-for指定事务对哪些异常应当继续执行而不回滚
Class<? extends Throwable>[] noRollbackFor() default {};
// 不会导致事务回滚的异常类名字数组
String[] noRollbackForClassName() default {};
}
На самом деле через@Transactional
Вы можете узнать об определении свойств транзакции. Атрибут транзакции описывает, как стратегия транзакции применяется к методу. Атрибут транзакции содержит 5 аспектов:
- распространять поведение
- уровень изоляции
- Правила отката
- время ожидания транзакции
- только для чтения
Свойства распространения транзакций Spring
Поведение распространения определяет границу транзакции между клиентом и вызываемым методом, т. е. правила распространения отвечают на вопрос, следует ли запускать или приостанавливать новую транзакцию или должен ли метод выполняться в контексте транзакции. Весна определяет семьPROPAGATION_
Константа в начале указывает на его свойство распространения. существуетTransactionDefinition
Следующие константы определены в:
название | ценность | объяснять |
---|---|---|
PROPAGATION_REQUIRED | 0 | Поддержите текущую транзакцию, если текущей транзакции нет, создайте новую транзакцию. Это наиболее распространенный выбор и распространение транзакций по умолчанию в Spring. |
PROPAGATION_SUPPORTS | 1 | Поддерживать текущую транзакцию, если текущей транзакции нет, она будет выполнена в нетранзакционном режиме. |
PROPAGATION_MANDATORY | 2 | Поддерживает текущую транзакцию и генерирует исключение, если текущей транзакции нет. |
PROPAGATION_REQUIRES_NEW | 3 | Создать новую транзакцию. Если есть текущая транзакция, приостановить текущую транзакцию. |
PROPAGATION_NOT_SUPPORTED | 4 | Выполнить операцию нетранзакционным способом, приостановив текущую транзакцию, если есть текущая транзакция. |
PROPAGATION_NEVER | 5 | Выполняется без транзакций и выдает исключение, если транзакция уже существует. |
PROPAGATION_NESTED | 6 | Если транзакция уже существует, выполните ее во вложенной транзакции. Если текущих транзакций нет, сделайте что-то похожее на PROPAGATION_REQUIRED. |
Весенний уровень изоляции
Уровень изоляции определяет степень, в которой транзакция может быть затронута другими параллельными транзакциями. Параллелизм нескольких транзакций может привести к различным явлениям чтения, таким как грязное чтение, фантомное чтение и неповторяющееся чтение. существуетTransactionDefinition
Следующие константы определены в:
фрукты | цена | количество |
---|---|---|
ISOLATION_DEFAULT | -1 | Это уровень изоляции по умолчанию PLATFROMTRANSACTINGMANAGER, который использует уровень изоляции транзакции по умолчанию базы данных. Остальные четыре соответствуют уровням изоляции JDBC |
ISOLATION_READ_UNCOMMITTED | 1 | Это минимальный уровень изоляции для транзакции, который обеспечивает другую транзакцию, чтобы увидеть незарегистрированные данные из этой транзакции. Этот уровень изоляции дает грязные чтения, не повторяемые чтения и фантом. |
ISOLATION_READ_COMMITTED | 2 | Гарантируется, что данные, измененные одной транзакцией, могут быть прочитаны другой транзакцией только после ее фиксации. Другая транзакция не может прочитать незафиксированные данные транзакции. |
ISOLATION_REPEATABLE_READ | 4 | Этот уровень изоляции транзакций предотвращает грязное чтение и неповторяющееся чтение. Но могут возникать фантомные чтения. |
ISOLATION_SERIALIZABLE | 8 | Это самый дорогой, но самый надежный уровень изоляции транзакций. Транзакции обрабатываются для последовательного выполнения. Помимо предотвращения грязных и неповторяемых чтений, он также позволяет избежать фантомных чтений. |
только для чтения
Если транзакция выполняет только операции чтения во внутренней базе данных, база данных может использовать функцию только для чтения идентификатора транзакции для выполнения некоторых конкретных оптимизаций. Делая транзакции доступными только для чтения, вы даете базе данных возможность применять оптимизации, которые она считает нужными. Поскольку доступ только для чтения применяется базой данных при запуске транзакции, только те варианты поведения распространения, которые потенциально могут запустить новую транзакцию (PROPAGATION_REQUIRED
,PROPAGATION_REQUIRED_NEW
,PROPAGATION_NESTED
) метод имеет смысл.
время ожидания транзакции
Чтобы приложение работало хорошо, транзакции не могут выполняться слишком долго. Поскольку часы тайм-аута запускаются в начале транзакции, это имеет смысл только для методов, которые имеют поведение распространения (как указано выше), которые могут запускать новую транзакцию. Значением по умолчанию является значение тайм-аута базовой системы транзакций.Если базовая система транзакций базы данных не устанавливает значение тайм-аута, то оно равно нулю и предела тайм-аута нет.
откат транзакции
Правила отката транзакции определяют, какие исключения вызовут откат транзакции, а какие нет. По умолчанию транзакции откатываются только для исключений времени выполнения, а не для проверенных исключений. заключается в том, что выброшенное исключениеRuntimeException
Подкласс (Ошибки также вызывают откат транзакции), в то время как создание проверенного исключения не приведет к откату транзакции. Транзакции могут быть явно настроены для отката транзакций при возникновении этих исключений, включая проверенные исключения. Также можно явно определить те транзакции, для которых не выполняется откат при возникновении исключения. также можно запрограммироватьsetRollbackOnly()
метод, чтобы указать, что транзакция должна быть отменена, после вызоваsetRollbackOnly()
Единственная операция, которую можно выполнить после этого, — это откат.
Настройте диспетчер транзакций Spring
В Spring используйте xml для настройки следующего:
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
Если это Spring Boot, нам просто нужно установитьDataSource
Вот и все,DataSourceTransactionManagerAutoConfiguration
будет автоматически настроенDataSourceTransactionManager
.
@Bean
@ConditionalOnMissingBean(PlatformTransactionManager.class)
public DataSourceTransactionManager transactionManager(
DataSourceProperties properties) {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(
this.dataSource);
if (this.transactionManagerCustomizers != null) {
this.transactionManagerCustomizers.customize(transactionManager);
}
return transactionManager;
}
После настройки менеджера транзакций, конечно, нам все равно придется управлять транзакцией самостоятельно. Spring предоставляет два способа управления транзакциями: программное управление транзакциями и декларативное управление транзакциями. Давайте посмотрим, как их применять.
Программное управление транзакциями
Программное управление транзакциями, которое мы можем использоватьPlatformTransactionManager
Реализованный для управления транзакциями, тот же Spring также предоставляет нам классы-шаблоныTransactionTemplate
Для управления транзакциями ниже в основном представлен класс шаблона.
Класс шаблона
Нам нужно настроить в файле конфигурации:
<!--配置事务管理的模板-->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"></property>
<!--定义事务隔离级别,-1表示使用数据库默认级别-->
<property name="isolationLevelName" value="ISOLATION_DEFAULT"></property>
<property name="propagationBehaviorName" value="PROPAGATION_REQUIRED"></property>
</bean>
TransactionTemplate
Помогает нам инкапсулировать большой объем кода и экономит нашу работу. Таблица счетов специально построена для имитации сцены сбережения денег.
//BaseSeviceImpl.java
@Override
public void insert(String sql, boolean flag) throws Exception {
dao.insertSql(sql);
// 如果flag 为 true ,抛出异常
if (flag) {
throw new Exception("has exception!!!");
}
}
//获取总金额
@Override
public Integer sum() {
return dao.sum();
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring-test.xml"})
public class TransactionTest{
@Resource
private TransactionTemplate transactionTemplate;
@Autowired
private BaseSevice baseSevice;
@Test
public void transTest() {
System.out.println("before transaction");
Integer sum1 = baseSevice.sum();
System.out.println("before transaction sum: "+sum1);
System.out.println("transaction....");
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
try {
baseSevice.insert("INSERT INTO account VALUES (100);",false);
baseSevice.insert("INSERT INTO account VALUES (100);",false);
} catch (Exception e) {
status.setRollbackOnly();
e.printStackTrace();
}
}
});
System.out.println("after transaction");
Integer sum2 = baseSevice.sum();
System.out.println("after transaction sum: "+sum2);
}
}
Когда возникает исключение типа Exception и его необходимо откатить, необходимо перехватить исключение и вызвать объект состояния.setRollbackOnly()
Метод информирует менеджера транзакций о необходимости отката текущей транзакции.
Декларативное управление транзакциями
Существует два распространенных способа декларативного управления транзакциями: один основан на файле конфигурации xml пространств имен tx и aop, а другой — на файле конфигурации xml пространств имен tx и aop.@Transactional
Аннотации, так как версии Spring и Java становятся все выше и выше, все больше и больше они склонны использовать аннотации.
Файлы конфигурации XML на основе пространств имен tx и aop
<tx:advice id="advice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="insert" propagation="REQUIRED" read-only="false" rollback-for="Exception"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="pointCut" expression="execution (* com.blueskykong.service.*.*(..))"/>
<aop:advisor advice-ref="advice" pointcut-ref="pointCut"/>
</aop:config>
@Test
public void transTest() {
System.out.println("before transaction");
Integer sum1 = baseSevice.sum();
System.out.println("before transaction sum: "+sum1);
System.out.println("transaction....");
try{
baseSevice.insert("INSERT INTO account VALUES (100);",true);
} catch (Exception e){
e.printStackTrace();
}
System.out.println("after transaction");
Integer sum2 = baseSevice.sum();
System.out.println("after transaction sum: "+sum2);
}
На основе аннотации @Transactional
Самый распространенный способ также является простым. Просто включите поддержку управления транзакциями аннотаций в файле конфигурации.
<tx:annotation-driven transaction-manager="transactionManager"/>
Класс запуска Spring boot должен открыть транзакцию, и аннотация, используемая для открытия транзакции,@EnableTransactionManagement
.
@Transactional(rollbackFor=Exception.class)
public void insert(String sql, boolean flag) throws Exception {
dao.insertSql(sql);
// 如果flag 为 true ,抛出异常
if (flag){
throw new Exception("has exception!!!");
}
}
Указывает откат при возникновении исключения. Откат проверенных исключений должен быть выполнен. По умолчанию непроверенные исключения, включая ошибки, также будут откатываться автоматически.
Суммировать
В этой статье в основном представлены типы транзакций Java: транзакции JDBC, транзакции JTA (Java Transaction API) и контейнерные транзакции. Кратко представим транзакцию JDBC и транзакцию JTA, а также подробно расскажем об управлении транзакциями Spring.Управление транзакциями Spring реализуется через диспетчер транзакций. Spring предоставляет множество встроенных реализаций диспетчера транзакций, таких как диспетчер транзакций источника данных.DataSourceTransactionManager
, Интегрированное управление транзакциями JPAJpaTransactionManager
Ждать. Соответственно представлены пять аспектов, включенных в свойства транзакций Spring, а также два метода управления транзакциями, предоставляемые Spring: программное управление транзакциями и декларативное управление транзакциями. Среди них декларативное управление транзакциями является наиболее удобным и используется чаще. Я надеюсь, что благодаря вступлению к этой статье читатели познакомятся с транзакциями в Java, когда столкнутся с распределенными транзакциями. В транзакциях JTA фактически вводятся родственные концепции распределенных транзакций, соответствующие спецификациям XA 2PC и 3PC.
Рекомендуемое чтение
Распределенная транзакция на основе надежной схемы обмена сообщениями