在没有XML的情况下在Spring 4.1.5中配置事务
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在没有XML的情况下在Spring 4.1.5中配置事务相关的知识,希望对你有一定的参考价值。
我正在编写与Oracle数据库连接的应用程序。我从DB调用函数,将新记录插入表。在回调之后,我可以决定我想做什么:提交或回滚。
不幸的是我是Spring的新手,所以配置有问题。而且我更希望在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;
}
}
我有Dao和Service,两者都实现了正确的接口。
服务实施:
@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);
}
}
道实施:
@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
}
}
更新:
我认为问题在于连接,因为这句话:
this.jdbcTemplate.getDataSource().getConnection();
创建新连接,因此无需提交或回滚。但我仍然无法理解为什么这不能正常工作。没有错误,没有新的记录......什么是奇怪的,就是当我调试connection.commit();我发现在DelegatingConnection.java中,参数有正确的连接,但有类似的东西:
_conn.commit();
和_conn有不同的连接。为什么?
我应该以某种方式同步这两种方法的连接或什么?或者这只是一个连接?说实话,我不确定它是如何工作的。存储过程的一个连接和所有回调都在这个连接中,或者每个回调都可能创建新的连接?
真正的问题是如何从先前的回调中提交或回滚数据,这些数据会插入到表中?
一个简单的方法是使用@Transactional
注释方法:
@Transactional
public void myBeanMethod() {
...
if (!doCommit)
throw new IllegalStateException(); // any unchecked will do
}
和spring将回滚所有数据库更改。
请记住将@EnableTransactionManagement
添加到您的spring应用程序/主类中
您可以使用@Transactional
和@EnableTransactionManagement
来设置事务,而无需使用XML配置。简而言之,使用@Transactional
注释要进行事务的方法/类。要设置事务管理,请在@EnableTransactionManagement
中使用@Configuration
。
有关如何使用两者的信息,请参阅Springs 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
。没有时间给你适当的样品,但如果你仍然卡住,可以在以后的日子里这样做;)
@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;
}
}
以上是关于在没有XML的情况下在Spring 4.1.5中配置事务的主要内容,如果未能解决你的问题,请参考以下文章
如何在没有spring boot actuator的情况下在spring cloud中使用zuul?