我正在编写与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);
}
}
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
}
}
更新
我认为问题在于连接,因为这句话:
this.jdbcTemplate.getDataSource().getConnection();
创建新连接,因此无需提交或回滚。但我仍然无法理解为什么这种方法无法正常工作。没有错误,没有新记录...... 什么是奇怪的是,当我发现 connection.commit(); 时,我发现在DelegatingConnection.java中,参数这个有正确的连接,但是有类似的东西:
_conn.commit();
和 _conn 有不同的连接。为什么呢?
我是否应该以某种方式同步这两种方法的连接或者什么?或者这只是一个连接?说实话,我不确定它是如何工作的。存储过程的一个连接和所有回调都在这个连接中,或者每个回调都可以创建新的连接吗?
真正的问题是如何提交或回滚先前回调中的数据,这些回调会插入到表中?
答案 0 :(得分:0)
一种简单的方法是使用@Transactional
注释方法:
@Transactional
public void myBeanMethod() {
...
if (!doCommit)
throw new IllegalStateException(); // any unchecked will do
}
并且spring将回滚所有数据库更改。
请记住将@EnableTransactionManagement
添加到您的spring应用程序/主类
答案 1 :(得分:0)
您可以使用@Transactional
和@EnableTransactionManagement
来设置事务,而无需使用XML配置。简而言之,使用@Transactional
注释要进行事务的方法/类。要设置事务管理,请使用@EnableTransactionManagement
中的@Configuration
。
请参阅弹簧docs,了解如何使用两者。 @EnableTransactionManagement
在JavaDocs中有详细说明,但应与XML配置相匹配。
更新
问题是您正在将原始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
。没有时间给你适当的样品,但如果你仍然卡住,可能会在以后的日子里这样做;)
答案 2 :(得分: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;
}
}