2015-03-20 2 views
3

Я пишу приложение, которое соединяется с Oracle Database. Я вызываю функцию из БД, которая вставляет новые записи в таблицу. И после этого обратного вызова я могу решить, что я хочу сделать: фиксация или откат.Настроить транзакцию весной 4.1.5 без XML

Unfortunalety Я новичок весной, поэтому у меня проблемы с конфигурацией. И более того, я хочу сделать эту конфигурацию в Java-классе, а не в XML. И здесь мне нужна твоя помощь.

ОБНОВЛЕНО КОД:

ApplicationConfig код:

@Configuration 
@EnableTransactionManagement 
@ComponentScan("hr") 
@PropertySource({"classpath:jdbc.properties", "classpath:functions.properties", "classpath:procedures.properties"}) 
public class ApplicationConfig { 

    @Autowired 
    private Environment env; 

    @Bean(name="dataSource") 
    public DataSource dataSource() { 
     BasicDataSource dataSource = new BasicDataSource(); 
     dataSource.setDriverClassName(env.getProperty("jdbc.driver")); 
     dataSource.setUrl(env.getProperty("jdbc.url")); 
     dataSource.setUsername(env.getProperty("jdbc.username")); 
     dataSource.setPassword(env.getProperty("jdbc.password")); 
     dataSource.setDefaultAutoCommit(false); 
     return dataSource; 
    } 

    @Bean 
    public JdbcTemplate jdbcTemplate(DataSource dataSource) { 
     JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); 
     jdbcTemplate.setResultsMapCaseInsensitive(true); 
     return jdbcTemplate; 
    } 

    @Bean(name="txName") 
    public PlatformTransactionManager txManager() { 
     DataSourceTransactionManager txManager = new DataSourceTransactionManager(); 
     txManager.setDataSource(dataSource()); 
     return txManager; 
    } 
} 

У меня есть Дао и сервис, где и реализует соответствующий интерфейс.

реализация Услуги: реализация

@Service 
public class HumanResourcesServiceImpl implements HumanResourcesService { 

    @Autowired 
    private HumanResourcesDao hrDao; 

    @Override 
    public String generateData(int rowsNumber) { 
     return hrDao.generateData(rowsNumber); 
    } 

    @Override 
    @Transactional("txName") 
    public void shouldCommit(boolean doCommit, Connection connection) throws SQLException { 
     hrDao.shouldCommit(doCommit, connection); 
    } 
} 

Dao:

@Repository 
public class HumanResourcesDaoImpl implements HumanResourcesDao { 

    private JdbcTemplate jdbcTemplate; 
    private SimpleJdbcCall generateData; 

    @Autowired 
    public HumanResourcesDaoImpl(JdbcTemplate jdbcTemplate, Environment env) { 
     this.jdbcTemplate = jdbcTemplate; 
     generateData = new SimpleJdbcCall(jdbcTemplate) 
      .withProcedureName(env.getProperty("procedure.generateData")); 
    } 

    @Override 
    public String generateData(int rowsNumber) { 
     HashMap<String, Object> params = new HashMap<>(); 
     params.put("i_rowsNumber", rowsNumber); 
     Map<String, Object> m = generateData.execute(params); 
     return (String) m.get("o_execution_time"); 
    } 

    @Override 
    @Transactional("txName") 
    public void shouldCommit(boolean doCommit, Connection connection) throws SQLException { 
     if(doCommit) { 
      connection.commit(); 
     } else { 
      connection.rollback(); 
     } 
    } 
} 

Главный код класса:

public class Main extends Application implements Initializable { 
    @Override 
    public void initialize(URL url, ResourceBundle resourceBundle) { 
     ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class); 

     hrService = context.getBean(HumanResourcesService.class); 

     BasicDataSource ds = (BasicDataSource)context.getBean("dataSource"); 
     Connection connection = ds.getConnection(); 

     //do something and call 
     //hrService.generateData 
     //do something and call 
     //hrService.shouldCommit(true, connection); 
     //which commit or rollback generated data from previoues callback 
    } 
} 

UPDATE:

Я думаю, что проблема с подключением, потому что это утверждение:

this.jdbcTemplate.getDataSource().getConnection(); 

создает новое соединение, так, то нет ничего, чтобы совершить или откат. Но все же я не могу понять, почему это не работает должным образом. Нет ошибок, нет новых записей ... Что странно, это то, что когда я отлаживал connection.commit(); я узнал, что в DelegatingConnection.java, параметр это имеет правильное соединение, но есть что-то вроде:

_conn.commit(); 

и _conn имеет другое соединение. Зачем?

Должен ли я каким-то образом синхронизировать соединение для этих двух методов или что? Или это только одно соединение? Честно говоря, я не уверен, как это работает. Одно соединение и все обратные вызовы к хранимым процедурам находятся в этом соединении или, возможно, с каждым обратным вызовом создается новое соединение?

Реальный вопрос: как фиксировать или откатывать данные предыдущего обратного вызова, которые вставляются в таблицу?

+0

Что вы думаете об этом? в чем проблема? –

+0

Проблема в том, что я не могу ее правильно настроить ... Даже с инструкциями ниже этого не работает ... – Lui

+0

Вопрос должен состоять в следующем: как зафиксировать или отменить данные из предыдущего обратного вызова, которые вставляются в таблицу. – Lui

ответ

0

Один простой способ сделать это, чтобы комментировать метод с @Transactional:

@Transactional 
public void myBeanMethod() { 
    ... 
    if (!doCommit) 
     throw new IllegalStateException(); // any unchecked will do 
} 

и весна будет катиться все изменения базы данных обратно.

Не забудьте добавить @EnableTransactionManagement к вашему весне приложения/главному классу

+0

Спасибо за ответ. Но это не сработало для меня :(Я сказал больше об этом в ответ @qtips, пожалуйста, посмотрите на него :) – Lui

0

Вы можете использовать @Transactional и @EnableTransactionManagement для операций установки без использования конфигурации XML. Короче говоря, аннотируйте методы/классы, которые должны иметь транзакции с @Transactional. Чтобы настроить транзакционное управление, вы используете @EnableTransactionManagement внутри своего @Configuration.

См. Спрингс docs например о том, как использовать оба. @EnableTransactionManagement подробно описан в JavaDocs, но должен соответствовать конфигурации XML.

UPDATE

Проблема заключается в том, что вы смешиваете сырые вызовы JDBC (java.sql.Connection) с Spring JDBC. Когда вы выполняете свой SimpleJdbcCall, Spring создает новый Connection. Это не то же самое: Connection как тот, который вы позже попытаетесь зафиксировать. Следовательно, ничего не происходит, когда вы выполняете фиксацию. Я попытался каким-то образом получить соединение, которое использует SimpleJdbcCall, но найти его не удалось.

Чтобы проверить это, я попробовал следующее (я не использовал params):

@Override 
public String generateData(int rowsNumber) { 
    //HashMap<String, Object> params = new HashMap<>(); 
    //params.put("i_rowsNumber", rowsNumber); 
    //Map<String, Object> m = generateData.execute(params); 

    Connection targetConnection = DataSourceUtils.getTargetConnection(generateData.getJdbcTemplate().getDataSource().getConnection()); 
    System.out.println(targetConnection.prepareCall((generateData.getCallString())).execute()); 
    targetConnection.commit(); 

    return (String) m.get("o_execution_time"); 
} 

Если я не откладываю targetConnection, и вместо того, чтобы попытаться получить соединение снова путем вызова DataSourceUtils.getTargetConnection() при совершении, ничего случается. Таким образом, вы должны зафиксировать то же соединение, которое вы выполняете. Это, похоже, нелегко и не подходит.

Решение состоит в том, чтобы отказаться от вызова java.sql.Connection.commit(). Вместо этого вы используете Spring Transactions полностью. Если вы используете @Transaction в методе, который выполняет вызов базы данных, Spring автоматически фиксирует, когда метод завершится. Если тело метода испытывает любое Исключение (даже вне фактического вызова базы данных), оно автоматически откатывается. Другими словами, этого должно быть достаточно для нормального управления транзакциями.

Однако, если вы выполняете пакетную обработку и хотите иметь больше контроля над транзакциями с фиксациями и откатами, вы все равно можете использовать Spring. Чтобы программно управлять транзакциями с помощью Spring, вы можете использовать TransactionTemplate, которые имеют методы фиксации и отката. У вас нет времени, чтобы дать вам правильные образцы, но можете сделать это в последующие дни, если вы все еще застряли;)

+0

Спасибо за ответ! К сожалению, это не сработало ... Я добавляю annotaion EnableTransactionManagement в классе ApplicationConfig и annotaion Transactional в моем dao-методе, но затем я получаю сообщение об ошибке: «Отсутствие квалификационного компонента типа [org.springframework.transaction.PlatformTransactionManager] определено « Поэтому я добавляю Transactional также в методе службы, затем получаю ту же ошибку. Я искал ошибку и обнаружил, что [ссылка] (http://stackoverflow.com/a/20806857/3474610), а затем у меня нет ошибок, но это не работает. Я получаю информацию в консоли «Commit», но я не вижу новых записей в таблице DB. – Lui

+0

Вы как-то случайно перезаписываете настройку транзакции с помощью xml контекст? http://stackoverflow.com/questions/5491748/spring-jdbc-declarative-transactions-created-but-not-doing-anything. Вы также можете попытаться включить журнал отладки, чтобы узнать, что произойдет. Как вы это делаете? вы проверяете свой код? Я также читал что-то об отказе по умолчанию, которые проверяются модулем (но тогда вы должны получить сообщение об этом). Измените свой вопрос в журнале отладки и как вы проверите, чтобы другие могли помочь;) – qtips

+0

Нет, я d не перезаписываю его, я даже не использую xml в своем приложении. Все, что основано на Java-классах. Я также не использую Unit test. Я обновил свой вопрос, но я не знаю, как настроить журнал отладки. – Lui

0
@Configuration 
@EnableTransactionManagement 
@ComponentScan(basePackages="org.saat") 
@PropertySource(value="classpath:resources/db.properties",ignoreResourceNotFound=true) 
public class AppConfig { 

    @Autowired 
    private Environment env; 

    @Bean(name="dataSource") 
    public DataSource getDataSource() { 
     DriverManagerDataSource dataSource = new DriverManagerDataSource(); 
     dataSource.setDriverClassName(env.getProperty("db.driver")); 
     dataSource.setUrl(env.getProperty("db.url")); 
     dataSource.setUsername(env.getProperty("db.username")); 
     dataSource.setPassword(env.getProperty("db.password")); 
     return dataSource; 
    } 


    @Bean(name="entityManagerFactoryBean") 
    public LocalContainerEntityManagerFactoryBean getSessionFactory() { 
     LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean(); 
     factoryBean.setDataSource(getDataSource()); 
     factoryBean.setPackagesToScan("org.saat"); 
     factoryBean.setJpaVendorAdapter(getJpaVendorAdapter()); 
     Properties props=new Properties(); 
     props.put("hibernate.dialect", env.getProperty("hibernate.dialect")); 
     props.put("hibernate.hbm2ddl.auto",env.getProperty("hibernate.hbm2ddl.auto")); 
     props.put("hibernate.show_sql",env.getProperty("hibernate.show_sql")); 
     factoryBean.setJpaProperties(props); 
     return factoryBean; 
    } 


    @Bean(name="transactionManager") 
    public JpaTransactionManager getTransactionManager() { 
     JpaTransactionManager jpatransactionManager = new JpaTransactionManager(); 
     jpatransactionManager.setEntityManagerFactory(getSessionFactory().getObject()); 
     return jpatransactionManager; 
    } 

    @Bean 
    public JpaVendorAdapter getJpaVendorAdapter() { 
     HibernateJpaVendorAdapter hibernateJpaVendorAdapter = new HibernateJpaVendorAdapter(); 
     return hibernateJpaVendorAdapter; 
    } 
} 
+0

Объясните, что это делает, как он решает проблему OP, как он отличается от существующих ответов. (Https://meta.stackoverflow.com/questions/345719/low-quality-posts-and-code-only-answers) – Nic3500

Смежные вопросы