一、Connection类的简介

  • 是java API中提供的一个接口
  • 它的实现类由其他厂商完成,用来与数据库的连接
  • 这个接口可以调用方法,来获得SQL语句的对象(Statement、PreparedStatement)
    • 其中Statement接口本身使用较少,而它的子接口PreparedStatement 却经常使用
      • PreparedStatement 比 Statement 更安全,没有SQL注入的问题
      • 它的实例对象表示一条预编译过的 SQL 语句
      • 可以发送SQL语句到数据库

二、常用方法

1. 资源的关闭

void close()
 - 手动关闭Connection资源
    
boolean isClosed()
 - 检索此 Connection 对象是否已经被关闭

2. 创建PreparedStatement对象

PreparedStatement prepareStatement(String sql)
 - 创建 PreparedStatement 对象,方便将 SQL 语句发送到数据库
    
// 还有好几个重载的方法可以创建PreparedStatement的对象,但是使用较少

3. 只读模式

void setReadOnly(boolean readOnly)
 - 设置此 Connection 对象是否处于只读模式
    
boolean isReadOnly()
 - 检索此 Connection 对象是否处于只读模式

4. 自动提交

boolean getAutoCommit()
 - 查询此 Connection 对象的自动提交的状态
    
void setAutoCommit(boolean autoCommit)
 - 设置此 Connection 对象的自动提交的状态
    
void commit()
 - 取消自动提交后,需要手动提交sql语句
    
/*
    1. 经常使用的是取消自动提交,取消自动提交的应用场景:
    	- 事务处理:可以避免出现"脏数据"
    	- 批量处理:可以提高批量处理数据的效率
    
	2. 取消自动提交的目的就是保持数据的完整性
    	- 一个系统的更新操作可能需要多个SQL语句进行操作
    	- 一般在开始时手动设置setAutoCommit(false),等全部的 SQL语句正常执行完后,再手动进行commit()
    	- 如果中间出现错误,可以在catch()中进行rollback(),取消这一次的全部操作,避免了脏数据
    	- 这样即使更新数据的时候报错,更新的内容也不会提交到数据库中
    	- 而如果没有手动的进行setAutoCommit(false),或忘记了回滚数据
    	- 出错时就会造成,前几条SQL语句执行,后几条没有执行
*/

5. 隔离级别

int getTransactionIsolation()
 - 查询此 Connection 对象的当前事务的隔离级别
    
void setTransactionIsolation(int level)
 - 设置此 Connection 对象的当前事务的隔离级别

6. 保存点

Savepoint setSavepoint()
 - 在当前事务中创建一个未命名的保存点 (savepoint),并返回表示它的 Savepoint 对象。
Savepoint setSavepoint(String name)
 - 在当前事务中创建一个具有给定名称的保存点,并返回表示它的 Savepoint 对象。
void releaseSavepoint(Savepoint savepoint)
 - 从当前事务中移除给定 Savepoint 对象

7. 回滚

void rollback()
 - 回滚,意思就是数据库做修改之后未 commit 之前, 使用 rollback 可以恢复数据到修改之前
    
void rollback(Savepoint savepoint)
 - 回滚到保存点,可以恢复数据到Savepoint的位置

三、常用方法的演示

1. 资源的关闭

    @Test
    public void test() throws Exception {
        Connection conn = Utils_01.getConnection();

        // 1. 未关闭资源时,获取的状态是false
        System.out.println(conn.isClosed()); // false

        // 2. 手动关闭资源
        conn.close();

        // 3. 关闭资源后,获取的状态是true
        System.out.println(conn.isClosed()); // true
    }

2. 获取 PreparedStatement 对象

    @Test
    public void test() throws Exception {
        Connection conn = Utils_01.getConnection();

        // 调用方法,得到PreparedStatement对象
        // 注意:PreparedStatement也需要进行资源的关闭
        PreparedStatement ps = conn.prepareStatement("这里填入SQL语句");

        conn.close(); 
        ps.close();
    }

3. 只读模式

(1) 方法的简单使用

    @Test
    public void test() throws Exception {
        Connection conn = Utils_01.getConnection();

        // 1. 查询结果为"false",说明默认情况下不是只读状态,可以更新数据
        System.out.println(conn.isReadOnly());

        // 2. 设置为只读状态
        conn.setReadOnly(true);

        // 3. 更新设置后,查询结果为"true"
        System.out.println(conn.isReadOnly());

        // 4. 关闭资源
        conn.close();
    }

(2) 只读模式下读取数据

    @Test
    public void test() throws Exception {
        Connection conn = Utils_01.getConnection();

        // 1. 设置为只读状态
        conn.setReadOnly(true);

        // 2. 获取SQL语句的预编译状态,PreparedStatement的对象
        String sql = "select count(*) from user_table";
        PreparedStatement ps = conn.prepareStatement(sql);

        // 3. 执行查询操作
        boolean b = ps.execute();

        // 4. 是否查询成功
        System.out.println(b); // 结果为"true",即读取数据成功

        // 5. 关闭资源
        ps.close(); 
        conn.close();
    }

(3) 只读模式下更新数据

// 抛异常:
// java.sql.SQLException: Connection is read-only. Queries leading to data modification are not allowed
// 异常:当前连接处于只读状态,对数据进行修改的查询操作是不被允许的

	@Test
    public void test() throws Exception {
        Connection conn = Utils_01.getConnection();

        // 1. 设置为只读状态
        conn.setReadOnly(true);

        // 2. 获取SQL语句的预编译状态,PreparedStatement的对象
        String sql = "truncate user_table";
        PreparedStatement ps = conn.prepareStatement(sql);

        // 3. 执行更新操作
        boolean b = ps.execute();

        // 4. 是否更新成功
        System.out.println(b); // 结果为"true",即读取数据成功

        // 5. 关闭资源
        ps.close(); // 为了方便,这里用throws处理异常
        conn.close();
    }

4. 自动提交

(1) 方法的简单使用

	@Test
    public void test() throws Exception {
        Connection conn = Utils_01.getConnection();

        // 1. 结果为"true",说明默认是自动提交的状态
        System.out.println(conn.getAutoCommit());

        // 2. 取消自动提交
        conn.setAutoCommit(false);

        // 3. 结果为"false"
        System.out.println(conn.getAutoCommit());

        // 4. 关闭资源
        conn.close(); // 为了方便,这里用throws处理异常
    }

(2) 没有取消自动提交、中间没有出错行为

// 两次操作均正常执行	

	@Test
    public void test() throws Exception {
        Connection conn = Utils_01.getConnection();

        // 第一次操作
        String sql1 = "insert into user_table(`id`, `password`, `balance`)values(?, ?, ?)";
        PreparedStatement ps1 = conn.prepareStatement(sql1);
        ps1.setObject(1, 4);
        ps1.setObject(2, "123456");
        ps1.setObject(3, 10000);
        ps1.execute();
       

        // 第二次操作
        String sql2 = "insert into user_table(`id`, `password`, `balance`)values(?, ?, ?)";
        PreparedStatement ps2 = conn.prepareStatement(sql2);
        ps2.setObject(1, 5);
        ps2.setObject(2, "123456");
        ps2.setObject(3, 10000);
        ps2.execute();

        ps2.close(); 
        ps1.close();
        conn.close();
    }

(3) 没有取消自动提交、中间有出错的行为

// 中间有一个 "5 / 0" 的行为,直接导致第二次操作无法执行,使得只有第一次操作被成功执行

	@Test
    public void test() throws Exception {
        Connection conn = Utils_01.getConnection();

        // 第一次操作
        String sql1 = "insert into user_table(`id`, `password`, `balance`)values(?, ?, ?)";
        PreparedStatement ps1 = conn.prepareStatement(sql1);
        ps1.setObject(1, 4);
        ps1.setObject(2, "123456");
        ps1.setObject(3, 10000);
        ps1.execute();

        System.out.println(5 / 0); // 这里出错,下面的语句就不会执行,但是上面的语句已经执行,这不是我们想要的

        // 第二次操作
        String sql2 = "insert into user_table(`id`, `password`, `balance`)values(?, ?, ?)";
        PreparedStatement ps2 = conn.prepareStatement(sql2);
        ps2.setObject(1, 5);
        ps2.setObject(2, "123456");
        ps2.setObject(3, 10000);
        ps2.execute();

        ps2.close();
        ps1.close();
        conn.close();
    }

(4) 取消自动提交、中间有出错行为

// 第一次操作之后,出现 "5 / 0" 的错误,使得第二次操作无法执行
// 而又因为取消了自动提交,所以第一次操作是无效的

	@Test
    public void test() {

        Connection conn = null;
        PreparedStatement ps1 = null;
        PreparedStatement ps2 = null;

        try {
            conn = Utils_01.getConnection();

            // 取消自动提交
            conn.setAutoCommit(false);

            // 第一次操作
            String sql1 = "insert into user_table(`id`, `password`, `balance`)values(?, ?, ?)";
            ps1 = conn.prepareStatement(sql1);
            ps1.setObject(1, 4);
            ps1.setObject(2, "123456");
            ps1.setObject(3, 10000);
            ps1.execute();

            System.out.println(5 / 0);

            // 第二次操作
            String sql2 = "insert into user_table(`id`, `password`, `balance`)values(?, ?, ?)";
            ps2 = conn.prepareStatement(sql2);
            ps2.setObject(1, 5);
            ps2.setObject(2, "123456");
            ps2.setObject(3, 10000);
            ps2.execute();

            // 手动提交
            conn.commit();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {

            // 关闭Connection资源之前,修改状态为默认的自动提交
            // 主要针对数据库连接池的操作
            try {
                conn.setAutoCommit(true);
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }

            // 关闭资源:为了方便,这里仅简单处理异常
            try {
                ps2.close();
                ps1.close();
                conn.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

(5) 批量处理数据

// 因为每一次再提交数据的时候,都会有时间的消耗
// 而先执行完所有操作,再进行提交,可以减少很多不必要的时间消耗
// 可以发现取消自动提交后,时间消耗由9s直接缩短为1s

	// 1. 没有取消自动提交
	@Test
    public void test1() throws Exception {
        
        Connection conn = Utils_01.getConnection();
        PreparedStatement ps = null;

        long start = System.currentTimeMillis();
        for (int i = 1; i <= 10000; i++) {
            String sql1 = "insert into user_table(`id`, `password`, `balance`)values(?, ?, ?)";
            ps = conn.prepareStatement(sql1);
            ps.setObject(1, i * 10);
            ps.setObject(2, "123456");
            ps.setObject(3, 10000);
            ps.execute();
        }
        long end = System.currentTimeMillis();

        System.out.println(end - start); // 9842ms

        ps.close();
        conn.close();
    }

	// 2. 取消自动提交
	@Test
    public void test2() {
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            conn = Utils_01.getConnection();
            conn.setAutoCommit(false);
            
            long start = System.currentTimeMillis();
            for (int i = 1; i <= 10000; i++) {
                String sql1 = "insert into user_table(`id`, `password`, `balance`)values(?, ?, ?)";
                ps = conn.prepareStatement(sql1);
                ps.setObject(1, i * 10);
                ps.setObject(2, "123456");
                ps.setObject(3, 10000);
                ps.execute();
            }
            conn.commit();
            long end = System.currentTimeMillis();

            System.out.println(end - start); // 954ms,效率明显提高
            
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            
            // 关闭资源前,恢复为自动提交
            try {
                conn.setAutoCommit(true);
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }

            try {
                ps.close(); 
                conn.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

5. 隔离级别

/**
 *      JDBC定义了五种事务隔离级别:
 *          TRANSACTION_NONE             = 0; 驱动不支持事务
 *          TRANSACTION_READ_UNCOMMITTED = 1; 三种并发问题都会发生,允许脏读、不可重复读和幻读
 *          TRANSACTION_READ_COMMITTED   = 2; 只解决了脏读问题,禁止脏读,但允许不可重复读和幻读
 *          TRANSACTION_REPEATABLE_READ  = 4; 解决了脏读、不可重复读,但允许幻读
 *          TRANSACTION_SERIALIZABLE     = 8; 禁止脏读、不可重复读和幻读,隔离级别最高,但效率最低
 */

    @Test
    public void test() throws Exception {
        Connection conn = Utils_01.getConnection();

        // 1. 结果为"4",说明默认的隔离级别为"TRANSACTION_REPEATABLE_READ"
        System.out.println(conn.getTransactionIsolation());

        // 2. 设置隔离级别为"TRANSACTION_READ_COMMITTED",这个级别是常用的
        conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);

        // 3. 结果为"2"
        System.out.println(conn.getTransactionIsolation());

        // 4. 关闭资源
        conn.close();
    }

6. 保存点与事务回滚

    /**
     *      回退
     *          1.设置保存点 savepoint a
     *          2.取消自动提交
     *          3. 在提交之前进行回滚,必须是没有commit前使用
     *              - 如果事务已经提交,那么无论设置多少保存点,都统统消失
     *              - 如果关闭了自动提交,那么必须手动commit()
     *          4. 回退到保存点后,他后面的操作即使是正确的也不会被执行
     */



// 下面的代码:四次操作执行完后,遇到了"5 / 0" 的错误行为,进入catch(),回滚到保存点后再提交事务
// 故,只有第一次、第二次操作起效果,剩余两次无效果

    @Test
    public void test() {
        Connection conn = null;
        Savepoint sp = null;
        try {
            conn = Utils_01.getConnection();
            conn.setAutoCommit(false);


            // 第1次更新数据
            String sql1 = "update user_table set `password` = 'AAA1' WHERE `id` = 1";
            PreparedStatement ps1 = conn.prepareStatement(sql1);
            ps1.execute();
            

            // 第2次更新数据
            String sql2 = "update user_table set `password` = 'BBB2' WHERE `id` = 2";
            PreparedStatement ps2 = conn.prepareStatement(sql2);
            ps2.execute();

            // 保存点
            sp = conn.setSavepoint("第一个保存点");


            String sql3 = "update user_table set `password` = 'CCC3' WHERE `id` = 3";
            PreparedStatement ps3 = conn.prepareStatement(sql3);
            ps3.execute();


            // 第4次数据
            String sql4 = "update user_table set `password` = 'DDD4' WHERE `id` = 4";
            PreparedStatement ps4 = conn.prepareStatement(sql4);
            ps4.execute();


            // 三次操作均执行后,出现错误,进入catch()
            System.out.println(5 / 0);


            // 如果没有出错,在这里手动提交
            conn.commit();

        } catch (Exception e) {
            System.out.println("出错就回滚");
            try {
                // 回滚事务到保存点
                conn.rollback(sp);

                // 出错之后,在这里手动提交
                conn.commit();
            } catch (SQLException throwables) {
                System.out.println("回滚出错");
                throwables.printStackTrace();
            }
        }
    }

四、综合演示

    @Test
    public void test() {
        Connection conn = null;
        PreparedStatement ps = null;
        Savepoint savepoint = null;
        try {
            // 1. 获取连接
            conn = Utils_01.getConnection();

            
            // 2. 设置只读模式,之后就不可以进行更新操作,为了下面的操作,这里不开启了
            // conn.setReadOnly(true);

            
            // 3. 取消自动提交
            conn.setAutoCommit(false);

            
            // 4. 设置隔离级别,使得仅避免脏读问题
            conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);

            
            // 5. 设置保存点
            savepoint = conn.setSavepoint("XX");

            // 6. 预编译
            String sql = "insert into user_table(`id`, `password`, `balance`) values (?, ?, ?)";
            ps = conn.prepareStatement(sql);
            ps.setObject(1, 2);
            ps.setObject(2, "DFG");
            ps.setObject(3, 100);
            

            // 7. 执行SQL语句
            ps.execute();

            
            // 8. 提供错误语句,方便回滚操作
            System.out.println(5 / 0);

            
            // 9. 手动提交事务
            conn.commit();
            System.out.println("正常执行后,手动提交事务");

        }
        catch (Exception e) {
            try {
                System.out.println("出现异常,回滚到保存点");
                conn.rollback(savepoint);
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
            try {
                System.out.println("出现异常,手动提交事务");
                conn.commit();
            } catch (SQLException ee) {
                ee.printStackTrace();
            }
        }
        finally {
            if (ps != null) {
                try {
                    ps.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.commit();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }
    }
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐