MySQL数据库之DJBC——day30 事务(转账问题),连接池(四种连接池)

这是数据库中学习的最清楚的一个小章节了,前面的内容还没有补上,在全程班开始的这几天的空挡争取补上,加油,大家都有了自己的目标,学的太晚,加油
批处理

批处理

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

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

调用存储过程 {call [(,, ...)]}
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[( < arg1 >,, ...)]}
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.你在其他表中就可以使用这个自增长键的值           

事务

概念:

在一个事务中有多个逻辑单元组成,这些逻辑单元是一个整体,不可分割,要么都成功,要么都失败

事务的特性:

  • 原子性:不可分割,要么都发生,要么都不发生
  • 一致性:在同一个事务中同步进行,从一个一致性状态到另一个一致性状态
  • 持久性:只要进行提交或回滚就不能更改
  • 隔离性:多并发都需要隔离,数据库对每一个用户开启的事务不能被其他的事务进行干扰

案例一:
银行系统,张三给李四进行转钱
解决钱不翼而飞的情况

package org.westos.demo1shiwu;

import org.westos.utils.JDBCUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

/**
 * @Description:进行转账,出现异常的解决
 * @Author:@李小白
 * @Date:2019/8/29 10:47
 *
 * 如果发生了这种钱不翼而飞的异常,我们应该意识到,我们应该把发钱收钱存到一个事务当中。
 * 解决:
 * 进行手动提交
 * 如果发生异常,我们回滚事务
 * 不管有没有遇见问题我们都要进行正常提交
 * 首先,我们不要抛出异常进行手动抓取,发现关闭资源引用不到向上提取
 * 然后我们再进行测试
 *
 */
public class JDBCDemo1 {
    public static void main(String[] args) {
        //张三给李四转钱
        Connection conn=null;
        PreparedStatement statement1=null;
        PreparedStatement statement2=null;
        try {
            //进行连接数据库
            conn = JDBCUtils.getConnection();//默认是自动提交
            //进行手动提交
            conn.setAutoCommit(false);//设置成手动提交
            //张三少钱
            String sql1="update bank set money=money-1000 where username='zhangsan'";
           statement1 = conn.prepareStatement(sql1);
            //执行
            statement1.executeUpdate();
            //制造错误
            //System.out.println(1/0);
            //李四多钱
            String sql2="update bank set money=money+1000 where username='lisi'";
           statement2 = conn.prepareStatement(sql2);
            statement2.executeUpdate();

        } catch (Exception e) {
            //如果遇见问题,我们就进行回滚
            try {
                conn.rollback();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        } finally {
            //不管有无异常我们都进行提交
            try {
                conn.commit();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            //释放资源
            try {
                conn.close();
                statement1.close();
                statement2.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

案例二
银行系统转账,制造回滚点

package org.westos.demo1shiwu;

import org.westos.utils.JDBCUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Savepoint;

/**
 * @Description:进行转账,设置回滚点
 * @Author:@李小白
 * @Date:2019/8/29 10:47
 *
 * 分别两次进行转账
 * 第一次转账1000,第二次转账500,分别制造异常,发现只要有一个异常我们都转不过去
 * 第一次没有异常第二次遇到异常
 * 解决
 * 制造回滚点
 * 只要出现异常就回到回滚点
 *
 */
public class JDBCDemo2 {
    public static void main(String[] args) {
        //张三给李四转钱
        Connection conn=null;
        PreparedStatement statement1=null;
        PreparedStatement statement2=null;
        PreparedStatement statement3=null;
        PreparedStatement statement4=null;
        Savepoint savepoint=null;
        try {
            //进行连接数据库
            conn = JDBCUtils.getConnection();
            //进行手动提交
            conn.setAutoCommit(false);

            //第一次转账

            //张三少钱
            String sql1="update bank set money=money-1000 where username='zhangsan'";
           statement1 = conn.prepareStatement(sql1);
            //执行
            statement1.executeUpdate();
            //制造异常
           // System.out.println(1/0);
            //李四多钱
            String sql2="update bank set money=money+1000 where username='lisi'";
           statement2 = conn.prepareStatement(sql2);
            statement2.executeUpdate();

            //第二次转账
             savepoint = conn.setSavepoint();

            //张三少钱
            String sql3="update bank set money=money-500 where username='zhangsan'";
            statement3 = conn.prepareStatement(sql3);
            //执行
            statement3.executeUpdate();
            //制造异常
             System.out.println(1/0);
            //李四多钱
            String sql4="update bank set money=money+500 where username='lisi'";
            statement4 = conn.prepareStatement(sql4);
            statement4.executeUpdate();
        } catch (Exception e) {
            //如果遇见问题,我们就进行回滚
            try {
//                conn.rollback();//回滚事件
                conn.rollback(savepoint);//回滚到指定的回滚点
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        } finally {
            //不管有无异常我们都进行提交
            try {
                conn.commit();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            //释放资源
            try {
                conn.close();
                statement1.close();
                statement2.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

事务的隔离级别

如果不考虑隔离的话出现的读问题;
  • 脏读:在一个事务中读取到另一个事务没有提交的数据
  • 不可重复度:在一个事物中,每次读取的数据都不一样(针对update)
  • 虚读(幻读):在一个事物中两次查询的结果不一致(针对insert)无法演示,MySQL已经默认避免了
MySQL的四种隔离级别

read uncommitted 读未提交 上面的三种情况都会出现
read committed 读已提交 可以避免脏读的情况 Oracle默认级别
repeatable read 可重复度 可避免脏读和不可重复读 MySQL默认级别
serializable 串行化 可以避免所有的问题,,但是效率最低

演示:脏读的发生
zhangsan 给lisi 转钱

开启两个窗口进行演示,一个转钱一个收钱
mysql  -uroot    -p123456//登录数据库

可以设置数据库的隔离级别,四种隔离级别分别设置
set session transaction isolation level read  uncommitted;

开启事务 start transaction;
修改数据:udate   bank set money=1500 where username='lisi'


第一种隔离级别
让另一个窗口也开启事务,进行查找数据,查到了就是脏读
zhangsan 一提交commit;  lisi又查不到了

第二种隔离级别:
lisi每次查看的钱数都不一样

第三种隔离级别:
lisi需要把卡退出来在进行查询,才能看到

第四种隔离级别:
lisi需要提交再进行查询,才能看到


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

常见连接池的使用

为什么会有连接池?
由于建立数据库资源是一种非常耗时耗资源的行为,在连接池中放一些连接,放在内存中,需要的时候在从连接池中直接申请,借完要还

DBCP连接池

DataBase Connection Poll数据库连接池
是Apache开发,通过连接池可以自动管理数据库的释放和断开

  • 使用步骤
    1.配置数据库一个JAR包和DBCP的两个JAR包(commons-dbcp-1.4.jar和commons-pool-1.5.6.jar)
    2.使用api
    a.配置文件编码
    a.硬编码(不推荐)

案例:
转账的硬编码和配置文件编码
BasicDataSource bs = new BasicDataSource();

package org.westos.demo2lianjiechi;

import org.apache.commons.dbcp.BasicDataSource;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * @Description:常见的两种连接池之DBC的硬编码方式
 * @Author:@李小白
 * @Date:2019/8/30 16:36
 * DBCP的硬编码的方式首先导入JAR包
 * 然后就是创建数据源,导入Driver的名字和数据库的驱动和用户名和密码
 * 然后进行连接数据库,遍历结果集和关闭资源
 *
 */
public class DBCPDemo1 {

    public static void main(String[] args) throws SQLException {
        //依赖DBCP的两个jar包
        //创建数据源
        BasicDataSource bs = new BasicDataSource();
        bs.setDriverClassName("com.mysql.jdbc.Driver");
        bs.setUrl("jdbc:mysql://localhost:3306/mydb");
        bs.setUsername("root");
        bs.setPassword("123456");
        bs.setMaxWait(1000);
        //进行连接
        Connection conn= bs.getConnection();
        PreparedStatement statement = conn.prepareStatement("select * from user");
        ResultSet resultSet = statement.executeQuery();
        //遍历结果集
        while (resultSet.next()) {
            System.out.println(resultSet.getInt(1));
            System.out.println(resultSet.getString(2));
        }
        //释放资源
        conn.close();
        bs.close();
        statement.close();
        resultSet.close();
    }
}

配置文件编码

package org.westos.demo2lianjiechi;




import org.apache.commons.dbcp.BasicDataSourceFactory;

import javax.sql.DataSource;
import java.io.FileReader;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Properties;

/**
 * @Description:DBPC文件配置
 * @Author:@李小白
 * @Date:2019/8/30 18:11
 */
public class DBPCDemo2 {
    public static void main(String[] args) throws Exception {
        //把JDBC配置文件进行导入,和MySQL的驱动进行导入
        //建立工厂

        Properties properties = new Properties();
        properties.load(new FileReader("src/dbcp.properties"));
        DataSource dataSource = new BasicDataSourceFactory().createDataSource(properties);

        //与数据库进行连接
        Connection conn = dataSource.getConnection();
        PreparedStatement statement = conn.prepareStatement("select * from user");
        ResultSet resultSet = statement.executeQuery();

        //进行遍历
        while (resultSet.next()) {
            System.out.println(resultSet.getInt(1));
            System.out.println(resultSet.getString(2));
        }

        //关闭资源
        conn.close();
        statement.close();
        resultSet.close();

    }
}

C3P0

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

  • 使步骤
    1.导入jar包(c3p0-0.9.1.2.jar)
    2.使用api
    案例:
    硬编码和配置文件编码
    ComboPooledDataSource source = new ComboPooledDataSource();
package org.westos.demo2lianjiechi;


import com.mchange.v2.c3p0.ComboPooledDataSource;

import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * @Description:C3P0硬编代码
 * @Author:@李小白
 * @Date:2019/8/30 18:25
 */
public class C3P0Demo1 {


    public static void main(String[] args) throws PropertyVetoException, SQLException {
        //把数据库和C3P0的jar包进导入
        //创建数据源
        ComboPooledDataSource source = new ComboPooledDataSource();
        source.setDriverClass("com.mysql.jdbc.Driver");
        source.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");//查询的库名字
        source.setUser("root");
        source.setPassword("123456");

        //和数据库进行连接
        Connection conn = source.getConnection();
        PreparedStatement statement = conn.prepareStatement("select * from user ");
        ResultSet resultSet = statement.executeQuery();

        //遍历结果集
        while (resultSet.next()) {
            System.out.println(resultSet.getInt(1));
            System.out.println(resultSet.getString(2));
        }

        //关闭数据流
        conn.close();
        statement.close();
        resultSet.close();

    }
}

配置文件编码

要求1:配置文件的名称:c3p0.properties 或者 c3p0-config.xml
要求2:配置文件的路径:src下编码只需要一句话

new ComboPooledDataSource()//使用默认的配置
//可以切换xml配置文件中的标签 来加载不同的配置比如更换Orecal的数据库的配置
new ComboPooledDataSource(String configName)//使用命名的配置 若配置的名字找不到,使用默认的配置

package org.westos.demo2lianjiechi;

import com.mchange.v2.c3p0.ComboPooledDataSource;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * @Description:C3p0进行文件配置
 * @Author:@李小白
 * @Date:2019/8/30 18:45
 */
public class c3p0Demo2 {
    public static void main(String[] args) throws SQLException {
        //导入C3P0的配置文件jar包和MySQL的驱动JAR包

        //方法一:完成了
        //方法二:如果使用xml文件的第二配置,就把xml文件第二配置的名称传过来,就完成了
        ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource("MyConfig");

        //与数据库进行连接
        Connection conn = comboPooledDataSource.getConnection();
        PreparedStatement statement = conn.prepareStatement("select  * from USER ");
        ResultSet resultSet = statement.executeQuery();

        //遍历结果集
        while (resultSet.next()) {
            System.out.println(resultSet.getInt(1));
            System.out.println(resultSet.getString(2));
        }

        //释放资源
        conn.close();
        statement.close();
        resultSet.close();

    }
}

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(…):执行r操作
update(…):执行cud操作

	使用步骤
		// 创建对象
		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中 返回.

案例

package org.westos.demo2lianjiechi;

import com.alibaba.druid.pool.DruidDataSourceFactory;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.MapHandler;

import javax.sql.DataSource;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
 * @Description:DBUtils:简化JDBC封装的开源工具
 * @Author:@李小白
 * @Date:2019/8/30 20:38
 */
public class DuridDBUtilsDemo1 {
    public static void main(String[] args) throws Exception {
        //导入DBUtils的JAR包,和数据库的JAR包
        //创建德鲁伊的工厂类
        Properties properties = new Properties();
        properties.load(new FileReader("src/druid.properties"));
        DataSource ds = new DruidDataSourceFactory().createDataSource(properties);
        //创建QueryRunner 对象
        //传入数据源
        QueryRunner queryRunner = new QueryRunner(ds);
        //DML DQL返回值,你影响的行数
        int i = queryRunner.update("insert into user values(?,?)", 4, "李强娜");
        if (i>0) {
            System.out.println("插入成功");
        }else {
            System.out.println("插入失败");
        }
        //把你要查询的数据封装起来,//BeanListHandler,把数据库查出来的多条数据用对象封装气啦,再把对象放到集合里面
        List query = queryRunner.query("select * from user", new BeanListHandler(User.class));
        System.out.println(query);
        //查询一条结果把结果放到对象里面 BeanHandler
        User query1 = queryRunner.query("select * from user ", new BeanHandler(User.class));
        System.out.println(query1);
        //MapHandler () 把查询的结果放到Map集合里面
        Map map = queryRunner.query("select * from user ", new MapHandler());
        System.out.println(map);

        Object id = map.get("id");
        System.out.println(id);
        Object username = map.get("username");
        System.out.println(username);

        System.out.println(map.size());

    }
}

package org.westos.demo2lianjiechi;

/**
 * @Description:TODO
 * @Author:@李小白
 * @Date:2019/8/30 21:00
 */
public class User {
    private  int id;
    private String name;

    public User() {
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

德鲁伊连接池

package org.westos.demo2lianjiechi;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidPooledConnection;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * @Description:连接池里面的德鲁伊连接池(DRUID),硬编码的方式
 * @Author:@李小白
 * @Date:2019/8/30 19:12
 */
public class DruidDemo1 {
    public static void main(String[] args) throws SQLException {
        //德鲁伊连接池和DBCP连接池真的很像
        //导入德鲁伊连接池的JAR包和MySQL的jar包
        //创建数据源
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql:///mydb");
        ds.setUsername("root");
        ds.setPassword("123456");
        //和数据库建立连接
        DruidPooledConnection conn = ds.getConnection();
        PreparedStatement statement = conn.prepareStatement("select  * from user");
        ResultSet resultSet = statement.executeQuery();

        //遍历结果集
        while (resultSet.next()) {
            System.out.println(resultSet.getInt(1));
            System.out.println(resultSet.getString(2));
        }

        //释放资源
        conn.close();
        statement.close();
        resultSet.close();
    }
}

package org.westos.demo2lianjiechi;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Properties;

/**
 * @Description:德鲁伊的调用配置文件
 * @Author:@李小白
 * @Date:2019/8/30 19:32
 */
public class DruidDemo2 {
    public static void main(String[] args) throws Exception {
        //导入德鲁伊的配置文件,jar包,MySQL的jar包
        //配置文件的方式
        Properties properties = new Properties();
        properties.load(new FileReader("src/druid.properties"));
        //用工厂类,创建一个数据源
        DataSource dataSource = new DruidDataSourceFactory().createDataSource(properties);

        //开始和数据库进行连接
        Connection conn = dataSource.getConnection();
        PreparedStatement statement = conn.prepareStatement("select * from user");
        ResultSet resultSet = statement.executeQuery();

        //遍历结果集
        while (resultSet.next()) {
            System.out.println(resultSet.getInt(1));
            System.out.println(resultSet.getString(2));
        }

        //释放资源
        conn.close();
        statement.close();
        resultSet.close();

    }
}

你可能感兴趣的:(MySQL数据库)