`
lvwenwen
  • 浏览: 959236 次
  • 性别: Icon_minigender_1
  • 来自: 魔都
社区版块
存档分类
最新评论

Jdbc跟hibernate事物管理

    博客分类:
  • Jdbc
 
阅读更多
实例详解Spring JDBC事务管理
2009-06-08 17:56 佚名 中国IT实验 我要评论(0) 字号:T | T

本文以JDBC事务为例,介绍Spring的事务管理。Spring提供编程式的事务管理与声明式的事务管理,提供提供了一致的编程模型。
AD:
JDBC事务管理

Spring提供编程式的事务管理(Programmatic transaction manage- ment)与声明式的事务管理(Declarative transaction management),为不同的事务实现提供了一致的编程模型,这节以JDBC事务为例,介绍Spring的事务管理。

Spring对事务的支持

事务是一组原子(Atomic)操作的工作单元,以数据库存取的实例来说,就是一组SQL指令,这一组SQL指令必须全部执行成功,若因为某个原因未全部执行成功(例如其中一行SQL有错误),则先前所有执行过的SQL指令都会被撤消。

举个简单的例子,一个客户从A银行转账至B银行,要作的动作为从A银行的账户扣款、在B银行的账户加上转账的金额,两个动作必须成功,如果有一个动作失败,则此次转账失败。

事务还必须保持所参与资源的一致性(Consistent),例如在银行账户的例子中,两个账户的转账金额,B账户取款的金额不能大于A账户的存款金额。每个事务彼此之间必须是隔离的(Isolated),例如在A账户中可能有两笔事务,同时进行存款与提款的动作,两个事务基本上不需意识到彼此的存在。事务还必须是可持续的(Durable),在某一笔事务之后,这笔事务必须是被记录下来的。

在这里将介绍JDBC如何使用事务管理。首先来看看事务的原子性实现,在JDBC中,可以操作Connection的setAutoCommit() 方法,给定false参数,在下达一连串的SQL语句后,自行执行Connection的commit()来送出变更,如果中间发生错误,则执行 rollback() 来撤消所有的执行,例如:

try {
.....
connection.setAutoCommit(false);
.....
// 一连串SQL操作

connection.commit();
} catch(SQLException) {
// 发生错误,撤消所有变更

connection.rollback();
}
在Spring中对JDBC的事务管理加以封装,Spring事务管理的抽象关键在于org.springframework.transaction.PlatformTransactionManager接口的实现:

public interface PlatformTransactionManager {
TransactionStatus getTransaction(TransactionDefinition
definition) throws TransactionException;
void commit(TransactionStatus status)
throws TransactionException;
void rollback(TransactionStatus status)
throws TransactionException;
}
PlatformTransactionManager 接口有许多具体的事务实现类,例如DataSourceTransactionManager、 HibernateTransactionManager、JdoTransaction- Manager、JtaTransactionManager等,通过依赖于PlatformTransactionManager接口及各种的技术实现,Spring在事务管理上可以让开发人员使用一致的编程模型,即使所使用的是不同的事务管理技术。

TransactionException是Unchecked Exception.事务的失败通常都是致命的错误,Spring不强迫您一定要处理,而是让您自行选择是否要捕捉异常。

getTransaction() 方法根据一个TransactionDefinition对象来回传一个TransactionStatus对象,TransactionDefinition接口的实例定义了事务的隔离程度(Isolation level)、传播行为(Propagation behavior)、超时(Timeout)、只读(Read-only)等,TransactionStatus代表着一个新的事务发起或已经存在的事务,您可以通过它来控制事务的执行或调查的状态:

public interface TransactionStatus {
boolean isNewTransaction();
void setRollbackOnly();
boolean isRollbackOnly();
}
Spring提供编程式的事务管理(Programmatic transaction management)与声明式的事务管理(Declarative transaction management):

编程式的事务管理

编程式的事务管理可以清楚地控制事务的边界,也就是让您自行实现事务开始时间、撤消操作的时机、结束时间等,可以实现细粒度的事务控制。

声明式的事务管理

然而多数的情况下,事务并不需要细粒度的控制,而是采用声明式的事务管理,好处是Spring事务管理的相关API可以不用介入程序之中,从对象的角度来看,它并不知道自己正被纳入事务管理之中,在不需要事务管理的时候,只要在设置文件上修改一下设置,即可移去事务管理服务。

JDBC编程事务管理Spring提供两种方式实现编程式的事务管理,一是直接使用PlatformTransaction- Manager实现,二是使用org.springframework.transaction.support.Transaction- Template.

先来看看如何使用PlatformTransactionManager,在这里使用它的实现类 DataSourceTransactionManager,可以改写一下之前几节中的JdbcTemplateDemo项目,让它具有事务管理功能,修改一下UserDAO类的insert() 方法来作示范:ProgrammaticTransactionDemo UserDAO.java

package onlyfun.caterpillar;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.
datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.
support.DefaultTransactionDefinition;
public class UserDAO implements IUserDAO {
private DataSourceTransactionManager transactionManager;
private DefaultTransactionDefinition def;
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
jdbcTemplate = new JdbcTemplate(dataSource);
transactionManager =
new DataSourceTransactionManager(dataSource);
// 建立事务的定义

def = new DefaultTransactionDefinition();
def.setPropagationBehavior(
TransactionDefinition.PROPAGATION_REQUIRED);
}
public void insert(User user) {
String name = user.getName();
int age = user.getAge().intValue();
TransactionStatus status =
transactionManager.getTransaction(def);
try {
jdbcTemplate.update("INSERT INTO user (name,age) "
+ "VALUES('" + name + "'," + age + ")");
// 下面的SQL有错误,用以测试事务

jdbcTemplate.update("INSER INTO user (name,age) "
+ "VALUES('" + name + "'," + age + ")");
}
catch(DataAccessException e) {
transactionManager.rollback(status);
throw e;
}
transactionManager.commit(status);
}
public User find(Integer id) {
List rows = jdbcTemplate.queryForList(
"SELECT * FROM user WHERE id=" + id.intValue());
Iterator it = rows.iterator();
if(it.hasNext()) {
Map userMap = (Map) it.next();
Integer i = new Integer(
userMap.get("id").toString());
String name = userMap.get("name").toString();
Integer age = new Integer(
userMap.get("age").toString());
User user = new User();
user.setId(i);
user.setName(name);
user.setAge(age);
return user;
}
return null;
}
}
在 insert()方法中使用了DataSourceTransactionManager来进行事务管理,如果发生了异常,则catch区块中会进行事务的Rollback,在insert() 方法中故意写入错误的SQL(注意INSERT方法少写了一个T),因此实际上数据并不会被储存至数据库中。
要使用MySQL数据库进行事务处理,必须建立支持事务的表格类型,例如InnoDB的表格类型,这里用来建立表格的SQL如下所示:

CREATE TABLE user (
id INT(11) NOT NULL auto_increment PRIMARY KEY,
name VARCHAR(100) NOT NULL default '',
age INT
) TYPE = InnoDB;
另一个实现编程式事务管理的方法是使用TransactionTemplate,它需要一个TransactionManager实例,如下所示:

TransactionTemplate transactionTemplate =
new TransactionTemplate(transactionManager);
...
transactionTemplate.execute(new TransactionCallback() {
public Object doInTransaction(TransactionStatus status) {
return jdbcTemplate.update("INSERT INTO user (name,age) "
+ "VALUES('" + name + "'," + age + ")");
}
});
如果发生了异常,则会进行Rollback,否则提交事务,如果没有回传值,则也可以使用TransactionCallbackWithoutResult:

transactionTemplate.execute(
new TransactionCallbackWithoutResult() {
public void doInTransactionWithoutResult(
TransactionStatus status) {
. ...
}
});
5.3.3 JDBC声明事务管理

Spring声明式的事务管理依赖它的AOP框架来完成。使用声明事务管理的好处是,事务管理不能侵入您所开发的组件,具体来说,DAO对象不会意识到正在事务管理之中,事实上也应当如此,因为事务管理是属于系统层面的服务,而不是业务逻辑的一部分,如果想要改变事务管理策略的话,也只需要在定义文件中重新配置。

举个例子来说,可以将5.2.1节中的JdbcTemplateDemo 项目修改一下,在不修改UserDAO类的情况下,可以为它加入事务管理的服务,一个简单的方法是使用 TransactionProxyFactoryBean,指定要介入的事务管理对象及其方法,这需要在定义文件中修改,如下所示:

DeclarativeTransactionDemo beans-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id="dataSource"
class="org.springframework.jdbc.
→ datasource.DriverManagerDataSource"
destroy-method="close">
<property name="driverClassName"
value="com.mysql.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/demo"/>
<property name="username" value="caterpillar"/>
<property name="password" value="123456"/>
</bean>
<bean id="transactionManager"
class="org.springframework.jdbc.
→ datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="userDAO"
class="onlyfun.caterpillar.UserDAO">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="userDAOProxy"
class="org.springframework.transaction.
→ interceptor.TransactionProxyFactoryBean">
<property name="proxyInterfaces">
<list>
<value>onlyfun.caterpillar.IUserDAO</value>
</list>
</property>
<property name="target" ref="userDAO"/>
<property name="transactionManager"
ref="transactionManager"/>
<property name="transactionAttributes">
<props>
<prop key="insert*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
</beans>
TransactionProxyFactoryBean需要一个TransactionManager,由于这里使用的是JDBC,所以使用 DataSourceTransactionManager,TransactionProxyFactoryBean是个代理对象,"target" 属性指定要代理的对象,事务管理会自动介入指定的方法前后,这里使用 "transactionAttributes" 属性指定,"insert*" 表示指定方法名称以insert开头的都要纳入事务管理,您也可以指定方法全名,如果在方法执行过程中发生错误,则所有先前的操作自动撤回,否则正常提交。

在"insert*" 等方法上指定了 "PROPAGATION_REQUIRED",表示在目前的事务中执行操作,如果事务不存在就建立一个新的,相关的常数意义都可以在API文件的 TransactionDefinition接口中找到。您可以加上多个事务定义,中间使用逗号 "," 区隔,例如可以加上只读,或者是指定某个异常发生时撤回操作:

PROPAGATION_REQUIRED,readOnly,-MyCheckedException

MyCheckedException前面加上 "-" 时,表示发生指定异常时撤消操作,如果前面加上 "+",表示发生异常时立即提交。

由于"userDAO"被"userDAOProxy"代理了,所以要做的是取得"userDAOProxy",而不是"userDAO",例如:

DeclarativeTransactionDemo SpringDAODemo.java

package onlyfun.caterpillar;
import org.springframework.context.ApplicationContext;
import org.springframework.context.
support.ClassPathXmlApplicationContext;
public class SpringDAODemo {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext(
"beans-config.xml");
User user = new User();
user.setName("caterpillar");
user.setAge(new Integer(30));
IUserDAO userDAO =
(IUserDAO) context.getBean("userDAOProxy");
userDAO.insert(user);
user = userDAO.find(new Integer(1));
System.out.println("name: " + user.getName());
}
}
您也可以设置不同的TransactionInterceptor来得到更多的管理细节,例如:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<bean id="dataSource"
class="org.springframework.jdbc.
→ datasource.DriverManagerDataSource"
destroy-method="close">
<property name="driverClassName"
value="com.mysql.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://localhost:3306/demo"/>
<property name="username" value="caterpillar"/>
<property name="password" value="123456"/>
</bean>
<bean id="transactionManager"
class="org.springframework.jdbc.
→ datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="userDAO"
class="onlyfun.caterpillar.UserDAO">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="transactionInterceptor"
class="org.springframework.transaction.
→ interceptor.TransactionInterceptor">
<property name="transactionManager" ref="transactionManager"/>
<property name="transactionAttributeSource"
value="onlyfun.caterpillar.UserDAO.insert*=
→ PROPAGATION_REQUIRED "/>
</bean>
<bean id="userDAOProxy"
class="org.springframework.aop.
→ framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<list>
<value>onlyfun.caterpillar.IUserDAO</value>
</list>
</property>
<property name="target" ref="userDAO"/>
<property name="interceptorNames">
<list>
<value>transactionInterceptor</value>
</list>
</property>
</bean>
同样的,由于不再于设置文件中设置代理对象,所以直接取得"userDAO"实例进行操作即可。

Hibernate 事务与JDBC 事务同时使用注意事项 (转载)
2010-12-14 20:56
项目是老项目了,ssh结构的,数据保存的时候出错,
Pre-bound JDBC Connection found! HibernateTransactionManager does not support
Cannot deactivate transaction synchronization - not active
Cannot create PoolableConnectionFactory
等错误。这个头疼啊,正式下面的文章了 给了我启示,不过和他的做法不一样。
我的问题是由于service中调用了另一个service,然后应该是用了2个事务。然后2个dao一个是hibernate的一个是jdbc的。就出这个问题了。
解决方法是把调用的service的dao拿出来,配置到这个service里。就可以了。
感谢那些分享的人。
《引用》
当一个service ,既调用了用Hibernate技术实现的DAO,又调用了用JDBC技术实现的DAO时,service需要怎样配置事务?当一个service 调用了其他service,而两个service所配置的事务不同,我们应该怎么处理?


本文通过4个测试,描述了在配置JDBC事务和Hibernate事务混用的情况下,应该注意的事情。写的不好,请大家多多帮助。
我们先进行一个测试,测试结果如下:

1. service 配置baseTxProxy(HibernateTransactionManager) Hibernate和JDBC实现的DAO没有配置事务(没有出现异常)。

2.service配置JDBCTxDAOProxy (DataSourceTransactionManager) Hibernate和JDBC实现的DAO没有配置事务(没有出现异常)。

3.service配置baseTxProxy(HibernateTransactionManager)Hibernate和JDBC实现的DAO配置了对应实现的事务(没有异常)。


4.service配置JDBCTxDAOProxy (DataSourceTransactionManager) Hibernate和JDBC实现的DAO配置了对应实现的事务(抛出异常)
下面对这个测试进行进一步的说明:

1. service 配置baseTxProxy(HibernateTransactionManager) Hibernate和JDBC实现的DAO 没有配置事务:
正常:

   查询功能正常,修改、删除功能正常。系统不抛出异常。
问题:

   当Hinernat实现的DAO调用save方法添加一条记录时,用JDBC实现的DAO读取不到数据。

Java代码
System.out.println("hibernate delete");  zxmTestHiDAO.deleteDB();  System.out.println("hibernate insert");  zxmTestHiDAO.saveDB();  System.out.println("hibernate read");  zxmTestHiDAO.readDB(); 
System.out.println("hibernate delete"); zxmTestHiDAO.deleteDB();System.out.println("hibernate insert"); zxmTestHiDAO.saveDB();System.out.println("hibernate read"); zxmTestHiDAO.readDB();




问题原因及解决办法:

  当调用Hibernate的save方法时,Hibernat并没有提交数据,而是放入Hibernaet缓存中,在提交事务前,一起提交数据,这时,JDBC读取数据库,是得不到数据的,及时他们是配置在一个事务中的。

  为了让JDBC能能够读取到Hibernate添加的数据,我们需要调用Hibernate提供的flush方法。这样,JDBC就能够读取到了。

  当Hibernate调用delete和update时,会自动调用flush方法。

2. service 配置JDBCTxDAOProxy (DataSourceTransactionManager) Hibernate和JDBC实现的DAO 没有配置事务:
正常:

   查询、JDBC调用正常。系统不抛出异常。
问题:

   hibernate不能提交事务。
问题原因及解决办法:

  没有好的解决办法,不建议这么配置。

3.service 配置baseTxProxy(HibernateTransactionManager)  Hibernate和JDBC实现的DAO 配置了对应实现的事务:
测试结果与测试1相同。

4.service 配置JDBCTxDAOProxy (DataSourceTransactionManager) Hibernate和JDBC实现的DAO 配置了对应实现的事务:
问题:

系统抛出如下异常,说明配置错误,不能这样使用。

Pre-bound JDBC connection found - HibernateTransactionManager does not support running within DataSourceTransactionManager if told to manage the DataSource itself. It is recommended to use a single HibernateTransactionManager for all transactions on a single DataSource, no matter whether Hibernate or JDBC access.
结论:

根据以上测试结果,建议使用第1、3种方法。

使用时,要特别注意Hinernate 添加了数据,JDBC要读取这个数据时,要调用Hibernate 的flush方法。
分享到:
评论

相关推荐

    JDBC和hibernate事务的详解

    **JDBC与Hibernate事务详解** 在Java开发中,数据库事务管理是确保数据一致性、完整性和原子性的重要机制。本讲解将深入探讨JDBC(Java Database Connectivity)和Hibernate两种不同的技术在处理事务时的方法和特点...

    JDBC与Hibernate的比较

    **Hibernate**则是一种ORM(Object Relational Mapping,对象关系映射)框架,它简化了数据持久化层的复杂性,提供了比JDBC更为高级的功能,如缓存机制、事务管理以及对象关系映射等。通过使用Hibernate,开发者可以...

    JDBC与Hibernate区别

    然而,由于直接操作SQL,它的学习曲线相对较陡峭,且需要手动处理资源关闭和事务管理等问题,增加了代码的繁琐程度。 相比之下,Hibernate是一个对象关系映射(ORM)框架,它抽象了数据库操作,提供了面向对象的...

    jdbc和hibernate的区别

    3. **事务管理**:两者都支持显式的事务处理,可以通过编程方式来控制事务的提交和回滚。 **不同点:** 1. **SQL语言**:JDBC直接使用标准SQL与数据库通信,而Hibernate则使用HQL(Hibernate Query Language),一...

    spring hibernate,spring jdbc事务管理

    两个项目,一个项目是基于spring jdbc实现的分布式事务,一个是基于spring hibernate的分布式事务,hibernate项目里的applicationContext2.xml是基于mysql和mssql, applicationContext3.xml基于两个mssql, ...

    spring分别与jdbc和hibernate结合的事务控制--案例

    本案例重点探讨了Spring如何与两种流行的数据访问技术——JDBC(Java Database Connectivity)和Hibernate——相结合,进行事务管理。事务控制是确保数据库操作一致性、完整性的关键,尤其在多步骤操作中,它能防止...

    Jdbc 和hibernate

    - 支持事务管理:提供了高级的事务处理机制。 - 查询语言:HQL(Hibernate查询语言)和Criteria API,更接近面向对象的查询方式。 - 第二级缓存:可提高数据读取效率。 - 支持复杂关联:如一对一、一对多、多对...

    详解Hibernate事务处理机制

    - **JDBC事务管理**:这是最基本的事务管理方式,适用于单个应用程序上下文中。它通过设置连接的自动提交属性为`false`来开启事务,并在提交或回滚时恢复到默认状态。 ```java Session session = sf.openSession...

    jdbc_Hibernate总结

    Hibernate是一个流行的ORM(Object-Relational Mapping)框架,它为开发者提供了对JDBC的轻量级封装,简化了数据库操作。使用Hibernate,开发者可以更专注于业务逻辑,而不是底层的SQL语法。在搭建Hibernate项目时,...

    jdbc和hibernate学习内容

    7. **事务处理**:Hibernate支持编程式和声明式事务管理,使得事务处理更加便捷。 8. **懒加载和级联操作**:了解如何配置和使用懒加载以优化性能,以及如何设置级联操作以简化对象间的关联操作。 总的来说,掌握...

    在Spring中配置Hibernate事务

    在Spring框架中集成和配置Hibernate事务管理是企业级Java应用中的常见实践,它能提供高效且灵活的事务处理策略。Spring作为一款强大的依赖注入(DI)和面向切面编程(AOP)容器,能够轻松地管理和协调不同数据访问...

    oracle分别使用jdbc和hibernate的例子

    Hibernate的优势在于它提供了一种面向对象的方式来操作数据库,减少了SQL的编写,支持自动化事务管理,并具有二级缓存和查询优化等功能。但是,相比JDBC,Hibernate可能带来一定的性能开销。 总之,JDBC适合简单、...

    spring mvc+hibernate 实现事务管理(全注解版)

    2. **Hibernate事务**: Hibernate本身也提供了一种事务管理机制,但在Spring环境中,我们通常使用Spring的事务管理器来协调。当@Transactional注解应用于方法上,Spring会在该方法执行前后自动管理事务的开始和结束...

    hibernate事务,并发及缓存管理实例

    一、Hibernate事务管理 在数据库操作中,事务确保了数据的一致性和完整性。Hibernate提供了四种事务隔离级别:读未提交(READ UNCOMMITTED)、读已提交(READ COMMITTED)、可重复读(REPEATABLE READ)和串行化...

    JDBC+Hibernate将Blob数据写入Oracle

    在Oracle数据库中,BLOB类型的字段具有特殊的处理方式,尤其在使用JDBC(Java Database Connectivity)和Hibernate框架时,需要特别注意其写入过程。以下是对“JDBC+Hibernate将Blob数据写入Oracle”这一主题的深入...

    Java_Jdbc_Hibernate_Struts2_Android_Web异常及其处理办法

    处理这类异常需要理解Hibernate的日志输出,以找出具体的问题所在,例如实体验证失败、事务处理错误等。 【Struts2异常处理】 Struts2是一个基于MVC(Model-View-Controller)架构的Web应用框架。它的异常处理主要...

    Hibernate事务

    在 Hibernate 中,事务管理可以基于 JDBC Transaction 或 JTA (Java Transaction API) 进行,这两种方式各有特点。 首先,Hibernate 默认的事务处理机制是基于 JDBC Transaction。这意味着在进行数据库操作时,...

    jdbc与hibernate的优缺点比较(转载的精髓).pdf

    Entity Bean是JDBC和Hibernate之间的一种选择,它提供了事务管理和状态管理等服务,但通常被认为比JDBC复杂且性能较低。 综上所述,JDBC更适合于对性能要求高且数据库操作相对简单的场景,而Hibernate则适用于需要...

    jdbc和hibernate总结

    - **自动管理事务**:简化事务处理。 - **延迟加载**:只在需要时加载数据,节省资源。 - **支持多种查询方式**:HQL、Criteria、SQL,适应不同需求。 总结来说,JDBC是基础的数据库访问技术,适合进行简单的...

    Spring+Hibernate注解事务实例

    本实例将深入探讨如何结合Spring的注解声明式事务管理与Hibernate的数据访问技术,构建一个完整的事务处理系统。 Spring框架以其灵活的依赖注入(DI)和面向切面编程(AOP)闻名,它允许开发者将事务管理从业务逻辑...

Global site tag (gtag.js) - Google Analytics