JDBC事务与连接池学习(2)

JDBC 今日笔记

批处理

 插入大量数据时,建议使用批处理来做
 statement.addBatch();//添加批处理,先将数据缓存起来
  statement.executeBatch();//执行批处理
  statement.clearBatch();//清空缓存

JDBC 调用存储过程和自定义函数 CallableStatement

调用存储过程 {call <procedure-name>[(<arg1>,<arg2>, ...)]}
 		Connection conn = JDBCUtils.getConnection();
        String sql="{call testPro(?,?)}"; //输入或输出参数用?占位
        CallableStatement prepareCall = conn.prepareCall(sql);
        //给输入参数设置值
        prepareCall.setInt(1,7369);
        //如果有输出参数我们需要注册输出参数
        prepareCall.registerOutParameter(2, Types.INTEGER);
        boolean b = prepareCall.execute();
        //获取输出结果
        int r = prepareCall.getInt(2);
        System.out.println(r);
        //释放资源
        JDBCUtils.close(conn,prepareCall);
        
        
 调用自定义函数 {?=call<procedure -name >[( < arg1 >,<arg2 >, ...)]}
 		String sql="{?=call testFun(?)}";
        CallableStatement callableStatement = conn.prepareCall(sql);
        //设置输入参数
        callableStatement.setInt(2,7902);
        //注册返回值
        callableStatement.registerOutParameter(1, Types.INTEGER);
        callableStatement.execute();
        //获取返回的结果
        int r = callableStatement.getInt(1);
        System.out.println("结果是:"+r);
        //释放资源
        JDBCUtils.close(conn,callableStatement);

 

获取自增长键的值

要获取自增长键的值 需要在获取操作对象时声明一个参数 Statement.RETURN_GENERATED_KEYS
 PreparedStatement preparedStatement = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
 
 2.当数据插入成功后,就可以取出这个自增长键的值
  //获取自增长键的结果集
            ResultSet generatedKeys = preparedStatement.getGeneratedKeys();
            while (generatedKeys.next()){
                keyValue = generatedKeys.getInt(1);
            }
            
 3.你在其他表中就可以使用这个自增长键的值           
 

事务

基本概念:
事务使指一组最小逻辑操作单元,里面有多个操作组成。组成事务的每一部分必须要同时提交成功,如果有一个操作失败,整个操作就回滚。

事务ACID特性
	原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。 
	一致性(Consistency)
事务必须使数据库从一个一致性状态变换到另外一个一致性状态。
	隔离性(Isolation)
事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
	持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响


默认情况下,Connection 对象处于自动提交模式下,
这意味着它在执行每个语句后都会自动提交更改。
        如果禁用了自动提交模式,那么要提交更改就必须显式调用 commit 方法;否则无法保存数据库更改。
        
        //void setAutoCommit ( boolean autoCommit)
                  throws SQLException将此连接的自动提交模式设置为给定状态。如果连接处于自动提交模式下,则它的所有 SQL 语句将被执行并作为单个事务提交。否则,它的 SQL 语句将聚集到事务中,
        //直到调用 commit 方法或 rollback 方法为止。默认情况下,新连接处于自动提交模式。

`案例:扣钱,加钱 转账案例
将扣钱,加钱放入同一个事务中,转账过程中碰到异常时,事务回滚,返回原先值;
回滚事务:rollback();
手动提交事务:commit();
取消自动提交事务:SetAutoCommit(false);

代码实现:
public class pra {

    public static void main(String[] args)  {
        Connection con=null;
        PreparedStatement statement1 =null;
        PreparedStatement statement2 =null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            con=DriverManager.getConnection("jdbc:mysql://localhost:3306/lala","root","123456");
            con.setAutoCommit(false);//取消自动提交事务
            String sql1="update bank set money=money-10000  where name='zhangsan' ";
            statement1 = con.prepareStatement(sql1);
            statement1.executeUpdate();
            System.out.println(1/0);
            String sql2="update bank set money=money+10000  where name='lisi' ";
            statement2 = con.prepareStatement(sql2);
            statement2.executeUpdate();


        } catch (Exception e) {
            try {
                con.rollback(); //设置事务回滚,遇到异常时,事务回滚,数据库中的数据不变
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        } finally {
            try {
                con.commit();//手动提交事务
                con.close();
                statement1.close();
                statement2.close();

            } catch (SQLException e) {
                e.printStackTrace();
            }


        }
    }
}

设置回滚点:setSavepoint();

回滚到指定回滚点:rollback(回滚点变量名);

代码实现:
public class pra2 {
    public static void main(String[] args) {
        Connection con=null;
        PreparedStatement statement=null;
        PreparedStatement statement1=null;
        PreparedStatement statement2=null;
        PreparedStatement statement3=null;
        Savepoint savepoint=null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            con = DriverManager.getConnection("jdbc:mysql:///lala", "root", "123456");
            con.setAutoCommit(false);
            String sql1="update bank set money=money-5000 where name='zhangsan'";
            statement = con.prepareStatement(sql1);
            statement.executeUpdate();
            String sql2="update bank set money=money+5000 where name='lisi'";
            statement1 = con.prepareStatement(sql2);
            statement1.executeUpdate();
            savepoint= con.setSavepoint();
            System.out.println(1/0);
            String sql3="update bank set money=money-5000 where name='zhangsan'";
            statement2 = con.prepareStatement(sql3);
            statement2.executeUpdate();
            String sql4="update bank set money=money+5000 where name='lisi'";
            statement3 = con.prepareStatement(sql4);
            statement3.executeUpdate();
        }  catch (Exception e) {
            try {
                con.rollback(savepoint);
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        } finally {
            try {
                con.commit();
                statement.close();
                statement1.close();
                statement2.close();
                statement3.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

事务的隔离级别

事务总结:
	事务的特性:★★★
		ACID
		原子性:事务里面的操作单元不可切割,要么全部成功,要么全部失败
		一致性:事务执行前后,业务状态和其他业务状态保持一致.
		隔离性:一个事务执行的时候最好不要受到其他事务的影响
		持久性:一旦事务提交或者回滚.这个状态都要持久化到数据库中


	不考虑隔离性会出现的读问题★★
		脏读:在一个事务中读取到另一个事务没有提交的数据
		
		不可重复读:在一个事务中,两次查询的结果不一致(针对的update操作)  不可重复读,是指在数据库访问中,一个事务范围内两个相同的查询却返回了不同数据。
		虚读(幻读):在一个事务中,两次查询的结果不一致(针对的insert操作) 无法演示出来,MySQL已经默认避免了

		MySQL 有四种隔离级别

	通过设置数据库的隔离级别来避免上面的问题(理解)
		read uncommitted  	读未提交	上面的三个问题都会出现
		read committed  	读已提交	可以避免脏读的发生 Oracle 默认级别
		repeatable read		可重复读	可以避免脏读和不可重复读的发生  MySQL 默认级别
		serializable		串行化		可以避免所有的问题
	了解
		演示脏读的发生:
			将数据库的隔离级别设置成 读未提交
				set session transaction isolation level read uncommitted;
			查看数据库的隔离级别
				select @@tx_isolation;
			演示:
			打开两个窗口进行演示:给两个窗口设置好同的隔离级别
			开启事务  start transaction;
			修改数据:update bank set money=1500 where username='lisi';
			让另一个窗口开启事务 查询数据 他查到了  就是脏读
			我这边窗口 一回滚(rollback),钱又没过去
		避免脏读的发生,将隔离级别设置成  读已提交
			set session transaction isolation level read committed;
			不可避免不可重复读的发生.
		
		避免不可重复读的发生 经隔离级别设置成 可重复读
			set session transaction isolation level  repeatable read;
			
		演示串行化 可以避免所有的问题
			set session transaction isolation level  serializable;  我这边的事务不提交,那边的事务无法执行
			锁表的操作. 
		
	四种隔离级别的效率
		read uncommitted>read committed>repeatable read>serializable
	四种隔离级别的安全性
		read uncommitted<read committed<repeatable read<serializable
		
	开发中绝对不允许脏读发生.
		mysql中默认级别:repeatable read
		oracle中默认级别:read committed

	java中控制隔离级别:(了解)
		Connection的api
			void setTransactionIsolation(int level) 
				level是常量
 

常见连接池的使用

为什么要有连接池?
由于建立数据库连接是一种非常耗时、耗资源的行为,
  所以通过连接池预先同数据库建立一些连接,放在内存中,应用程序需要建立数据库连接时直接到连接池中申请一个就行,使用完毕后再归还到连接池中。

1.DBCP连接池

DBCP(DataBase Connection Pool)数据库连接池,
 是Java数据库连接池的一种,由Apache开发,通过数据库连接池,
    可以让程序自动管理数据库连接的释放和断开
 使用步骤:
			1.导入jar包(commons-dbcp-1.4.jar和commons-pool-1.5.6.jar)
			2.使用api
				a.硬编码
					a.硬编码(不推荐)   
    
    //创建连接池
		BasicDataSource ds = new BasicDataSource();
		
		//配置信息
		ds.setDriverClassName("com.mysql.jdbc.Driver");
		ds.setUrl("jdbc:mysql:///day07");
		ds.setUsername("root");
		ds.setPassword("1234");
		
		Connection conn=ds.getConnection();
		String sql="insert into category values(?,?);";
		PreparedStatement st=conn.prepareStatement(sql);
		//设置参数
		st.setString(1, "c011");
		st.setString(2, "饮料");
		
		int i = st.executeUpdate();
		System.out.println(i);


*** 采用配置文件的方式
		//存放配置文件
		Properties prop = new Properties();
		prop.load(new FileInputStream("src/dbcp.properties"));
		//设置
		//prop.setProperty("driverClassName", "com.mysql.jdbc.Driver");
		
		//创建连接池
		DataSource ds = new BasicDataSourceFactory().createDataSource(prop);
		
		Connection conn=ds.getConnection();
		String sql="insert into category values(?,?);";
		PreparedStatement st=conn.prepareStatement(sql);
		
		//设置参数
		st.setString(1, "c012");
		st.setString(2, "饮料1");
		
		int i = st.executeUpdate();
		System.out.println(i);

2.C3P0

C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate、Spring等。

使用步骤:
			1.导入jar包(c3p0-0.9.1.2.jar)
			2.使用api
		方式1:硬编码
		ComboPooledDataSource ds = new ComboPooledDataSource();
		//设置基本参数
		ds.setDriverClass("com.mysql.jdbc.Driver");
		ds.setJdbcUrl("jdbc:mysql:///day07");
		ds.setUser("root");
		ds.setPassword("1234");
		
		Connection conn=ds.getConnection();
		String sql="insert into category values(?,?);";
		PreparedStatement st=conn.prepareStatement(sql);
		
		//设置参数
		st.setString(1, "c013");
		st.setString(2, "毒药");
		
		int i = st.executeUpdate();
		System.out.println(i);
		
*** 采用配置文件的方式

b.配置文件
					要求1:配置文件的名称:c3p0.properties 或者 c3p0-config.xml
					要求2:配置文件的路径:src下
				
					编码只需要一句话
						new ComboPooledDataSource()//使用默认的配置
						//可以切换xml配置文件中的标签 来加载不同的配置比如更换Orecal的数据库的配置
						new ComboPooledDataSource(String configName)//使用命名的配置 若配置的名字找不到,使用默认的配置

3.Druid 阿里德鲁伊连接池

 DRUID是阿里巴巴开源平台上一个数据库连接池实现,它结合了C3P0、DBCP、PROXOOL等DB池的优点,同时加入了日志监控,可以很好的监控DB池连接和SQL的执行情况,可以说是针对监控而生的DB连接池(据说是目前最好的连接池,不知道速度有没有BoneCP快)。


使用步骤,导入德鲁伊的jar包
  1.采用硬编码的方式
    	//采用硬编码的方式来创建连接池
        DruidDataSource dataSource = new DruidDataSource();
        //设置参数
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/crm");
        dataSource.setInitialSize(5);
        dataSource.setMinIdle(1);
        dataSource.setMaxActive(10);
        //获取连接对象
        DruidPooledConnection connection = dataSource.getConnection();
        System.out.println(connection);


   2.采用配置文件的方式,
      a.引入配置文件
      b.加载配置文件
       //采用加载配置文件的方式
        Properties properties = new Properties();
        properties.load(new FileInputStream("src/db_server.properties"));
        DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
        Connection conn = dataSource.getConnection();
        System.out.println(conn);

德鲁伊连接池代码实现:

硬编码代码实现:
public class test {
    public static void main(String[] args) throws SQLException {
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql:///lala");
        ds.setUsername("root");
        ds.setPassword("123456");
        DruidPooledConnection connection = ds.getConnection();
        PreparedStatement statement = connection.prepareStatement("select * from bank ;");
        ResultSet resultSet = statement.executeQuery();
        while(resultSet.next()){
            String name = resultSet.getString(1);
            int money = resultSet.getInt(2);
            System.out.println("姓名:"+name+"存款:"+money);
        }
    }
}
//配置文件代码实现
//通过一个工厂类,创建一个数据源
//DruidDataSourceFactory().createDataSource(参数)
//然后获取连接对象
Properties properties = new Properties();
        properties.load(new FileReader("pro,pro"));
        //建立工厂,生成数据源
        DataSource ds = new DruidDataSourceFactory().createDataSource(properties);
        //获取连接
        Connection connection = ds.getConnection();
        //获取预编译对象
        PreparedStatement statement = connection.prepareStatement("select * from bank ;");
        ResultSet resultSet = statement.executeQuery();
        while(resultSet.next()){
            String name = resultSet.getString(1);
            int money = resultSet.getInt(2);
            System.out.println("姓名:"+name+"存款:"+money);
        }

注意:Druid线程池,使用配置文件方法连接时,参数名固定为:driverClassName,url,username,password

DBUtils

Commons DbUtils是Apache组织提供的一个对JDBC进行简单封装的开源工具类库,
使用它能够简化JDBC应用程序的开发,同时也不会影响程序的性能。 

使用步骤:
		1.导入jar包(commons-dbutils-1.4.jar)
		2.创建一个queryrunner类
			queryrunner作用:操作sql语句
				构造方法:
					new QueryRunner(Datasource ds);
		3.编写sql
		4.执行sql
			query(..):执行查询操作
			update(...):执行增,删,改操作
			
	使用步骤
		// 创建对象
		QueryRunner runner = new QueryRunner(new ComboPooledDataSource());
		// 定义sql
		String sql = "insert into bank(username,money) values(?,?)";
		// 执行sql
		runner.update(sql,"赵六",500);
		....
	执行查询后返回的结果集
	ResultSetHandler(存储对象的字节码对象):封装结果集 接口,每次封装一条记录
	BeanListHandler(存储对象的字节码对象), 将查询结果的每一条记录封装成指定的bean对象,将每一个bean对象放入list中 返回.
					
//update方法代码实现
Properties properties = new Properties();
        properties.load(new FileReader("pro,pro"));
        //建立工厂,生成数据源
        DataSource ds = new DruidDataSourceFactory().createDataSource(properties);
        //rr(ds);
        QueryRunner queryRunner = new QueryRunner(ds);
        int i = queryRunner.update("update bank set money=0 where name=?", "lisi");
        if(i>0){
            System.out.println("更新成功!");
        }else{
            System.out.println("饿了");
        }
//查询表中所有数据代码实现
 Properties properties = new Properties();
        properties.load(new FileReader("pro,pro"));
        //建立工厂,生成数据源
        DataSource ds = new DruidDataSourceFactory().createDataSource(properties);
        //rr(ds);
        QueryRunner queryRunner = new QueryRunner(ds);
        List<bank> query = queryRunner.query("select * from bank;", new BeanListHandler<bank>(bank.class));
        for (bank bank : query) {
            System.out.println(bank.getName()+"==="+bank.getMoney());
        }
//查询表中单条数据代码实现
 Properties properties = new Properties();
        properties.load(new FileReader("pro,pro"));
        //建立工厂,生成数据源
        DataSource ds = new DruidDataSourceFactory().createDataSource(properties);
        //rr(ds);
        QueryRunner queryRunner = new QueryRunner(ds);
        bank bank = queryRunner.query("select * from bank where name=?;", new BeanHandler<bank>(bank.class), "lisi");
        System.out.println(bank.getName()+"==="+bank.getMoney());
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值