Как мне поддерживать несколько sql-запросов к разным базам данных в одной транзакции с использованием spring JPA

#java #mysql #database #hibernate #spring-data-jpa

#java #mysql #База данных #спящий режим #spring-data-jpa

Вопрос:

У меня есть настройки объектов и репозиториев для 2 баз данных

persistence.xml

 <persistence-unit name="user_per_unit" transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
    <class>com.example.User</class>
    <class>com.example.Order</class>
    <class>com.example.Package</class>
    <exclude-unlisted-classes>true</exclude-unlisted-classes>
    <properties>
        <property name="hibernate.show_sql" value="true" />
        <property name="hibernate.format_sql" value="false" />
        <property name="hibernate.hbm2ddl.auto" value="validate" />
        <property name="hibernate.jdbc.batch_size" value="100" />
        <property name="hibernate.order_inserts" value="true" />
        <property name="hibernate.order_updates" value="true" />
        <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
    </properties>
</persistence-unit>

<persistence-unit name="stock_per_unit" transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
    <class>com.example.Stock</class>
    <class>com.example.Graph</class>
    <class>com.example.Change</class>
    <exclude-unlisted-classes>true</exclude-unlisted-classes>
    <properties>
        <property name="hibernate.show_sql" value="true" />
        <property name="hibernate.format_sql" value="false" />
        <property name="hibernate.hbm2ddl.auto" value="validate" />
        <property name="hibernate.jdbc.batch_size" value="100" />
        <property name="hibernate.order_inserts" value="true" />
        <property name="hibernate.order_updates" value="true" />
        <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
    </properties>
</persistence-unit>
  

и в applicationContext.xml

 <jpa:repositories base-package="com.example.user.repository" entity-manager-factory-ref="userEntityManagerFactory" transaction-manager-ref="userTransactionManager"/>

<bean id="userEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="persistenceUnitName"    value="user_per_unit" />
    <property name="dataSource"             ref="userDataSource" />
    <property name="jpaVendorAdapter"       ref="jpaVendorAdapter" />
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.show_sql">true</prop>
            <prop key="hibernate.format_sql">false</prop>
        </props>
    </property>
</bean>

<bean id="userTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="userEntityManagerFactory" />
</bean>

<bean id="userDataSource" class="org.apache.commons.dbcp2.BasicDataSource">
    <property name="driverClassName" value="${driver}" />
    <property name="url" value="${url}" />
    <property name="username" value="${username}" />
    <property name="password" value="${password}" />
    <property name="initialSize" value="${size}" />
    <property name="maxTotal" value="${maxtotal}" />
    <property name="maxIdle" value="${maxidle}" />
    <property name="minIdle" value="${minidle}" />
</bean>

<jpa:repositories base-package="com.example.stock.repository" entity-manager-factory-ref="stockEntityManagerFactory" transaction-manager-ref="stockTransactionManager"/>

<bean id="stockEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="persistenceUnitName"    value="stock_per_unit" />
    <property name="dataSource"             ref="stockDataSource" />
    <property name="jpaVendorAdapter"       ref="jpaVendorAdapter" />
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.show_sql">true</prop>
            <prop key="hibernate.format_sql">false</prop>
        </props>
    </property>
</bean>

<bean id="stockTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="stockEntityManagerFactory" />
</bean>

<bean id="stockDataSource" class="org.apache.commons.dbcp2.BasicDataSource">
    <property name="driverClassName" value="${driver}" />
    <property name="url" value="${url}" />
    <property name="username" value="${username}" />
    <property name="password" value="${password}" />
    <property name="initialSize" value="${size}" />
    <property name="maxTotal" value="${maxtotal}" />
    <property name="maxIdle" value="${maxidle}" />
    <property name="minIdle" value="${minidle}" />
</bean>
  

Теперь в моем классе я выполняю несколько CURD операций в таком методе, как

userRepository.updateUser(...);

userRepository.addUser(...);

userRepository.deleteUser(...);

stockRepository.updateUser(...);

stockRepository.addUser(...);

stockRepository.deleteUser(...);

Как я могу запустить транзакцию сверху, и если что-то не удается в процессе, вернуться к исходному состоянию до того, как я запустил свой метод.

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

1. Совместимы ли ваши драйверы JDBC с XA? Если это так, вам нужно включить диспетчер транзакций JTA вместо простого. Как только вы это сделаете, вам также нужно будет ожидать более медленных транзакций.

2. @TheImpaler Я понятия не имею, совместимы ли драйверы JDBC с XA

3. Вам нужно будет проконсультироваться с поставщиком. Если они не совместимы с XA, вы не сможете создать единую транзакцию, которая охватывает две базы данных. По сути, вам нужно включить двухфазную фиксацию.

4. @TheImpaler У меня есть доступ к администратору mysql, есть ли способ проверить, соответствует ли он требованиям?

5. «Неофициальный» способ, который я использовал, — это получить строку версии драйвера JDBC с помощью Connection.getDatabaseMetaData() , а затем просмотреть ее на веб-сайте MySQL. Тем не менее, я бы официально попросил операционную группу убедиться, что это будет работать в рабочей среде, когда придет время.

Ответ №1:

Я использовал ChainesTransactionManager, чтобы заставить это работать

applicationContext.xml

 <bean id="chainedTxManager" class="org.springframework.data.transaction.ChainedTransactionManager">
    <constructor-arg>
        <list>
            <ref bean="userTransactionManager"/>
            <ref bean="stockTransactionManager"/>
        </list>
    </constructor-arg>
</bean>
  

и использовал аннотацию над моим классом как

@Transactional(value = "chainedTxManager")

Когда когда-либо какой-либо запрос в любой БД завершается с ошибкой, я создаю исключение и его автоматический откат, если исключения нет, его фиксация.