1.阐述事务管理的步骤
1、 JDBC事务
JDBC 事务是用 Connection 对象控制的。JDBC Connection 接口(java.sql.Connection )提供了两种事务模式:自动提交和手工提交。
java.sql.Connection 提供了以下控制事务的方法:
public void setAutoCommit(boolean)
public boolean getAutoCommit()
public void commit()
public void rollback()
使用 JDBC 事务界定时,您可以将多个 SQL 语句结合到一个事务中。
JDBC 事务的一个缺点是事务的范围局限于一个数据库连接。一个 JDBC 事务不能跨越多个数据库。
2、JTA(Java Transaction API)事务
JTA是一种高层的,与实现无关的,与协议无关的API,应用程序和应用服务器可以使用JTA来访问事务。
JTA允许应用程序执行分布式事务处理--在两个或多个网络计算机资源上访问并且更新数据,这些数据可以分布在多个数据库上。JDBC驱动程序的JTA支持极大地增强了数据访问能力。
如果计划用 JTA 界定事务,那么就需要有一个实现javax.sql.XADataSource 、 javax.sql.XAConnection 和javax.sql.XAResource 接口的 JDBC 驱动程序。一个实现了这些接口的驱动程序将可以参与JTA 事务。一个 XADataSource 对象就是一个 XAConnection 对象的工厂。XAConnection s 是参与 JTA 事务的 JDBC 连接。
您将需要用应用服务器的管理工具设置 XADataSource 。从应用服务器和 JDBC 驱动程序的文档中可以了解到相关的指导。
J2EE 应用程序用 JNDI 查询数据源。一旦应用程序找到了数据源对象,它就调用 javax.sql.DataSource.getConnection() 以获得到数据库的连接。
XA 连接与非 XA 连接不同。一定要记住 XA 连接参与了JTA 事务。这意味着 XA 连接不支持 JDBC 的自动提交功能。同时,应用程序一定不要对 XA 连接调用 java.sql.Connection.commit() 或者java.sql.Connection.rollback() 。相反,应用程序应该使用 UserTransaction.begin()、UserTransaction.commit() 和 serTransaction.rollback() 。
3、容器事务
容器事务主要是J2EE应用服务器提供的,容器事务大多是基于JTA完成,这是一个基于JNDI的,相当复杂的API实现。相对编码实现JTA 事务管理,我们可以通过EJB容器提供的容器事务管理机制(CMT)完成同一个功能,这项功能由J2EE应用服务器提供。这使得我们可以简单的指定将哪个方法加入事务,一旦指定,容器将负责事务管理任务。这是我们土建的解决方式,因为通过这种方式我们可以将事务代码排除在逻辑编码之外,同时将所有困难交给 J2EE容器去解决。使用EJB CMT的另外一个好处就是程序员无需关心JTA API的编码,不过,理论上我们必须使用EJB。
三种事务差异:
1、JDBC事务控制的局限性在一个数据库连接内,但是其使用简单。
2、JTA事务的功能强大,事务可以跨越多个数据库或多个DAO,使用也比较复杂。
3、容器事务,主要指的是J2EE应用服务器提供的事务管理,局限于EJB应用使用。
• 2.编写程序来演示乐观锁的作用
• 乐观并发控制
• 当数据库系统采用Read Committed隔离级别时,仍不能防止不可重复读和幻读。在可能出现这种问题的场合,可以在应用程序中采用乐观锁和悲观锁方式来解决。
• 乐观锁是假定当前事务操作数据库资源时,不会有其他事务同时访问,因此不做数据库层次上的锁定。为了维护正确数据,hibernate用version和timestamp来实现。
• 1)使用版本号进行版本控制
• 在持久化类中定义一个version属性。类型只能是整型的。
• 在映射文件中添加<version>标签,注意一定要放在<id>元素后面。public voidtestTx1() {
• Session session =HibernateSessionFactory.getSession();
• Transaction tx =session.beginTransaction();
• Student stu = null;
• try {
• stu =(Student)session.get(Student.class,1);
• System.out.println(stu.getName());
• System.out.println("version="+ stu.getVersion());
• stu.setName("在第一个事务中修改");
• session.getTransaction().commit();
• }
• catch (HibernateException he) {
• tx.rollback();
• he.printStackTrace();
• }
• session.close();
• // 操作完毕
• System.out.println("tx1操作完成后------");
• System.out.println(stu.getName());
• System.out.println("version="+ stu.getVersion());
• }
•
•
• 3.叙述悲观锁的实现原理及使用步骤
悲观锁,正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。
用户其实并不需要花很多精力去担心锁定策略的问题。通常情况下,只要为JDBC连接指定一下隔离级别,然后让数据库去搞定一切就够了。然而,高级用户有时候希望进行一个排它的悲观锁定,或者在一个新的事务启动的时候,重新进行锁定。
Hibernate总是使用数据库的锁定机制,从不在内存中锁定对象!
类LockMode 定义了Hibernate所需的不同的锁定级别。一个锁定可以通过以下的机制来设置:
当Hibernate更新或者插入一行记录的时候,锁定级别自动设置为LockMode.WRITE。
当用户显式的使用数据库支持的SQL格式SELECT ... FOR UPDATE 发送SQL的时候,锁定级别设置为LockMode.UPGRADE
当用户显式的使用Oracle数据库的SQL语句SELECT... FOR UPDATE NOWAIT 的时候,锁定级别设置LockMode.UPGRADE_NOWAIT
当Hibernate在“可重复读”或者是“序列化”数据库隔离级别下读取数据的时候,锁定模式自动设置为LockMode.READ。这种模式也可以通过用户显式指定进行设置。
LockMode.NONE 代表无需锁定。在Transaction结束时,所有的对象都切换到该模式上来。与session相关联的对象通过调用update() 或者saveOrUpdate()脱离该模式。
"显式的用户指定"可以通过以下几种方式之一来表示:
调用 Session.load()的时候指定锁定模式(LockMode)。
调用Session.lock()。
调用Query.setLockMode()。
如果在UPGRADE或者UPGRADE_NOWAIT锁定模式下调用Session.load(),并且要读取的对象尚未被session载入过,那么对象通过SELECT... FOR UPDATE这样的SQL语句被载入。如果为一个对象调用 load()方法时,该对象已经在另一个较少限制的锁定模式下被载入了,那么Hibernate就对该对象调用lock() 方法。
如果指定的锁定模式是READ, UPGRADE 或 UPGRADE_NOWAIT,那么Session.lock()就执行版本号检查。(在UPGRADE 或者UPGRADE_NOWAIT 锁定模式下,执行SELECT ... FOR UPDATE这样的SQL语句。)
如果数据库不支持用户设置的锁定模式,Hibernate将使用适当的替代模式(而不是扔出异常)。这一点可以确保应用程序的可移植性。
•