1.19.2015

15. Транзакции JTA.



Каждая реляционная база данных поддерживает транзакции,  под транзакцией понимается законченная работа над данными, результатом транзакции может быть одно значение:  фиксация работы над данными или откат к  прежнему состоянию в случае ошибки.  Все транзакции разделяются на два типа, на управляемые и неуправляемые. Начнем с самых простых, неуправляемых они реализуются посредством интерфейса JDBC API.  Вы начинаете транзакцию, обычно с вызова метода  connection. setAutoCommit(false); и заканчиваете методом .commit();  в случае успеха или .rollback();  в случае неудачи.  Все примеры, которые мы реализовали с помощью hibernate в прошлых главах, использовали по умолчанию соединение посредством интерфейса JDBC API. Мы конечно не прямо работали с интерфейсом  JDBC API, а косвенно через hibernate обертку, конкретно с транзакциями работали через  интерфейс  org.hibernate.Transaction.   В программах, которые работают с несколькими системами баз данных  и одновременно меняют данные в двух или более системах нужен другой механизм фиксации изменений.  Для этого используют менеджер транзакций и распределенные транзакции  с двух фазным коммитом,  эти транзакции реализуются посредством Java Transaction API (JTA) . Такие транзакции называются управляемыми, и работа с ними происходит  обычно на сервере приложений, но есть так же Open Source  реализации JTA, такие например как  Atomikos.  JTA используется не только как распределенные транзакции, но и в декларативных управляемых транзакциях контейнера, называемых Container  Manager Transaction (CMT).  У CMT  начало транзакции определяется на основе какого-либо дескриптора,  который обычно применим к методу  DAO класса” или методу EJB.
Рассмотрим простой пример использования  jta транзакции, для этого нам потребуется две вещи,  нужен менеджер транзакций, возьмем Atomikos, и  две библиотеки которые позволят нам создать JNDI сервер и разместить в нем наш connection pool.  И так внесем изменения в наш файл настроек  hibernate.cfg.xml:

<?xml version='1.0' encoding='utf-8'?>
<hibernate-configuration>
    <session-factory>
        <!-- работаем с нашим JNDI  пуллом -->
        <property name="connection.datasource">testDS1</property>
        <property name="dialect">org.hibernate.dialect.DerbyDialect</property>
        <!-- устанавливаем тип сессии для работы  c jta  транзакциями -->
        <property name="current_session_context_class">jta</property>
        <!—дадим подсказку hibernate через какой класс он может взаимодействовать с менеджером транзакций Atomikos -->
        <property name="transaction.manager_lookup_class">
com.atomikos.icatch.jta.hibernate3.TransactionManagerLookup</property>
        <mapping class="org.vit.ch1.Person"/>
    </session-factory>
</hibernate-configuration>

Смотрим на листинг примера, комментарии по ходу кода:

public class Main {
    private static UserTransaction userTransaction;
    private static AtomikosDataSourceBean dataSource1;
    private static AtomikosDataSourceBean dataSource2;

    public static void main(String[] args) throws Exception {
        initDataSources(); /* создаем два datasources  и привязываем их к нашему JNDI серверу.     */
        initTransactionManager(); /*   подготавливаем транзакцию.     */
        SessionFactory sf1 = new Configuration().configure("/hibernate.cfg.xml").buildSessionFactory();
        SessionFactory sf2 = new Configuration().configure("/hibernate1.cfg.xml").buildSessionFactory();
        userTransaction.setTransactionTimeout(600);
        userTransaction.begin();// начинаем jta транзакцию
        try {
            System.out.println("*** работаем с первой DB1 ***");
            doWork(sf1, "user");
            System.out.println("***работаем со второй DB2 ***");
            doWork(sf2, "user");
            userTransaction.commit();// подтверждаем нашу  jta транзакцию
        } catch (Exception ex) {
            ex.printStackTrace();
            userTransaction.rollback();//откат транзакции если что-то пошло не так.
        }
        sf1.close();
        sf2.close();
        dataSource1.close();
        dataSource2.close();
        System.out.println("Both databases updated successfully");
    }

    /**    создаем и выводим данные.     */
    private static void doWork(SessionFactory sf, String username) {
        Session session = sf.getCurrentSession();
        persistPerson(session, username);
        listPersons(session);
        session.close();
    }

    /**  выводим все данные.     */
    private static void listPersons(Session session) {
        List users = session.createQuery("from Person ").list();
        for (int i = 0; i < users.size(); i++) {
            Person person = (Person) users.get(i);
            System.out.println(person.toString());
        }
    }

    /**     создаем person     */
    private static void persistPerson(Session session, String userName) {
        Person u = new Person();
        u.setFirstName(userName);
        session.save(u);
    }

    /**   подготавливаем транзакцию.     */
    private static void initTransactionManager() {
        userTransaction = new com.atomikos.icatch.jta.UserTransactionImp();
    }

    /**     создаем два datasources  и привязываем их к нашему JNDI серверу.     */
    private static void initDataSources() throws NamingException {
        Context ctx = new InitialContext();
        dataSource1 = getDataSource_db1();
        ctx.rebind("testDS1", dataSource1);
        dataSource2 = getDataSource_db2();
        ctx.rebind("testDS2", dataSource2);
        ctx.close();
    }

    /*     создаем первый datasource.    */
    private static AtomikosDataSourceBean getDataSource_db1() {
        AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
        ds.setUniqueResourceName("derby1");
        ds.setXaDataSourceClassName("org.apache.derby.jdbc.EmbeddedXADataSource");
        Properties p = new Properties();
        p.setProperty("databaseName","users1");
        ds.setXaProperties(p);
        ds.setPoolSize(3);
        return ds;
    }

     /*     создаем второй datasource.    */
    private static AtomikosDataSourceBean getDataSource_db2() {
        AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
        ds.setUniqueResourceName("derby2");
        ds.setXaDataSourceClassName("org.apache.derby.jdbc.EmbeddedXADataSource");
        Properties p = new Properties();
        p.setProperty("databaseName","users2");
        ds.setXaProperties(p);
        ds.setPoolSize(3);
        return ds;
    }
}

Из примера видно, что для работы с JTA  транзакциями hibernate  нужно только  получить  через менеджер транзакций,  jta транзакцию и работать с ней как с обычной транзакцией, все остальное за нас сделает hibernate.

Комментариев нет:

Отправить комментарий