JDBC(二)

二、JDBC进阶

1、事务

(一)概念

•在数据库中,所谓事务是指一组逻辑操作单元,使数据从一种状态变换到另一种状态
•为确保数据库中数据的一致性,数据的操纵应当是离散的成组的逻辑单元:当它全部完成时,数据的一致性可以保持,而当这个单元中的一部分操作失败,整个事务应全部视为错误,所有从起始点以后的操作应全部回退到开始状态。
•事务的操作:先定义开始一个事务,然后对数据作修改操作,这时如果提交(COMMIT),这些修改就永久地保存下来,如果回退(ROLLBACK),数据库管理系统将放弃所作的所有修改而回到开始事务时的状态。


(二)事务的ACID(acid)属性

1. 原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。 
2. 一致性(Consistency)
事务必须使数据库从一个一致性状态变换到另外一个一致性状态。
3. 隔离性(Isolation)
事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
4. 持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响

(三)JDBC中的事务处理

Connection的三个方法与事务相关:

setAutoCommit(boolean):设置是否为自动提交事务,如果true(默认值就是true)表示自动提交,也就是每条执行的SQL语句都是一个单独的事务,如果设置false,那么就相当于开启了事务了;con.setAutoCommit(false)表示开启事务!!!

commit():提交结束事务;con.commit();表示提交事务

rollback():回滚结束事务。con.rollback();表示回滚事务

 

jdbc处理事务的代码格式:

try {

  con.setAutoCommit(false);//开启事务…

  ….

  …

  con.commit();//try的最后提交事务

} catch() {

  con.rollback();//回滚事务

}

public void transfer(boolean b) {
       Connection con = null;
       PreparedStatement pstmt = null;
      
       try {
           con = JdbcUtils.getConnection();
           //手动提交
           con.setAutoCommit(false);
          
           String sql = "update account set balance=balance+? where id=?";
           pstmt = con.prepareStatement(sql);
          
           //操作
           pstmt.setDouble(1, -10000);
           pstmt.setInt(2, 1);
           pstmt.executeUpdate();
          
           // 在两个操作中抛出异常
           if(b) {
              throw new Exception();
           }
          
           pstmt.setDouble(1, 10000);
           pstmt.setInt(2, 2);
           pstmt.executeUpdate();
          
           //提交事务
           con.commit(); 
       } catch(Exception e) {
           //回滚事务
           if(con != null) {
              try {
                  con.rollback();
              } catch(SQLException ex) {}
           }
           throw new RuntimeException(e);
       } finally {
           //关闭
           JdbcUtils.close(con, pstmt);
       }
    }
总结:

1、设置为手动提交事务,即开启了事务。conn.setAutoCommit(boolean);

2、如果出现了异常就回滚结束事务 conn.rollback();
3、当两个操作都执行完了,提交结束事务。 conn.commit();


(四)保存点

新的 JDBC 3.0 还原点接口提供了额外的事务控制。大部分现代的数据库管理系统的环境都支持设定还原点,例如 Oracle 的 PL/SQL。

当你在事务中设置一个还原点来定义一个逻辑回滚点。如果在一个还原点之后发生错误,那么可以使用 rollback 方法来撤消所有的修改或在该还原点之后所做的修改。

Connection 对象有两个新的方法来管理还原点-

  • setSavepoint(String savepointName): 定义了一个新的还原点。它也返回一个 Savepoint 对象。

  • releaseSavepoint(Savepoint savepointName): 删除一个还原点。请注意,它需要一个作为参数的 Savepoint 对象。这个对象通常是由 setSavepoint() 方法生成的一个还原点。

有一个 rollback (String savepointName) 方法,该方法可以回滚到指定的还原点。

下面的例子说明了如何使用 Savepoint 对象-

try{
   //Assume a valid connection object conn
   conn.setAutoCommit(false);
   Statement stmt = conn.createStatement();

   //set a Savepoint
   Savepoint savepoint1 = conn.setSavepoint("Savepoint1");
   String SQL = "INSERT INTO Employees " +
                "VALUES (106, 20, 'Rita', 'Tez')";
   stmt.executeUpdate(SQL);  
   //Submit a malformed SQL statement that breaks
   String SQL = "INSERTED IN Employees " +
                "VALUES (107, 22, 'Sita', 'Tez')";
   stmt.executeUpdate(SQL);
   // If there is no error, commit the changes.
   conn.commit();

}catch(SQLException se){
   // If there is any error.
   conn.rollback(savepoint1);
}

在这种情况下,之前的 INSERT 语句不会成功,一切都将被回滚到最初状态。

(五)并发事务问题和事务的隔离级别

并发事务问题:
脏读: 对于两个事物T1,T2, T1 读取了已经被 T2 更新但还 没有被提交的字段.之后,若T2回滚,T1读取的内容就是临时且无效的.
不可重复读:对于两个事物T1,T2, T1 读取了一个字段, 然后T2 更新了该字段.之后,T1再次读取同一个字段, 值就不同了.
幻读: 对于两个事物T1,T2, T1 从一个表中读取了一个字段,然后T2在该表中 插入了一些新的行.之后,如果T1再次读取同一个表,就会多出几行.

事务的隔离级别:


JDBC设置隔离级别

con.setTransactionIsolation(int level)

参数可选值如下:

 Connection.TRANSACTION_READ_UNCOMMITTED;

 Connection.TRANSACTION_READ_COMMITTED;

 Connection.TRANSACTION_REPEATABLE_READ;

 Connection.TRANSACTION_SERIALIZABLE。


查看和设置数据库的隔离级别(MYSQL)

•每启动一个 mysql程序,就会获得一个单独的数据库连接.每个数据库连接都有一个全局变量@@tx_isolation,表示当前的事务隔离级别.MySQL默认的隔离级别为RepeatableRead
•查看当前的隔离级别:SELECT @@tx_isolation;
•设置当前 mySQL连接的隔离级别: 
–set transaction isolation level readcommitted;
•设置数据库系统的全局的隔离级别:
– set global transaction isolation level read committed;


2、数据库连接池

1)数据库连接池的概念

用池来管理Connection,这可以重复使用Connection。有了池,所以我们就不用自己来创建Connection,而是通过池来获取Connection对象。当使用完Connection后,调用Connection的close()方法也不会真的关闭Connection,而是把Connection“归还”给池。池就可以再利用这个Connection对象了。




数据库连接池的工作原理


2)JDBC数据库连接池接口(DataSource)

Java为数据库连接池提供了公共的接口:javax.sql.DataSource,各个厂商可以让自己的连接池实现这个接口。这样应用程序可以方便的切换不同厂商的连接池!


3)DBCP数据库连接池

  使用:

/**
	 * 使用方法一:
	 * 直接newBasicDataSource对象
	 * 然后手动设置四大参数和数据库连接池的基本信息
	 * @throws SQLException
	 */
	@Test
	public void testDBCP() throws SQLException {
		//BasicDataSource是DataSource接口的实现类,由DBCP提供
		final BasicDataSource dataSource = new BasicDataSource();
		
		//2、为数据源指定必须的属性
		dataSource.setUsername("root");
		dataSource.setPassword("000");
		dataSource.setUrl("jdbc:mysql:///mydb1");
		dataSource.setDriverClassName("com.mysql.jdbc.Driver");
		
		//3指定数据源的一些可选属性
		//指定数据库连接池中,初始化连接数的个数
		dataSource.setInitialSize(5);
		
		//指定最大连接数:即可以向数据库申请的最大连接数
		dataSource.setMaxActive(5);
		
		//指定最小连接数:在数据库连接池中保存的最少的空闲连接数量
		dataSource.setMinIdle(2);
		
		//等待数据库连接池分配连接的最长时间  毫秒
		dataSource.setMaxWait(1000*5);
		
		//4、从数据源中获取数据库连接
		Connection connection = dataSource.getConnection();
		System.out.println(connection.getClass());
	}

/**
	 * 使用方法二:
	 * 1、加载dbcp的properties配置文件:配置文件的键需要来自BasicDataSource的属性
	 * (因为你的代码中没有显式的提取键的值和为BasicData Source属性赋值
	 * 所以,这些必然是实现在源码中的,故肯定要求使用默认的键名称,与属性名称匹配)
	 * 
	 * driverClassName  url  username  password
	 * 
	 * 2、调用BasicDataSourceFactory 的createDataSource方法创建DataSource对象
	 * 
	 * 3、从DataSource对象中获取数据库连接
	 * 
	 * 4、四大参数:driverClassName  url  username  password
	 *   和数据库连接池的基本信息全都在dbcp.properties文件中
	 *   
	 *   调用此方法:BasicDataSourceFactory.createDataSource(properties);
	 *   上述信息会自动设置
	 *   
	 * org.apache.commons.dbcp.BasicDataSource
	 * BasicDataSource  来自  dbcp数据库连接池jar包
	 * @throws IOException 
	 */
	@Test
	public void testDBCPWithDataSourceFactory() throws Exception {
		Properties properties = new Properties();
		//"dbcp.properties" -->  inputStream
		//不能用this,因为是单元测试方法
		InputStream inputStream = JDBCTest.class.getClassLoader().getResourceAsStream("dbcp.properties");
		properties.load(inputStream);
		
		//不需要显式提取书属性文件的键的值,并为Data Source属性赋值
		//因为BasicDataSource中都封装好了,因此,也对键的名称有规定
		DataSource dataSource = BasicDataSourceFactory.createDataSource(properties);
		Connection connection = dataSource.getConnection();
		System.out.println(connection);
	}
	
	
	
	/**
	 * 向Mysql的customers 数据表中插入1万条记录
	 * 测试如何插入,用时最短
	 * 1、使用PreparedStatement:
	 * 非批处理:10773
	 * 批处理:12218
	 */
	@Test
	public void testBatchWithPreparedStatement(){
		Connection connection = null;
		PreparedStatement preparedStatement = null;
		String sql = null;
		
		try {
			connection = JDBCTools.getConnection();
			JDBCTools.beginTx(connection);
			sql = "INSERT INTO customers VALUES(?,?,?)";
			preparedStatement = connection.prepareStatement(sql);
			Date date = new Date(new java.util.Date().getTime());
			
			long begin = System.currentTimeMillis();
			for(int i = 0; i < 100000; i++){
				preparedStatement.setInt(1, i + 1);
				preparedStatement.setString(2, "name_" + i);
				preparedStatement.setDate(3, date);
				
				preparedStatement.executeUpdate();
				
				//preparedStatement.addBatch();
			}
			
			//preparedStatement.executeBatch();
			long end = System.currentTimeMillis();
			
			System.out.println("Time: " + (end - begin)); 
			
			JDBCTools.commit(connection);
		} catch (Exception e) {
			e.printStackTrace();
			JDBCTools.rollback(connection);
		} finally{
			JDBCTools.release(null, preparedStatement, connection);
		}
	}
dbcp.properties文件的内容:

username=root
password=000
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///mydb1

initialSize=10
maxActive=50
minIdle=5
maxWait=5000



4)C3P0数据库连接池

  使用:

/**
	 * 使用方法一:
	 * @throws Exception
	 */
	@Test
	public void testC3P0() throws Exception {
		Properties properties = new Properties();
		ComboPooledDataSource ds = new ComboPooledDataSource();
		ds.setDriverClass( "com.mysql.jdbc.Driver");
		ds.setJdbcUrl("jdbc:mysql:///mydb1");
		ds.setUser("root");
		ds.setPassword("000");
		
		Connection connection = ds.getConnection();
		System.out.println(connection.getClass().getName());
	}
	

  使用方法二:

配置文件要求:

  文件名称:必须叫c3p0-config.xml

  文件位置:必须在src下

c3p0也可以指定配置文件,而且配置文件可以是properties,也可骒xml的。当然xml的高级一些了。但是c3p0的配置文件名必须为c3p0-config.xml,并且必须放在类路径下。


c3p0的配置文件中可以配置多个连接信息,可以给每个配置起个名字,这样可以方便的通过配置名称来切换配置信息。


c3p0.xml文件的内容:

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<!- 默认的配置 ->
	<default-config> 
		<property name="jdbcUrl">jdbc:mysql://localhost:3306/mydb1</property>
		<property name="driverClass">com.mysql.jdbc.Driver</property>
		<property name="user">root</property>
		<property name="password">123</property>
		<property name="acquireIncrement">3</property>
		<property name="initialPoolSize">10</property>
		<property name="minPoolSize">2</property>
		<property name="maxPoolSize">10</property>
	</default-config>
	<named-config name="mysql-config"> 
		<property name="jdbcUrl">jdbc:mysql://localhost:3306/mydb1</property>
		<property name="driverClass">com.mysql.jdbc.Driver</property>
		<property name="user">root</property>
		<property name="password">123</property>
		<property name="acquireIncrement">3</property>
		<property name="initialPoolSize">10</property>
		<property name="minPoolSize">2</property>
		<property name="maxPoolSize">10</property>
	</named-config>
</c3p0-config>

public void fun2() throws PropertyVetoException, SQLException {
		ComboPooledDataSource ds = new ComboPooledDataSource(); //不指定配置文件名称,则使用默认配置
		Connection con = ds.getConnection();
		System.out.println(con);
		con.close();
	}
public void fun2() throws PropertyVetoException, SQLException {
		ComboPooledDataSource ds = new ComboPooledDataSource("mysql-config") ;//指定配置文件名称,
		Connection con = ds.getConnection();				      //则使用指定的配置
		System.out.println(con);
		con.close();
	}

3、ApacheDBUtils简介

  •commons-dbutils是 Apache组织提供的一个开源JDBC工具类库,它是对JDBC的简单封装,学习成本极低,并且使用dbutils能极大简化jdbc编码的工作量,同时也不会影响程序的性能。
  •API介绍:
–org.apache.commons.dbutils.QueryRunner
–org.apache.commons.dbutils.ResultSetHandler
–工具类
org.apache.commons.dbutils.DbUtils






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值