Какой менеджер транзакций я должен использовать для шаблона JBDC При использовании JPA?

Я использую стандартный менеджер транзакций JPA для своих транзакций JPA. Однако теперь я хочу добавить некоторые объекты JDBC, которые совместно используют тот же 'источник данных'. Как я могу сделать операции JDBC транзакционными с пружинной транзакцией? Я должен переключиться на менеджеры транзакций JTA? Действительно ли возможно использовать обе JPA & JDBC транзакционный сервис с тем же источником данных? Еще лучше действительно ли возможно смешать эти две транзакции?

ОБНОВЛЕНИЕ: @Espen:

Мне расширили дао от SimpleJdbcDaoSupport, который использует getSimpleJDBCTemplate.update для вставки строки базы данных. Когда RuntimeException брошен от служебного кода, транзакция никогда не откатывает при использовании JPATransactionManager. Это действительно откатывает при использовании DatasourceTransactionManager. Я пытался отладить JPATransactionManager и кажусь, что он никогда не выполняет откат при лежании в основе JDBCConnection (я предполагаю вследствие того, что источник данных не, обязательно должен быть JDBC для JPA). Моя установка конфигурации точно похожа на Вас объясненный здесь.

Вот мои тестовые коды:

<context:property-placeholder location="classpath:*.properties"/>

<!-- JPA EntityManagerFactory -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="persistenceXmlLocation"
        value="classpath:/persistence-test.xml" />
    <property name="persistenceProvider">
        <bean class="org.hibernate.ejb.HibernatePersistence" />
    </property>

</bean>

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

<!--
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean>
-->

<!-- Database connection pool -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="${database.driverClassName}" />
    <property name="url" value="${database.url}" />
    <property name="username" value="${database.username}" />
    <property name="password" value="${database.password}" />
    <property name="testOnBorrow" value="${database.testOnBorrow}" />
    <property name="validationQuery" value="${database.validationQuery}" />
    <property name="minIdle" value="${database.minIdle}" />
    <property name="maxIdle" value="${database.maxIdle}" />
    <property name="maxActive" value="${database.maxActive}" />
</bean>




<!-- Initialize the database -->
<!--<bean id="databaseInitializer" class="com.vantage.userGroupManagement.logic.StoreDatabaseLoader">
    <property name="dataSource" ref="storeDataSource"/>
</bean>-->

<!-- ANNOTATION SUPPORT -->

<!-- Enable the configuration of transactional behavior based on annotations -->
<tx:annotation-driven transaction-manager="transactionManager"/>

<!-- JPA annotations bean post processor -->
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>

<!-- Exception translation bean post processor (based on Repository annotation) -->
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>

<!-- throws exception if a required property has not been set -->
<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"/>


<bean id="userService" class="com.rfc.example.service.UserServiceImpl"> 
    <property name="userDao" ref="userDao"></property>
    <property name="contactDao" ref="contactDao"></property>
    <property name="callRecordingScheduledProgramTriggerDAO" ref="com.rfc.example.dao.CallRecordingScheduledProgramTriggerDAO"></property>
</bean>

<bean id="userDao" class="com.rfc.example.dao.UserDaoJPAImpl" />

<bean id="contactDao" class="com.rfc.example.dao.ContactDaoJPAImpl"></bean>

<bean id="com.rfc.example.dao.CallRecordingScheduledProgramTriggerDAO" class="com.rfc.example.dao.CallRecordingScheduledProgramTriggerDAOJDBCImpl">
    <property name="dataSource" ref="dataSource"></property>
</bean>

И ВОТ ДАО:

@Transactional
public class CallRecordingScheduledProgramTriggerDAOJDBCImpl  extends SimpleJdbcDaoSupport implements CallRecordingScheduledProgramTriggerDAO{
    private static final Log log = LogFactory.getLog(CallRecordingScheduledProgramTriggerDAOJDBCImpl.class);

@SuppressWarnings("unchecked")
public CallRecordingScheduledProgramTrigger save(
        CallRecordingScheduledProgramTrigger entity) {
    log.debug("save -> entity: " + entity);



    String sql = null;
    Map args = new HashMap();

    String agentIdsString = getAgentIdsString(entity.getAgentIds());


    String insertSQL = "insert into call_recording_scheduled_program_trigger" +
            "       (  queue_id, queue_id_string, agent_ids_string, caller_names, caller_numbers, trigger_id, note, callcenter_id, creator_id_string, creator_id) " +
            " values(:queueId, :queueIdString, :agentIdsString, :callerNames, :callerNumbers, :triggerId, :note, :callcenterId , :creatorIdString, :creatorId  )";

    args.put("queueId", entity.getQueueId());
    args.put("agentIdsString",agentIdsString);
    args.put("callerNames", entity.getCallerNames());       
    args.put("queueIdString", entity.getQueueIdString());
    args.put("callerNumbers", entity.getCallerNumbers());
    args.put("triggerId", entity.getTriggerId());
    args.put("note", entity.getNote());
    args.put("callcenterId", entity.getCallcenterId());
    args.put("creatorId", entity.getCreatorId());
    args.put("creatorIdString", entity.getCreatorIdString());

    sql = insertSQL;
    getSimpleJdbcTemplate().update(sql, args);
    System.out.println("saved: ----------" + entity);
    return entity;
}

}

Вот клиентский код, который называет дао и выдает исключение (пружинный сервис)

@Transactional(propagation=Propagation.REQUIRED)
public void jdbcTransactionTest() {
    System.out.println("entity: " );
    CallRecordingScheduledProgramTrigger entity = new CallRecordingScheduledProgramTrigger();

    entity.setCallcenterId(10L);
    entity.setCreatorId(22L);
    entity.setCreatorIdString("sajid");
    entity.setNote(System.currentTimeMillis() + "");
    entity.setQueueId(22);
    entity.setQueueIdString("dddd");
    String triggerId = "id: " + System.currentTimeMillis();
    entity.setTriggerId(triggerId);
    callRecordingScheduledProgramTriggerDAO.save(entity);

    System.out.println("entity saved with id: " + triggerId );

    throw new RuntimeException();
}

Примечание: код работает как ожидалось при использовании DatasourceTransactionManager

ОБНОВЛЕНИЕ - 2:

Хорошо я нашел первопричину проблемы. Благодаря Espen.

Моя конфигурация менеджера по объекту была похожа на это (скопированный с пружинного приложения клиники для домашних животных):

    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="persistenceXmlLocation"
        value="classpath:/persistence-test.xml" />
    <property name="persistenceProvider">
        <bean class="org.hibernate.ejb.HibernatePersistence" />
    </property>

</bean>

Затем я изменил его для симпатии этого:

    <bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="persistenceXmlLocation"
        value="classpath:/persistence-test.xml" />
    <property name="dataSource" ref="dataSource"/>

    <property name="jpaVendorAdapter">
        <bean
            class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
       <property name="showSql" value="true" />
       <property name="generateDdl" value="true" />
       <property name="databasePlatform" value="org.hibernate.dialect.MySQL5Dialect" />
    </bean>

 </property>
</bean>

Теперь все, кажется, работает! Кто-либо может объяснить, что различие между этими двумя приближается?

22
задан Shantha Kumara 16 August 2013 в 09:03
поделиться

2 ответа

Можно смешивать код JPA и JDBC в одной транзакции с помощью JpaTransactionManager .

Фрагмент из Spring 3 JavaDoc :

Этот диспетчер транзакций также поддерживает прямой доступ к источникам данных в рамках транзакции (т. Е. Простой код JDBC работает с одним и тем же источником данных). Это позволяет смешивать сервисы, которые обращаются к JPA, и сервисы, использующие простой JDBC (не зная о JPA)!

Однако вы должны знать, что JPA кэширует запросы и выполняет их все в конце транзакции. Поэтому, если вы хотите сохранить некоторые данные внутри транзакции с помощью JPA, а затем получить данные с помощью JDBC, это не будет работать без явной очистки контекста сохранения JPA, прежде чем вы попытаетесь получить его с помощью кода JDBC.

Пример кода, который с помощью кода JDBC утверждает, что код JPA удалил строку внутри транзакции:

@Test
@Transactional
@Rollback(false)
public void testDeleteCoffeeType() {

    CoffeeType coffeeType = coffeeTypeDao.findCoffeeType(4L);
    final String caffeForte = coffeeType.getName();

    coffeeTypeDao.deleteCoffeeType(coffeeType);
    entityManager.flush();

    int rowsFoundWithCaffeForte = jdbcTemplate
        .queryForInt("SELECT COUNT(*) FROM COFFEE_TYPES where NAME = ?", 
            caffeForte);
    assertEquals(0, rowsFoundWithCaffeForte);
}

И если вы предпочитаете использовать класс JpaTemplate , просто замените entityManager.flush () с jpaTemplate.flush ();

В ответ на комментарий Саджида: С помощью Spring вы можете настроить диспетчер транзакций, поддерживающий как JPA, так и JDBC, следующим образом:

<tx:annotation-driven transaction-manager="transactionManager" />

<!-- Transaction manager -->
<bean id="transactionManager" class="org.springframework.orm.jpa
            .JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

и версия, управляемая аннотациями

@Bean
public JpaTransactionManager transactionManager(EntityManagerFactory emf) {
    JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
    jpaTransactionManager.setEntityManagerFactory(emf);
    return jpaTransactionManager;
}

. Чтобы заставить его работать, запросы JDBC должны выполняться с помощью класса JdbcTemplate или SimpleJdbcTemplate.В вашем случае с DAO, который расширяет SimpleJdbcDaoSupport, вы должны использовать метод getSimpleJdbcTemplate (..).

И, наконец, чтобы позволить двум методам DAO участвовать в одной транзакции, вызовите оба метода DAO из метода класса службы, аннотированного с помощью @Transactional. С элементом в вашей конфигурации Spring будет обрабатывать транзакцию за вас с помощью данного диспетчера транзакций.

На бизнес-уровне:

public class ServiceClass {..

@Transactional
public void updateDatabase(..) {
  jpaDao.remove(..);
  jdbcDao.insert(..);
}
}

Редактировать 2: Значит, что-то не так. У меня это работает точно так, как указано в документации Javadoc. Имеет ли ваш менеджер сущностей свойство источника данных, подобное моему компоненту ниже? Это будет работать только до тех пор, пока вы вводите один и тот же источник данных в диспетчер сущностей и в свои расширенные классы JpaDaoSupport.

<bean id="entityManagerFactoryWithExternalDataSoure" primary="true"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor
                .HibernateJpaVendorAdapter" />
    </property>
    <property name="jpaProperties">
        <value>
            hibernate.format_sql=true
        </value>
    </property>
</bean>
26
ответ дан 29 November 2019 в 05:29
поделиться

Я еще не проработал это подробно, так как я не смешивал JDBC и JPA, но если у вас есть соединение JDBC для источника данных XA тогда это транзакция JTA. Поэтому, если вы запускаете свой код в сессионном компоненте без сохранения состояния, например, с включенной транзакцией, вы автоматически получаете как свои Entities, так и JDBC, управляемые JTA.

РЕДАКТИРОВАТЬ Вот пример кода из Сервлета

private @Resource DataSource xaDatasource;
private @Resource UserTransaction utx;
private @PersistenceUnit EntityManagerFactory factory;

public void doGet(HttpServletRequest req, HttpServletResponse res) ... {
   utx.begin();
   //Everything below this will be in JTA
   Connection conn = xaDatasource.getConnection();
   EntityManager mgr = factory.createEntityManager();
   //Do your stuff
   ...
   utx.commit();
}

Заявление об ограничении ответственности: Код не протестирован.

Просто поймите, что это не весна, но я все равно оставлю это

0
ответ дан 29 November 2019 в 05:29
поделиться
Другие вопросы по тегам:

Похожие вопросы: