JDBC详细笔记

目录:

1、JDBC简介

2、JDBC的本质

3、JDBC开发前的准备工作

4、JDBC编程六步(需要背会)

5、SQL注入问题

5.1、当前程序存在的问题:

5.2、导致SQL注入的根本原因是什么?

5.3、解快SQL注入问题?

5.4、对比一下statement和Preparedstatement?

6、JDBC事务机制:

7、工具类

8、锁


目录:

1、JDBC简介

Java DataBase Connectivity(Java语言连接数据库)

java 数据库连接 是 JavaEE 平台下的技术规范 定义了在 Java 语言中连接数据,执行 SQL 语句的标准 可以为多种关系数据库提供统一访问

2、JDBC的本质

JDBC是SUN公司制定的一套接口(interface)
	java.sql.*; (这个软件包下有很多接口。)

接口都有调用者和实现者。
面向接口调用、面向接口写实现类,这都属于面向接口编程。

为什么要面向接口编程?
	解耦合:降低程序的耦合度,提高程序的扩展力。
	多态机制就是非常典型的:面向抽象编程。(不要面向具体编程)
		建议:
			Animal a = new Cat();
			Animal a = new Dog();
			// 喂养的方法
			public void feed(Animal a){ // 面向父类型编程。
			
			}
		不建议:
			Dog d = new Dog();
			Cat c = new Cat();

思考:为什么SUN制定一套JDBC接口呢?
	因为每一个数据库的底层实现原理都不一样。
	Oracle数据库有自己的原理。
	MySQL数据库也有自己的原理。
	MS SqlServer数据库也有自己的原理。
	....
	每一个数据库产品都有自己独特的实现原理。

JDBC的本质到底是什么?
	一套接口。

对JDBC本质的理解

ab201b8c8b584f65a2384dc09fa4cb7c.png

3、JDBC开发前的准备工作

准备工作,先从官网下载对应的驱动jar包,然后将其配置到环境变量classpath当中。

classpath=.;F:\course\JDBC\resources\MySql Connector Java 5.1.23\mysql-connector-java-5.1.23-bin.jar
以上的配置是针对于文本编辑器的方式开发,使用IDEA工具的时候,不需要配置以上的环境变量。
IDEA有自己的配置方式。

4、JDBC编程六步(需要背会)

第一步:注册驱动(作用:告诉Java程序,即将要连接的是哪个品牌的数据库)

Class.forName("com.mysql.cj.jdbc.Driver");

第二步:获取连接(表示JVM的进程和数据库进程之间的通道打开了,这属于进程之间的通信,重量级的,使用完之后一定要关闭通道。)

Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/ycy","root","root");

第三步:获取数据库操作对象(专门执行sql语句的对象)

Statement stmt = conn.createStatement();
1

第四步:执行SQL语句(DQL DML…)

String sql = "select ename from emp order by ename desc ";
rs = stmt.executeQuery(sql);

第五步:处理查询结果集(只有当第四步执行的是select语句的时候,才有这第五步处理查询结果集。)

while (rs.next()){
  System.out.println(rs.getString("ename"));
}

第六步:释放资源(使用完资源之后一定要关闭资源。Java和数据库属于进程间的通信,开启之后一定要关闭。)

if (rs != null){
          try {
              rs.close();
          } catch (SQLException throwables) {
              throwables.printStackTrace();
          }
}
if (stmt != null){
    try {
        stmt.close();
    } catch (SQLException throwables) {
        throwables.printStackTrace();
    }
}
if (conn != null){
    try {
        conn.close();
    } catch (SQLException throwables) {
        throwables.printStackTrace();
    }
}

4.1类加载的方式注册驱动

/*
	注册驱动的另一种方式(这种方式常用)
*/
import java.sql.*;

public class JDBCTest03{
	public static void main(String[] args){
		try{
			//1、注册驱动
			// 这是注册驱动的第一种写法。
			// DriverManager.registerDriver(new com.mysql.jdbc.Driver());
			// 注册驱动的第二种方式:常用的。
			// 为什么这种方式常用?因为参数是一个字符串,字符串可以写到xxx.properties文件中。
			// 以下方法不需要接收返回值,因为我们只想用它的类加载动作。
			Class.forName("com.mysql.jdbc.Driver");
			//2、获取连接
			Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/ycy","root","root");
			// com.mysql.jdbc.JDBC4Connection@41cf53f9
			System.out.println(conn);

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

由于反射机制,Driver类继承了NonRegisteringDriver实现了java.sql.Driver,并且是静态代码块 所以可以用

Class.forName("com.mysql.jdbc.Driver");

e43c04123802421f8c2dfc810512ea03.png

4.2从属性资源文件中读取连接数据库信息

jdbc.properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://192.168.151.9:3306/ycy
user=root
password=root

JDBC.test04

//将连接数据库的所有信息配置到配置文件中
/*
   实际开发中不建议把连接数据库的信息写死到java程序中。
*/
import java.sql.*;
import java.util.*;
public class JDBCTest04{
   public static void main(String[] args){
      // 使用资源绑定器绑定属性配置文件
      ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
      String driver = bundle.getString("driver");
      String url = bundle.getString("url");
      String user = bundle.getString("user");
      String password = bundle.getString("password");
      Connection conn = null;
      Statement stmt = null;
      try{
         //1、注册驱动
         Class.forName(driver);
         //2、获取连接
         conn = DriverManager.getConnection(url,user,password);
         //3、获取数据库操作对象
         stmt = conn.createStatement();
         //4、执行SQL语句          
         String sql = "update dept set dname = '销售部2', loc = '天津2' where deptno = 20";
         int count = stmt.executeUpdate(sql);
         System.out.println(count == 1 ? "修改成功" : "修改失败");
      }catch(Exception e){
         e.printStackTrace();
      }finally{
         //6、释放资源
         if(stmt != null){
            try{
               stmt.close();
            }catch(SQLException e){
               e.printStackTrace();
            }  
         }
         if(conn != null){
            try{
               conn.close();
            }catch(SQLException e){
               e.printStackTrace();
            }  
         }
      }
   }
}

4.3处理查询结果集

/*
   处理查询结果集(遍历结果集。)
*/
import java.sql.*;
public class JDBCTest05{
   public static void main(String[] args){
      Connection conn = null;
      Statement stmt = null;
      ResultSet rs = null;
      try{
         //1、注册驱动
         Class.forName("com.mysql.jdbc.Driver");
         //2、获取连接
         conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/ycy","root","root");
         //3、获取数据库操作对象
         stmt = conn.createStatement();
         //4、执行sql
         String sql = "select empno as a,ename,sal from emp";
         // int executeUpdate(insert/delete/update)
         // ResultSet executeQuery(select)
         rs = stmt.executeQuery(sql); // 专门执行DQL语句的方法。
         //5、处理查询结果集
         /*
         boolean flag1 = rs.next();
         //System.out.println(flag1); // true
         if(flag1){
            // 光标指向的行有数据
            // 取数据
            // getString()方法的特点是:不管数据库中的数据类型是什么,都以String的形式取出。
            String empno = rs.getString(1); // JDBC中所有下标从1开始。不是从0开始。
            String ename = rs.getString(2);
            String sal = rs.getString(3);
            System.out.println(empno + "," + ename + "," + sal);
         }
         flag1 = rs.next();
         if(flag1){
            // 以下程序的1 2 3 说的第几列。
            String empno = rs.getString(1);
            String ename = rs.getString(2);
            String sal = rs.getString(3);
            System.out.println(empno + "," + ename + "," + sal);
         }
         */
         while(rs.next()){
            /*
            String empno = rs.getString(1);
            String ename = rs.getString(2);
            String sal = rs.getString(3);
            System.out.println(empno + "," + ename + "," + sal);
            */
            /*
            // 这个不是以列的下标获取,以列的名字获取
            //String empno = rs.getString("empno");
            String empno = rs.getString("a"); // 重点注意:列名称不是表中的列名称,是查询结果集的列名称。
            String ename = rs.getString("ename");
            String sal = rs.getString("sal");
            System.out.println(empno + "," + ename + "," + sal);
            */
            // 除了可以以String类型取出之外,还可以以特定的类型取出。
            /*
            int empno = rs.getInt(1);
            String ename = rs.getString(2);
            double sal = rs.getDouble(3);
            System.out.println(empno + "," + ename + "," + (sal + 100));
            */
            int empno = rs.getInt("a");
            String ename = rs.getString("ename");
            double sal = rs.getDouble("sal");
            System.out.println(empno + "," + ename + "," + (sal + 200));
         }
      }catch(Exception e){
         e.printStackTrace();
      }finally{
         //6、释放资源
         if(rs != null){
            try{
               rs.close();
            }catch(Exception e){
               e.printStackTrace();
            }
         }
         if(stmt != null){
            try{
               stmt.close();
            }catch(Exception e){
               e.printStackTrace();
            }
         }
         if(conn != null){
            try{
               conn.close();
            }catch(Exception e){
               e.printStackTrace();
            }
         }
      }
   }
}

4.4使用IDEA开发JDBC代码配置驱动

Project Structure---Libraries(添加Java库)---选择Choose Modules---ok

4.5PowerDesigner安装(设计数据库表等)

next--》

a4932d1e204246c49f25a7a6d121e519.png

d43c81366bb14a05ba852ac0c6c305fb.png9dfc599717fa4be2b1ea75616521e170.png

一路下一步--》

PowerDesigner可以生成sql语句

f11a8948e4db4eb38fa108a7a11f94a7.png0ab0b7baa1514e73a43d6ac347ffa81b.pngd6929239957c48ebb3cbb636aeaa04e6.png8a484110b2134133a886f240ba1c3aa6.png

5、SQL注入问题

5.1、当前程序存在的问题:

用户名: frfr

密码: frfr’ or 1’=‘1’

登录成功

这种现象被称为SQL注入(安全隐患)。

package com.ycy.jdbc;

import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

/*
实现功能:
    1、需求:
        模拟用户登录功能的实现。
    2、业务描述:
        程序运行的时候,提供一个输入的入口,可以让用户输入用户名和密码
        用户输入用户名和密码之后,提交信息,java程序收集到用户信息
        Java程序连接数据库验证用户名和密码是否合法
        合法:显示登录成功
        不合法:显示登录失败
    3、数据的准备:
        在实际开发中,表的设计会使用专业的建模工具,我们这里安装一个建模工具:PowerDesigner
        使用PD工具来进行数据库表的设计。(参见user-login.sql脚本)
    4、当前程序存在的问题:
        用户名:fdsa
        密码:fdsa' or '1'='1
        登录成功
        这种现象被称为SQL注入(安全隐患)。(黑客经常使用)
    5、导致SQL注入的根本原因是什么?
        用户输入的信息中含有sql语句的关键字,并且这些关键字参与sql语句的编译过程,
        导致sql语句的原意被扭曲,进而达到sql注入。
 */
public class JDBCTest06 {
    public static void main(String[] args) {
        // 初始化一个界面
        Map<String,String> userLoginInfo = initUI();
        // 验证用户名和密码
        boolean loginSuccess = login(userLoginInfo);
        // 最后输出结果
        System.out.println(loginSuccess ? "登录成功" : "登录失败");
    }

    /**
     * 用户登录
     * @param userLoginInfo 用户登录信息
     * @return false表示失败,true表示成功
     */
    private static boolean login(Map<String, String> userLoginInfo) {
        // 打标记的意识
        boolean loginSuccess = false;
        // 单独定义变量
        String loginName = userLoginInfo.get("loginName");
        String loginPwd = userLoginInfo.get("loginPwd");

        // JDBC代码
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;

        try {
            // 1、注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            // 2、获取连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/ycy", "root", "root");
            // 3、获取数据库操作对象
            stmt = conn.createStatement();
            // 4、执行sql
            String sql = "select * from t_user where loginName = '"+loginName+"' and loginPwd = '"+loginPwd+"'";
            // 以上正好完成了sql语句的拼接,以下代码的含义是,发送sql语句给DBMS,DBMS进行sql编译。
            // 正好将用户提供的“非法信息”编译进去。导致了原sql语句的含义被扭曲了。
            rs = stmt.executeQuery(sql);
            // 5、处理结果集
            if(rs.next()){
                // 登录成功
                loginSuccess = true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 6、释放资源
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (stmt != null) {
                try {
                    stmt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
        return loginSuccess;
    }

    /**
     * 初始化用户界面
     * @return 用户输入的用户名和密码等登录信息
     */
    private static Map<String, String> initUI() {
        Scanner s = new Scanner(System.in);

        System.out.print("用户名:");
        String loginName = s.nextLine();

        System.out.print("密码:");
        String loginPwd = s.nextLine();

        Map<String,String> userLoginInfo = new HashMap<>();
        userLoginInfo.put("loginName", loginName);
        userLoginInfo.put("loginPwd", loginPwd);

        return userLoginInfo;
    }


}

5.2、导致SQL注入的根本原因是什么?

用户输入的信息中含有SQL语句的关键字,并且这些关键字参与SQL语句的编译过程,

导致sql语句的原意被扭曲,进而达到SQL注入。

5.3、解快SQL注入问题?

只要用户提供的信息不参与SQL语句的编译过程,问题就解决了。

即使用户提供的信息中含有SQL语句的关键字,但是没有参与编译,不起作用。

要想用户信息不参与sQL语句的编译,那么必须使用java.sql.Preparedstatement

Preparedstatement接口继承了java.sql.Statement

Preparedstatement是属于预编译的数据库操作对象。

Preparedstatement的原理是:预先对SQL语句的框架进行编译,然后再给SQL语句传"值”

package com.ycy.jdbc;

import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

/**
 * 作者:杜聚宾
 *
 * 1、解决SQL注入问题?
 *      只要用户提供的信息不参与SQL语句的编译过程,问题就解决了。
 *      即使用户提供的信息中含有SQL语句的关键字,但是没有参与编译,不起作用。
 *      要想用户信息不参与SQL语句的编译,那么必须使用java.sql.PreparedStatement
 *      PreparedStatement接口继承了java.sql.Statement
 *      PreparedStatement是属于预编译的数据库操作对象。
 *      PreparedStatement的原理是:预先对SQL语句的框架进行编译,然后再给SQL语句传“值”。
 * 2、测试结果:
 *      用户名:fdas
 *      密码:fdsa' or '1'='1
 *      登录失败
 * 3、解决SQL注入的关键是什么?
 *      用户提供的信息中即使含有sql语句的关键字,但是这些关键字并没有参与编译。不起作用。
 * 4、对比一下Statement和PreparedStatement?
 *      - Statement存在sql注入问题,PreparedStatement解决了SQL注入问题。
 *      - Statement是编译一次执行一次。PreparedStatement是编译一次,可执行N次。PreparedStatement效率较高一些。
 *      - PreparedStatement会在编译阶段做类型的安全检查。
 *
 *      综上所述:PreparedStatement使用较多。只有极少数的情况下需要使用Statement
 * 5、什么情况下必须使用Statement呢?
 *      业务方面要求必须支持SQL注入的时候。
 *      Statement支持SQL注入,凡是业务方面要求是需要进行sql语句拼接的,必须使用Statement。
 */
public class JDBCTest07 {
    public static void main(String[] args) {
        // 初始化一个界面
        Map<String,String> userLoginInfo = initUI();
        // 验证用户名和密码
        boolean loginSuccess = login(userLoginInfo);
        // 最后输出结果
        System.out.println(loginSuccess ? "登录成功" : "登录失败");
    }

    /**
     * 用户登录
     * @param userLoginInfo 用户登录信息
     * @return false表示失败,true表示成功
     */
    private static boolean login(Map<String, String> userLoginInfo) {
        // 打标记的意识
        boolean loginSuccess = false;
        // 单独定义变量
        String loginName = userLoginInfo.get("loginName");
        String loginPwd = userLoginInfo.get("loginPwd");

        // JDBC代码
        Connection conn = null;
        PreparedStatement ps = null; // 这里使用PreparedStatement(预编译的数据库操作对象)
        ResultSet rs = null;

        try {
            // 1、注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            // 2、获取连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/ycy", "root", "root");
            // 3、获取预编译的数据库操作对象
            // SQL语句的框子。其中一个?,表示一个占位符,一个?将来接收一个“值”,注意:占位符不能使用单引号括起来。
            String sql = "select * from t_user where loginName = ? and loginPwd = ?";
            // 程序执行到此处,会发送sql语句框子给DBMS,然后DBMS进行sql语句的预先编译。
            ps = conn.prepareStatement(sql);
            // 给占位符?传值(第1个问号下标是1,第2个问号下标是2,JDBC中所有下标从1开始。)
            ps.setString(1, loginName);
            ps.setString(2, loginPwd);
            // 4、执行sql
            rs = ps.executeQuery();
            // 5、处理结果集
            if(rs.next()){
                // 登录成功
                loginSuccess = true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 6、释放资源
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (ps != null) {
                try {
                    ps.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
        return loginSuccess;
    }

    /**
     * 初始化用户界面
     * @return 用户输入的用户名和密码等登录信息
     */
    private static Map<String, String> initUI() {
        Scanner s = new Scanner(System.in);

        System.out.print("用户名:");
        String loginName = s.nextLine();

        System.out.print("密码:");
        String loginPwd = s.nextLine();

        Map<String,String> userLoginInfo = new HashMap<>();
        userLoginInfo.put("loginName", loginName);
        userLoginInfo.put("loginPwd", loginPwd);

        return userLoginInfo;
    }
}

5.4、对比一下statement和Preparedstatement?

Statement存在sql注入问题,Preparedstatement解决了SQL注入问题。

Statement是编译一次执行一次。Preparedstatement是编译一次可执行N次,效率更高。

Preparedstatement会在编译阶段做类型的安全检查。

package com.ycy.jdbc;

import java.sql.*;
import java.util.Scanner;

preparestatement
public class Test02 {
    public static void main(String[] args) {
        //用户在控制台输入desc是降序,asc是升序
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入desc(降序查看)或asc(升序查看)");
        System.out.println("请输入:");
        String keyword = sc.nextLine();

        //执行sql

        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            //1注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            //2获取连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/ycy","root","root");
            //获取预编译的数据库操作对象
            String sql = "select ename from emp order by ename ? ";
            ps = conn.prepareStatement(sql);
            ps.setString(1,keyword);
            //执行sql
            rs = ps.executeQuery();
            //遍历结果集
            while (rs.next()){
                System.out.println(rs.getString("ename"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if (rs != null){
                try {
                    rs.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if (ps != null){
                try {
                    ps.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if (conn != null){
                try {
                    conn.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }
        }

    }

5.5statement用途

只能说statement和preparedstatement各自有各自的好处

package com.ycy.jdbc;

import java.sql.*;
import java.util.Scanner;

/**
 * 作者:杜聚宾
 */
public class JDBCTest08 {
    public static void main(String[] args) {
        /*
        // 用户在控制台输入desc就是降序,输入asc就是升序
        Scanner s = new Scanner(System.in);
        System.out.println("输入desc或asc,desc表示降序,asc表示升序");
        System.out.print("请输入:");
        String keyWords = s.nextLine();

        // 执行SQL
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            // 注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            // 获取连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/ycy", "root","root");
            // 获取预编译的数据库操作对象
            String sql = "select ename from emp order by ename ?";
            ps = conn.prepareStatement(sql);
            ps.setString(1, keyWords);
            // 执行SQL
            rs = ps.executeQuery();
            // 遍历结果集
            while(rs.next()){
                System.out.println(rs.getString("ename"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (ps != null) {
                try {
                    ps.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
        */

        // 用户在控制台输入desc就是降序,输入asc就是升序
        Scanner s = new Scanner(System.in);
        System.out.println("输入desc或asc,desc表示降序,asc表示升序");
        System.out.print("请输入:");
        String keyWords = s.nextLine();

        // 执行SQL
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        try {
            // 注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            // 获取连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/ycy", "root","root");
            // 获取数据库操作对象
            stmt = conn.createStatement();
            // 执行SQL
            String sql = "select ename from emp order by ename " + keyWords;
            rs = stmt.executeQuery(sql);
            // 遍历结果集
            while(rs.next()){
                System.out.println(rs.getString("ename"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (stmt != null) {
                try {
                    stmt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

5.6PreparedStatement完成增删改

package com.ycy.jdbc;

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

/**
 * 作者:杜聚宾
 * PreparedStatement完成INSERT DELETE UPDATE
 */
public class JDBCTest09 {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            // 1、注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            // 2、获取连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/ycy", "root","root");
            // 3、获取预编译的数据库操作对象
            /*String sql = "insert into dept(deptno,dname,loc) values(?,?,?)";
            ps = conn.prepareStatement(sql);
            ps.setInt(1, 60);
            ps.setString(2, "销售部");
            ps.setString(3, "上海");*/

            /*String sql = "update dept set dname = ?, loc = ? where deptno = ?";
            ps = conn.prepareStatement(sql);
            ps.setString(1, "研发一部");
            ps.setString(2, "北京");
            ps.setInt(3, 60);*/

            String sql = "delete from dept where deptno = ?";
            ps = conn.prepareStatement(sql);
            ps.setInt(1, 60);

            // 4、执行SQL
            int count = ps.executeUpdate();
            System.out.println(count);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 6、释放资源
            if (ps != null) {
                try {
                    ps.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

6、JDBC事务机制

1、JDBC中的事务是自动提交的,什么是自动提交?
    只要执行任意一条DML语句,则自动提交一次。这是JDBC默认的事务行为。
    但是在实际的业务当中,通常都是N条DML语句共同联合才能完成的,必须
     保证他们这些DMI语句在同一个事务中同时成功或者同时失败。
 2、以下程序先来验证一下JDBc的事务是否是自动提交机制!
     测试结果: JDBC中 只要执行任意一条 DML语句,就提交一次
package com.ycy.jdbc;

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

/**
 * JDBC事务机制:
 *      1、JDBC中的事务是自动提交的,什么是自动提交?
 *          只要执行任意一条DML语句,则自动提交一次。这是JDBC默认的事务行为。
 *          但是在实际的业务当中,通常都是N条DML语句共同联合才能完成的,必须
 *          保证他们这些DML语句在同一个事务中同时成功或者同时失败。
 *
 *      2、以下程序先来验证一下JDBC的事务是否是自动提交机制!
 *          测试结果:JDBC中只要执行任意一条DML语句,就提交一次。
 */
public class JDBCTest10 {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            // 1、注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            // 2、获取连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/ycy", "root","root");
            // 3、获取预编译的数据库操作对象
            String sql = "update dept set dname = ? where deptno = ?";
            ps = conn.prepareStatement(sql);

            // 第一次给占位符传值
            ps.setString(1, "X部门");
            ps.setInt(2, 30);
            int count = ps.executeUpdate(); // 执行第一条UPDATE语句
            System.out.println(count);

            // 重新给占位符传值
            ps.setString(1, "y部门");
            ps.setInt(2, 20);
            count = ps.executeUpdate(); // 执行第二条UPDATE语句
            System.out.println(count);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 6、释放资源
            if (ps != null) {
                try {
                    ps.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

重点三行代码

conn.setAutoCommit(false);

conn.commit();

conn.rollback();

难的是集群事务,就是多个服务不在一起时

package com.ycy.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
 * sql脚本:
 *  drop table if exists t_act;
 *  create table t_act(
 *      actno int,
 *      balance double(7,2) // 注意:7表示有效数字的个数,2表示小数位的个数。
 *  );
 *  insert into t_act(actno,balance) values(111,20000);
 *  insert into t_act(actno,balance) values(222,0);
 *  commit;
 *  select * from t_act;
 *
 *  重点三行代码?
 *      conn.setAutoCommit(false);
 *      conn.commit();
 *      conn.rollback();
 */
public class JDBCTest11 {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            // 1、注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            // 2、获取连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/ycy", "root","root");
            // 将自动提交机制修改为手动提交
            conn.setAutoCommit(false); // 开启事务
            // 3、获取预编译的数据库操作对象
            String sql = "update t_act set balance = ? where actno = ?";
            ps = conn.prepareStatement(sql);
            // 给?传值
            ps.setDouble(1, 10000);
            ps.setInt(2, 111);
            int count = ps.executeUpdate();
            //String s = null;
            //s.toString();
            // 给?传值
            ps.setDouble(1, 10000);
            ps.setInt(2, 222);
            count += ps.executeUpdate();
            System.out.println(count == 2 ? "转账成功" : "转账失败");
            // 程序能够走到这里说明以上程序没有异常,事务结束,手动提交数据
            conn.commit(); // 提交事务
        } catch (Exception e) {
            // 回滚事务
            if(conn != null){
                try {
                    conn.rollback();
                } catch (SQLException e1) {
                    e1.printStackTrace();
                }
            }
            e.printStackTrace();
        } finally {
            // 6、释放资源
            if (ps != null) {
                try {
                    ps.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}


7、工具类

package com.ycy.jdbc.utils;
import java.sql.*;
/**
 * JDBC工具类,简化JDBC编程。
 */
public class DBUtil {
    /**
     * 工具类中的构造方法都是私有的。
     * 因为工具类当中的方法都是静态的,不需要new对象,直接采用类名调用。
     */
    private DBUtil() {
    }
    // 静态代码块在类加载时执行,并且只执行一次。
    static {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取数据库连接对象
     *
     * @return 连接对象
     * @throws SQLException
     */
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection("jdbc:mysql://localhost:3306/ycy", "root", "root");
    }
    /**
     * 关闭资源
     * @param conn 连接对象
     * @param ps 数据库操作对象
     * @param rs 结果集
     */
    public static void close(Connection conn, Statement ps, ResultSet rs){
        if(rs != null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(ps != null){
            try {
                ps.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if(conn != null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
package com.ycy.jdbc;

import com.ycy.jdbc.utills.DBUtil;

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

public class test07 {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;

        try {
            conn = DBUtil.getConnection();
            //错误写法
//            String sql = "select ename from emp where ename like '_?%'";
//            ps = conn.prepareStatement(sql);
//            ps.setString(1,"A");

            String sql = "select ename from emp where ename like ?";
            ps = conn.prepareStatement(sql);
            ps.setString(1,"_A%");
            rs = ps.executeQuery();
            while(rs.next()){
                System.out.println(rs.getString("ename"));
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            DBUtil.close(conn,ps,rs);
        }

    }
}

8、锁

行级锁 – 悲观锁

cdf5079a88644824814c3dd009d8f86e.png

select ename, job, sal from emp where job= ' MANAGER ' for update; 

在事务没结束,上面代码就将三条数据锁住,其他事务无法修改

d1fe8867b8e74eb4b121328035617fb9.png

事务1

package com.ycy.jdbc;

import com.ycy.jdbc.utills.DBUtil;

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

/**
 * 这个程序开启一个事务,这个事务专门进行查询,并且使用行级锁/悲观锁,锁住相关的记录。
 */
public class Test08 {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;

        try {
            conn = DBUtil.getConnection();
            //开启事务
            conn.setAutoCommit(false);

            String sql = "select ename,job,sal from emp where job = ? for update";
            ps = conn.prepareStatement(sql);
            ps.setString(1,"MANAGER");
            rs = ps.executeQuery();
            while(rs.next()){
                System.out.println(rs.getString("ename")+","+rs.getString("job")
                +","+rs.getDouble("sal"));
            }
            //提价事务 = 事务结束
            conn.commit();
        } catch (SQLException throwables) {
            if (conn != null){
                try {
                    conn.rollback();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            throwables.printStackTrace();
        }finally {
            DBUtil.close(conn,ps,rs);
        }
    }
}

事务2

package com.ycy.jdbc;

import com.ycy.jdbc.utills.DBUtil;

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

public class test09 {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;

        try {
            conn = DBUtil.getConnection();
            conn.setAutoCommit(false);

            String sql = "update emp set sal = sal * 1.1 where job = ?";
            ps = conn.prepareStatement(sql);
            ps.setString(1,"MANAGER");
            int count = ps.executeUpdate();
            System.out.println(count);
            conn.commit();
        } catch (SQLException throwables) {
            if(conn!= null){
                try {
                    conn.rollback();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            throwables.printStackTrace();
        }finally {
            DBUtil.close(conn,ps,null);
        }
    }

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

YCY^v^

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值