JDBC(事务 连接池)

事务

事务四大特性(ACID)

事务: 
	生活中的事务: 指的是比较正式的事情
	数据库中的事务: 指的是一组最小逻辑操作单元,这个操作它包含了很多步骤,组成这个事务的这些步骤,是整体不可再分割的,这些步骤要么同时成功,要么同时失败
	
	转账: 张三给李四转账1000元
		1.张三的账户上减去1000元
		出现异常
		2.李四的账户上增加1000元
	上面减钱和加钱这两步操作 必须在同一事务中进行。
    
事务的四大特性(ACID):
1.原子性(Atomicity):
	原子性是指事务是一个不可分割的工作单位 
	事务中的操作要么都发生,要么都不发生
2.一致性:
	事务必须使数据库从一个一致性状态变换到另外一个一致性状态
3.隔离性:
	事务的隔离性是多个用户并发访问数据库时
	数据库为每一个用户开启的事务
	不能被其他事务的操作数据所干扰
	多个并发事务之间要相互隔离
4.持久性:
	持久性是指一个事务一旦被提交
	它对数据库中数据的改变就是永久性的
	接下来即使数据库发生故障也不应该对其有任何影响
	
默认情况下,Connection对象处于自动提交模式下
这意味着它在执行每个语句后都会自动提交更改。
如果禁用了自动提交模式,那么要提交更改就必须显式调用commit方法
否则无法保存数据库更改。
//void setAutoCommit ( boolean autoCommit)
throws SQLException将此连接的自动提交模式设置为给定状态
如果连接处于自动提交模式下,则它的所有SQL语句将被执行并作为单个事务提交。
否则,它的SQL语句将聚集到事务中,
直到调用commit方法或rollback方法为止。
默认情况下,新连接处于自动提交模式。	

事务隔离级别

★不考虑隔离性会出现的读问题
脏读: 
	在一个事务中读取到另一个事务没有提交的数据
不可重复读:
	在一个事务中,两次查询的结果不一致(针对的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是常量    

银行转账

public class JDBCBank {
    public static void main(String[] args) throws Exception {
        //转账:张三给李四转账1000块钱。
        //第一步:张三的账户上要减去1000块钱。
        //第二步:李四的账户上要增加1000块钱。
        //也就是说上面减钱加钱的这两步操作,必须在同一个事务中进行。

        //解决,把数据库,默认自动开启事务,和自动提交事务,改成手动的
        Connection conn = JDBCUtils.getConnection();
        //通过Java代码 设置数据库的隔离级别
        //conn.setTransactionIsolation(Connection.TRANSACTION_READ_UNCOMMITTED);
        //关闭数据库 自动提交事务 变为手动提交
        conn.setAutoCommit(false);
        //为true表示启用自动提交模式 为false表示禁用自动提交模式
        
        //设置回滚点
        Savepoint one = null;
        try {
            //第一步:张三的账户上要减去1000块钱。
            //数据库的默认设置 就是自动开启事事务 自动提交事务
            //执行这条SQL 他是一个单独的事务
            String sql1 = "update bank set money=money-1000 where username='zhangsan'";
            PreparedStatement preparedStatement = conn.prepareStatement(sql1);
            preparedStatement.executeUpdate();

            //模拟异常
            //int i = 1 / 0;

            //第二步:李四的账户上要增加1000块钱。
            //数据库的默认设置 就是自动开启事事务 自动提交事务
            //执行这条SQL 他是一个单独的事务
            String sql2 = "update bank set money=money+1000 where username='lisi'";
            PreparedStatement preparedStatement2 = conn.prepareStatement(sql2);
            preparedStatement2.executeUpdate();

            //设置回滚点
            one = conn.setSavepoint();
            //
            System.out.println("第二次转账");
            //第一步:张三的账户上要减去1000块钱。
            //数据库的默认设置 就是自动开启事事务 自动提交事务
            //执行这条SQL 他是一个单独的事务
            String sql3 = "update bank set money=money-1000 where username='zhangsan'";
            PreparedStatement preparedStatement3 = conn.prepareStatement(sql3);
            preparedStatement3.executeUpdate();

            //模拟异常
            int j = 1 / 0;

            //第二步:李四的账户上要增加1000块钱。
            //数据库的默认设置 就是自动开启事事务 自动提交事务
            //执行这条SQL 他是一个单独的事务
            String sql4 = "update bank set money=money+1000 where username='lisi'";
            PreparedStatement preparedStatement4 = conn.prepareStatement(sql4);
            preparedStatement4.executeUpdate();

        } catch (Exception e) {
            e.printStackTrace();
            if (one == null) {
                //如果遇到异常,就要回滚事务。
                conn.rollback();  // ROLLBACK;
            } else {
                System.out.println("执行了");
                conn.rollback(one); //回滚到指定的回滚点
            }
        } finally {
            //不够有没有遇到异常,我们都提交事务
            conn.commit(); //COMMIT;
        }
        //释放资源
    }
}

连接池

为什么要有连接池?
  由于建立数据库连接是一种非常耗时、耗资源的行为,所以通过连接池预先同数据库建立一些连接,放在内存中,应用程序需要建立数据库连接时直接到连接池中申请一个就行,使用完毕后再归还到连接池中。
    
  数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。	
  
  连接池基本的思想是在系统初始化的时候,将数据库连接作为对象存储在内存中,当用户需要访问数据库时,并非建立一个新的连接,而是从连接池中取出一个已建立的空闲连接对象。使用完毕后,用户也并非将连接关闭,而是将连接放回连接池中,以供下一个请求访问使用。而连接的建立、断开都由连接池自身来管理。同时,还可以通过设置连接池的参数来控制连接池中的初始连接数、连接的上下限数以及每个连接的最大使用次数、最大空闲时间等等。也可以通过其自身的管理机制来监视数据库连接的数量、使用情况等

1.DBCP连接池

DBCP(DataBase Connection Pool) 数据库连接池
是java数据库连接池的一种 由Apache开发
通过数据库连接池,可以让程序自动管理数据库连接的释放和断开

1.依赖DBCP连接池的两个jar包,还要有MySQL的驱动jar包
2.使用:
	方式1:硬编码 参数写死在代码
        //创建连接池
        BasicDataSource ds = new BasicDataSource();
        //配置信息
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/my_database");
        ds.setUsername("root");
        ds.setPassword("123456");
        //从池子中获取连接对象
        Connection conn = ds.getConnection();
        
	方式2:采用配置文件方式
        Properties p = new Properties();
        p.load(new FileReader("dbcp.properties"));
        //创建连接池
        DataSource ds = BasicDataSourceFactory.createDataSource(p);
        Connection c = ds.getConnection();

硬编码

public class DBCPTest1 {
    public static void main(String[] args) throws Exception {
        //创建连接池
        BasicDataSource ds = new BasicDataSource();
        //配置信息
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/my_database");
        ds.setUsername("root");
        ds.setPassword("123456");

        Connection conn = ds.getConnection();
        String sql = "insert into bank values(?,?);";
        PreparedStatement preparedStatement
                = conn.prepareStatement(sql);
        //参数设置
        preparedStatement.setString(1,"tom");
        preparedStatement.setInt(2,8000);

        int i = preparedStatement.executeUpdate();
        System.out.println(i);

        preparedStatement.close();
    }
}

配置文件方式

public class DBCPTest2 {
    public static void main(String[] args) throws Exception {
        //存放配置文件
        Properties properties = new Properties();
        properties.load(new FileReader("dbcp.properties"));

        //创建连接池
        DataSource ds = BasicDataSourceFactory.createDataSource(properties);
        //从池子中获取连接对象
        Connection conn = ds.getConnection();

        String sql = "select * from bank;";
        PreparedStatement preparedStatement = conn.prepareStatement(sql);
        ResultSet resultSet = preparedStatement.executeQuery();
        while (resultSet.next()) {
            String string = resultSet.getString("username");
            System.out.println(string);
        }

        preparedStatement.close();
        resultSet.close();
    }
}

DBCP配置文件

#连接基本设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/my_database
username=root
password=123456


#<!--扩展配置 了解-->
#初始化连接
initialSize=10

#最大连接数量
maxActive=50

#<!-- 最大空闲连接 -->
maxIdle=20

#<!-- 最小空闲连接 -->
minIdle=5

#<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
maxWait=60000

#JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;] 
#注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。
connectionProperties=useUnicode=true;characterEncoding=gbk

#指定由连接池所创建的连接的自动提交(auto-commit)状态。
defaultAutoCommit=true

#driver default 指定由连接池所创建的连接的只读(read-only)状态。
#如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix)
defaultReadOnly=

#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_UNCOMMITTED

2.C3P0连接池

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

c3p0与dbcp区别
    dbcp没有自动回收空闲连接的功能
    c3p0有自动回收空闲连接的功能

1.依赖C3P0连接池的一个jar包,还要有MySQL的驱动jar包
2.使用:
    方式1:硬编码 参数写死在代码
		//创建连接池
        ComboPooledDataSource ds
        	= new ComboPooledDataSource();
        //配置信息
        ds.setDriverClass("com.mysql.jdbc.Driver");
        ds.setJdbcUrl("jdbc:mysql:///my_database");
        ds.setUser("root");
        ds.setPassword("123456");
		//从池子中获取连接对象
        Connection conn = ds.getConnection(); 

    方式2:采用配置文件方式
        要求1:
        	配置文件的名称:
                c3p0.properties 
                或者 c3p0-config.xml
		要求2:
			配置文件的路径:
				src下
        //创建连接池            
		ComboPooledDataSource ds 
                    = new ComboPooledDataSource();
		//从池子中获取连接对象 
        Connection conn = ds.getConnection();
 

硬编码

public class C3P0Test1 {
    public static void main(String[] args) throws PropertyVetoException, SQLException {
        //创建连接池
        ComboPooledDataSource ds = new ComboPooledDataSource();
        //配置信息
        ds.setDriverClass("com.mysql.jdbc.Driver");
        ds.setJdbcUrl("jdbc:mysql:///my_database");
        ds.setUser("root");
        ds.setPassword("123456");
		//从池子中获取连接对象 
        Connection conn = ds.getConnection();
        
        String sql = "insert into bank values(?,?);";
        PreparedStatement preparedStatement
                = conn.prepareStatement(sql);
        //参数设置
        preparedStatement.setString(1,"wang");
        preparedStatement.setInt(2,2000);

        int i = preparedStatement.executeUpdate();
        System.out.println(i);

        preparedStatement.close();
    }
}

配置文件方式

public class C3P0Test2 {
    public static void main(String[] args) throws Exception {
        //创建连接池
        ComboPooledDataSource ds = new ComboPooledDataSource();
        //从池子中获取连接对象 
        Connection conn = ds.getConnection();

        String sql = "select * from bank;";
        PreparedStatement preparedStatement = conn.prepareStatement(sql);
        ResultSet resultSet = preparedStatement.executeQuery();
        while (resultSet.next()) {
            String string = resultSet.getString("username");
            System.out.println(string);
        }

        preparedStatement.close();
        resultSet.close();
    }
}

C3P0配置文件

#连接基本设置
c3p0.driverClass=com.mysql.jdbc.Driver
c3p0.jdbcUrl=jdbc:mysql:///my_database
c3p0.user=root
c3p0.password=123456

3.Druid连接池

Druid 阿里德鲁伊连接池

Duridv是阿里巴巴开源平台上一个数据库连接池实现
它结合了C3P0、DBCP、PROXOOL等DB池的优点
同时加入了日志监控,可以很好的监控DB池连接和SQL的执行情况
可以说是针对监控而生的DB连接池

1.依赖Druid连接池的一个jar包,还要有MySQL的驱动jar包
2.使用:
    方式1:硬编码 参数写死在代码
		//创建连接池
        DruidDataSource ds 
        	= new DruidDataSource();
        //配置信息
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql:///my_database");
        ds.setUsername("root");
        ds.setPassword("123456");
        //获取连接对象
        Connection c = ds.getConnection();

    方式2:采用配置文件方式
		//存放配置文件
        Properties p = new Properties();
        p.load(new FileReader("druid.properties"));
        //创建连接池
        DataSource ds 
            = DruidDataSourceFactory.createDataSource(p);
        //从池子中获取连接对象
        Connection c = ds.getConnection();

        

硬编码

public class DruidTest1 {
    public static void main(String[] args) throws Exception {
        //创建连接池
        DruidDataSource ds = new DruidDataSource();
        //配置信息
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql://localhost:3306/my_database");
        ds.setUsername("root");
        ds.setPassword("123456");
        //获取连接对象
        Connection conn = ds.getConnection();

        String sql = "insert into bank values(?,?);";
        PreparedStatement preparedStatement
                = conn.prepareStatement(sql);
        //参数设置
        preparedStatement.setString(1,"jack");
        preparedStatement.setInt(2,6666);

        int i = preparedStatement.executeUpdate();
        System.out.println(i);

        preparedStatement.close();
    }
}

配置文件方式

public class DruidTest2 {
    public static void main(String[] args) throws Exception {
        //存放配置文件
        Properties properties = new Properties();
        properties.load(new FileReader("druid.properties"));
        //创建连接池
        DataSource ds = DruidDataSourceFactory.createDataSource(properties);
        //从池子中获取连接对象
        Connection conn = ds.getConnection();

        String sql = "select * from bank;";
        PreparedStatement preparedStatement = conn.prepareStatement(sql);
        ResultSet resultSet = preparedStatement.executeQuery();
        while (resultSet.next()) {
            String string = resultSet.getString("username");
            System.out.println(string);
        }

        preparedStatement.close();
        resultSet.close();
    }
}

Druid配置文件

#基本配置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///my_database
username=root
password=123456

#可选配置
filters=stat
initialSize=2
maxActive=300
maxWait=60000
timeBetweenEvictionRunsMillis=60000
minEvictableIdleTimeMillis=300000
validationQuery=SELECT 1
testWhileIdle=true
testOnBorrow=false
testOnReturn=false
poolPreparedStatements=false
maxPoolPreparedStatementPerConnectionSize=200

DBUtils工具类

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

dbUtils 封装了操作对象 帮我们把查询的数据封装好
连接池->连接对象->操作对象

使用步骤:
    1.导入jar包(commons-dbutils-1.4.jar)
    2.创建一个queryrunner类
        queryrunner作用:操作sql语句
        构造方法:
        new QueryRunner(Datasource ds);
    3.编写sql
    4.执行sql
        query(..):执行查询操作
        update(...):执行增删改操作

//传入连接池
通过连接池传入
new QueryRunner(连接池);

String sql = "insert into bank values(?,?);";
int i = queryRunner.update(sql,"qweqwe",15000);//增删改
判断if(i>0)

String sql1 = "select * from bank where money=?;";
queryRunner.query(sql1, new BeanHandler<User>(User.class),9999);//查
//new BeanHandler<Users>(Users.class) 一个对象

String sql2 = "select * from bank;";
queryRunner.query(sql2, new BeanListHandler<User>(User.class));//查
//new BeanListHandler<Users>(Users.class) 多个对象

增删改(DML)

public class JDBC_DBUtils1 {
    public static void main(String[] args) throws Exception {
        //创建连接池
        Properties properties = new Properties();
        properties.load(new FileReader("dbcp.properties"));
        DataSource ds = BasicDataSourceFactory.createDataSource(properties);

        //创建一个queryrunner类
        QueryRunner queryRunner = new QueryRunner(ds);
        
        String sql = "insert into bank values(?,?);";
        int i = queryRunner.update(sql,"qweqwe",15000);

        if (i > 0) {
            System.out.println("修改成功");
        } else {
            System.out.println("修改失败");
        }
    }
}

查(DQL)

public class JDBC_DBUtils2 {
    public static void main(String[] args) throws Exception {
        //创建连接池
        Properties properties = new Properties();
        properties.load(new FileReader("dbcp.properties"));
        DataSource ds = BasicDataSourceFactory.createDataSource(properties);

        //创建一个queryrunner类
        QueryRunner queryRunner = new QueryRunner(ds);

        System.out.println("====查询一条语句====");
        String sql1 = "select * from bank where money=?;";
        User user = queryRunner.query(sql1, new BeanHandler<User>(User.class),9999);
        System.out.println(user);

        System.out.println("====查询多条语句====");
        String sql2 = "select * from bank;";
        List<User> list = queryRunner.query(sql2, new BeanListHandler<User>(User.class));
        for (User user1 : list) {
            System.out.println(user1);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值