Как сделать транзакцию в jdbc
Using Transactions
There are times when you do not want one statement to take effect unless another one completes. For example, when the proprietor of The Coffee Break updates the amount of coffee sold each week, the proprietor will also want to update the total amount sold to date. However, the amount sold per week and the total amount sold should be updated at the same time; otherwise, the data will be inconsistent. The way to be sure that either both actions occur or neither action occurs is to use a transaction. A transaction is a set of one or more statements that is executed as a unit, so either all of the statements are executed, or none of the statements is executed.
This page covers the following topics
Disabling Auto-Commit Mode
When a connection is created, it is in auto-commit mode. This means that each individual SQL statement is treated as a transaction and is automatically committed right after it is executed. (To be more precise, the default is for a SQL statement to be committed when it is completed, not when it is executed. A statement is completed when all of its result sets and update counts have been retrieved. In almost all cases, however, a statement is completed, and therefore committed, right after it is executed.)
The way to allow two or more statements to be grouped into a transaction is to disable the auto-commit mode. This is demonstrated in the following code, where con is an active connection:
Committing Transactions
The statement con.setAutoCommit(true); enables auto-commit mode, which means that each statement is once again committed automatically when it is completed. Then, you are back to the default state where you do not have to call the method commit yourself. It is advisable to disable the auto-commit mode only during the transaction mode. This way, you avoid holding database locks for multiple statements, which increases the likelihood of conflicts with other users.
Using Transactions to Preserve Data Integrity
In addition to grouping statements together for execution as a unit, transactions can help to preserve the integrity of the data in a table. For instance, imagine that an employee was supposed to enter new coffee prices in the table COFFEES but delayed doing it for a few days. In the meantime, prices rose, and today the owner is in the process of entering the higher prices. The employee finally gets around to entering the now outdated prices at the same time that the owner is trying to update the table. After inserting the outdated prices, the employee realizes that they are no longer valid and calls the Connection method rollback to undo their effects. (The method rollback aborts a transaction and restores values to what they were before the attempted update.) At the same time, the owner is executing a SELECT statement and printing the new prices. In this situation, it is possible that the owner will print a price that had been rolled back to its previous value, making the printed price incorrect.
This kind of situation can be avoided by using transactions, providing some level of protection against conflicts that arise when two users access data at the same time.
To avoid conflicts during a transaction, a DBMS uses locks, mechanisms for blocking access by others to the data that is being accessed by the transaction. (Note that in auto-commit mode, where each statement is a transaction, locks are held for only one statement.) After a lock is set, it remains in force until the transaction is committed or rolled back. For example, a DBMS could lock a row of a table until updates to it have been committed. The effect of this lock would be to prevent a user from getting a dirty read, that is, reading a value before it is made permanent. (Accessing an updated value that has not been committed is considered a dirty read because it is possible for that value to be rolled back to its previous value. If you read a value that is later rolled back, you will have read an invalid value.)
How locks are set is determined by what is called a transaction isolation level, which can range from not supporting transactions at all to supporting transactions that enforce very strict access rules.
Isolation Level | Transactions | Dirty Reads | Non-Repeatable Reads | Phantom Reads |
---|---|---|---|---|
TRANSACTION_NONE | Not supported | Not applicable | Not applicable | Not applicable |
TRANSACTION_READ_COMMITTED | Supported | Prevented | Allowed | Allowed |
TRANSACTION_READ_UNCOMMITTED | Supported | Allowed | Allowed | Allowed |
TRANSACTION_REPEATABLE_READ | Supported | Prevented | Prevented | Allowed |
TRANSACTION_SERIALIZABLE | Supported | Prevented | Prevented | Prevented |
A non-repeatable read occurs when transaction A retrieves a row, transaction B subsequently updates the row, and transaction A later retrieves the same row again. Transaction A retrieves the same row twice but sees different data.
A phantom read occurs when transaction A retrieves a set of rows satisfying a given condition, transaction B subsequently inserts or updates a row such that the row now meets the condition in transaction A, and transaction A later repeats the conditional retrieval. Transaction A now sees an additional row. This row is referred to as a phantom.
Setting and Rolling Back to Savepoints
The method begins by creating a Savepoint with the following statement:
The method checks if the new price is greater than the maximumPrice value. If so, the method rolls back the transaction with the following statement:
Consequently, when the method commits the transaction by calling the Connection.commit method, it will not commit any rows whose associated Savepoint has been rolled back; it will commit all the other updated rows.
Releasing Savepoints
The method Connection.releaseSavepoint takes a Savepoint object as a parameter and removes it from the current transaction.
After a savepoint has been released, attempting to reference it in a rollback operation causes a SQLException to be thrown. Any savepoints that have been created in a transaction are automatically released and become invalid when the transaction is committed, or when the entire transaction is rolled back. Rolling a transaction back to a savepoint automatically releases and makes invalid any other savepoints that were created after the savepoint in question.
When to Call Method rollback
Руководство по JDBC. Транзакции.
Когда мы работаем с JDBC, то по умолчанию наше соединение работает в режиме auto-commit, это означает, что каждый SQL – запрос будет выполнен и результаты будут сохранены в таблице нашей базы данных (далее – БД).
Для простых приложений это крайне удобно. Но, если мы хотим увеличить производительность, использовать распределённые транзакции, либо интегрировать бизнес-логику, то нам необходимо выключить режим auto-commit для управления нашими транзакциями.
Транзакцию дают нам возможность контролировать когда и где сохранять изменения в БД. Благодаря этому мы, например, можем объединить группу SQL – запросов в одну логическую группу и, если один из запросов не пройдёт – мы отменяем всю транзакцию.
Для того чтобы получить доступ к управлению транзакциями, нам необходимо использовать метод setAutoCommit().
В коде это выглядит следующим образом:
Выполнение и откат (Commit and Rollback)
После того, как мы выполнили необходимые нам изменения, мы должны вызвать метод commit() таким образом:
Если же мы хотим выполнить откат изменений, то нам необходимо вызвать метод rollback():
Точки сохранения (Savepoints)
Начиниая со спецификации JDBC 3.0 интерфейс Savepoint даёт нам ещё больший контроль над транзакциями.
Когда мы используем savepoint, мы определяем точку, до которой произойдёт откат в случае, если нам понадобится отменить изменения.
Для управления этой функцией существует два метода:
Для понимания того, как это работает на практике рассмотрим пример простого приложения.
В результате работы программы мы получим, примерно, следующий результат:
Как мы видим, в этом приложении мы добавляем две записи с помощью двух SQL – запросов. Первый запрос корректен и выполняется без проблем. Второй же запрос содержит синтаксические ошибки и программа выдаёт нам SQLException.
Но метод commit() мы вызываем только после второго запроса. Поэтому, после выполнения отката, мы получаем таблицу в исходном виде, так как первый запрос также был отменён.
В этом уроке мы изучили основы управления транзакциями и рассмотрели пример с применением методов commit(), rollback() и использованием точки сохранения (savepoint).
В следующем уроке мы изучим исключения и их обработку при работе с JDBC.
Введение в транзакции в Java и Spring
Краткое и практическое руководство по транзакциям в Java и Spring.
1. введение
В этом уроке мы поймем, что подразумевается под транзакциями в Java. Таким образом, мы поймем, как выполнять локальные транзакции ресурсов и глобальные транзакции. Это также позволит нам изучить различные способы управления транзакциями в Java и Spring.
2. Что такое Транзакция?
Кроме того, эти транзакции могут включать один или несколько ресурсов, таких как база данных, очередь сообщений, что приводит к различным способам выполнения действий в рамках транзакции. Они включают выполнение локальных транзакций ресурсов с отдельными ресурсами. В качестве альтернативы в глобальной транзакции могут участвовать несколько ресурсов.
3. Локальные транзакции Ресурсов
В Java у нас есть несколько способов доступа к ресурсу, такому как база данных, и работы с ним. Следовательно, то, как мы работаем с транзакциями, также не одно и то же. В этом разделе мы узнаем, как мы можем использовать транзакции с некоторыми из этих библиотек в Java, которые довольно часто используются.
3.1. JDBC
Однако, если мы хотим объединить несколько операторов в одну транзакцию, этого также можно достичь:
3.2. JPA
Java Persistence API (JPA) – это спецификация в Java, которая может использоваться для преодоления разрыва между объектно-ориентированными моделями предметной области и системами реляционных баз данных|/. Таким образом, существует несколько реализаций JPA, доступных от третьих сторон, таких как Hibernate, EclipseLink и iBatis.
Давайте посмотрим, как мы можем создать EntityManager и определить границу транзакции вручную:
3.3. JMS
Давайте посмотрим, как мы можем создать транзакцию Сеанс для отправки нескольких сообщений в рамках транзакции:
4. Глобальные Транзакции
4.1. JTA
JTA определяет стандартные интерфейсы Java между менеджером транзакций и другими сторонами распределенной транзакции:
Давайте разберемся в некоторых ключевых интерфейсах, выделенных выше:
4.2. JTS
На высоком уровне он поддерживает API транзакций Java (JTA). Менеджер транзакций JTS предоставляет услуги по транзакциям сторонам, участвующим в распределенной транзакции:
Службы, которые JTS предоставляет приложению, в значительной степени прозрачны, и поэтому мы можем даже не заметить их в архитектуре приложения. Архитектура JTS построена вокруг сервера приложений, который абстрагирует всю семантику транзакций от прикладных программ.
5. Управление транзакциями JTA
5.1. JTA в сервере приложений
Кроме того, у нас есть выбор с точки зрения того, как мы хотим управлять границей транзакции в нашем приложении. Это приводит к двум типам транзакций на сервере приложений Java:
5.2. Автономный JTA
Теперь Atomikos предоставляет нам последний кусочек головоломки, чтобы собрать все вместе, экземпляр UserTransaction :
Теперь мы готовы создать приложение с распределенной транзакцией, охватывающей всю нашу базу данных и очередь сообщений:
6. Поддержка транзакций весной
Мы видели, что обработка транзакций – это довольно сложная задача, которая включает в себя множество шаблонного кодирования и конфигураций. Кроме того, каждый ресурс имеет свой собственный способ обработки локальных транзакций. В Java JTA абстрагирует нас от этих вариаций, но дополнительно привносит детали, зависящие от поставщика, и сложность сервера приложений.
Платформа Spring предоставляет нам гораздо более чистый способ обработки транзакций, как локальных, так и глобальных транзакций ресурсов в Java. Это вместе с другими преимуществами Spring создает убедительные аргументы в пользу использования Spring для обработки транзакций. Кроме того, довольно легко настроить и переключить диспетчер транзакций с помощью Spring, который может быть предоставлен сервером или автономным.
6.1. Конфигурации
Классы UserTransactionImp и UserTransactionManager предоставляются Atomikos здесь.
6.2. Управление транзакциями
Пройдя через все конфигурации в последнем разделе, мы должны чувствовать себя совершенно ошеломленными! В конце концов, мы можем даже усомниться в преимуществах использования Spring. Но помните, что вся эта конфигурация позволила нам абстрагироваться от большинства стандартных шаблонов, специфичных для поставщика, и наш фактический код приложения вообще не должен об этом знать.
Итак, теперь мы готовы изучить, как использовать транзакции весной, когда мы намерены обновить базу данных и опубликовать сообщения. Весна предоставляет нам два способа достичь этого с их собственными преимуществами на выбор. Давайте разберемся, как мы можем их использовать:
Приведенного выше простого кода достаточно, чтобы разрешить операцию сохранения в базе данных и операцию публикации в очереди сообщений в транзакции JTA.
7. Запоздалые мысли
Как мы уже видели, обработка транзакций, особенно тех, которые охватывают несколько ресурсов, сложна. Кроме того, транзакции по своей сути блокируются, что наносит ущерб задержке и пропускной способности приложения. Кроме того, тестирование и поддержка кода с распределенными транзакциями непросто, особенно если транзакция зависит от базового сервера приложений. Таким образом, в целом, лучше всего вообще избегать транзакций, если мы можем!
8. Заключение
Подводя итог, в этом уроке мы обсудили транзакции в контексте Java. Мы прошли через поддержку локальных транзакций отдельных ресурсов в Java для разных ресурсов. Мы также рассмотрели способы достижения глобальных транзакций на Java.
Далее мы рассмотрели различные способы управления глобальными транзакциями на Java. Кроме того, мы поняли, как Spring облегчает нам использование транзакций в Java.
Наконец, мы рассмотрели некоторые из лучших практик при работе с транзакциями на Java.
Как сделать транзакцию в jdbc
В предыдущей статье мы только познакомились с JDBC и написали простое приложение, которое позволило нам соединиться с СУБД и получить данные с помощью SQL-запроса. Хоть программа и не очень сложная, но на мой взгляд, мы сделали весьма важный шаг — мы смогли соединиться с базой данных и сделать пусть и простой, но запрос. Все, что мы рассмотрим дальше — это уже более удобные и более профессиональные способы использования JDBC.
Запросы на получение данных и запросы на обновление
SQL-запросы можно условно разделить на две группы:
Для первой группы используется уже знакомый нам метод интерфейса Statement — executeQuery(). В принципе для начала этого метода вполне достаточно. Он покрывает очень большой процент запросов, которые разрабатываются для реальных систем. Позже мы познакомимся с дополнительными возможностями, но на данных момент советую запомнить — если надо получить данные из таблицы, то executeQuery в подавляющем большинстве случаев будет самым правильным выбором.
Для второй группа запросов (опять же в большинстве случаев) может использоваться другой метод интерфейса Statement — executeUpdate(). Есл посмотреть документацию, то в отличии от executeQuery() (который возвращает ResultSet) этот метод возвращает целое число, которое говорит сколько строк в таблице было изменено при исполнении вашего запроса.
Например, вы можете оператором DELETE FROM JC_CONTACT удалить ВСЕ строки (посему будьте очень аккуратны). В этом случае метод executeUpdate() вернет количество удаленных строк. В некоторых ситуациях знание о количестве измененных строк бывает удобным для построения алгоритмов работы с данными.
В принципе с этим вопросов можно закончить — главное мы уже увидели. Для выборки данных — executeQuery(). Для изменения данных — executeUpdate().
Разновидности Statement
Самый простой интерфейс Statement мы уже видели. И хотя он вполне пригоден для работы, для сложных запросов он подходит не так хорошо. В некоторых источниках высказывается мнение, что использовать Statement вообще не надо — вместо него подходят более сложные и более функционально насыщенные интерфейсы.
PreparedStatement
Управление транзакциями, commit rollback
Транзакция Transaction включает одно или несколько изменений в базе данных, которые после выполнения либо все фиксируются (commit), либо все откатываются назад (rollback). При вызове метода commit или rollback текущая транзакция заканчивается и начинается другая.
По умолчанию каждое новое соединение находится в режиме автофиксации (autocommit = true). Это означает автоматическую фиксацию (commit) транзакции после выполнения каждого запроса. В этом случае транзакция включает только одно изменение (один запрос).
Если autocommit запрещен, т.е. равен false, то транзакция не заканчивается до явного вызова commit или rollback, включая, таким образом, все выражения, выполненные с момента последнего вызова commit или rollback. В этом случае все SQL-запросы в транзакции фиксируются или откатываются группой.
Иногда пользователю нужно, чтобы какое-либо изменение не вступило в силу до тех пор, пока не вступит в силу предыдущее изменение. Этого можно достичь запрещением autocommit и группировкой обоих запросов в одну транзакцию. Если оба изменения произошли успешно, то вызывается метод commit, который переносит эффект от этих изменений в БД; если одно или оба запроса не прошли, то вызывается метод rollback, который возвращает прежнее состояние БД.
Большинство JDBC-драйверов поддерживают транзакции. В действительности драйвер, соответствующий спецификации JDBC, обязан поддерживать их. Интерфейс DatabaseMetaData позволяет получить информацию об уровнях изолированности транзакций, которые поддерживаются данной СУБД.
В примере для соединения Connection режим автофиксации отключен и два оператора updateSales и updateTotal будут зафиксированы вместе при вызове метода commit.
В последней строке примера режим автофиксации autocommit восстанавливается. То есть, каждый следующий запрос опять будет фиксироваться автоматически после своего завершения.
Желательно запрещать режим автофиксации только тогда, когда в транзакции участвуют данные из нескольких таблиц, чтобы связанные данные нескольких таблиц либо были все записаны, либо отменены.
Уровни изолированности транзакций, dirty read
Транзакции не только обеспечивают полное завершение или откат операторов, которые они охватывают, но также изолируют данные. Уровень изоляции описывает степень видимости измененных данных для других транзакций.
Есть несколько способов разрешения конфликтов между одновременно выполняющимися транзакциями. Разработчик может определить уровень изолированности так, что пока одна транзакция изменяет какое-либо значение, вторая транзакция могла бы прочитать обновленное значение до того, пока первая не выполнит commit или rollback. Для этого следует установить уровень изолированности TRANSACTION_READ_UNCOMMITTED:
В данном коде серверу указано на возможность чтения измененных значений до того, как выполнится commit, т.е. определена возможность «грязного чтения» («dirty read«).
По умолчанию уровень изоляции транзакций обычно установлен в READ_COMMITED.
Изменение уровня изолированности во время транзакции нежелательно, так как произойдет автоматический вызов commit, что повлечет за собой фиксацию изменений.
В связи с тем, что уровни изоляции, предлагаемые различными поставщиками СУБД, могут меняться, Вам следует обратиться к документации за дополнительной информацией. Уровни изоляции не стандартизованы для платформы J2EE.
Обычно, чем выше уровень изолированности, тем медленнее выполняется приложение (из-за избыточной блокировки). При выборе конкретного уровня изолированности разработчик должен найти золотую середину между потребностями в производительности и требованиями к целостности данных. Очевидно, что реально поддерживаемые уровни зависят от возможностей используемой СУБД.
При создании объекта Connection уровень его изолированности зависит от драйвера или БД. Можно вызвать метод setIsolationLevel, чтобы изменить уровень изолированности транзакций, и новое значение уровня будет установлено до конца сессии. Чтобы установить уровень изолированности только для одной транзакции, надо установить его перед выполнением транзакции и восстановить прежнее значение после ее завершения.