• JDBC知识点


    1.JDBC是什么?

    Java Database Connection(Java语言连接数据库)
    具体来讲,就是通过java连接广泛的数据库,并对表中数据执行增删改查等操作的技术。
    
    • 1
    • 2

    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的本质到底是什么?
    	一套接口。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    3.JDBC技术相关接口

    接口作用
    Driver驱动接口,定义建立连接的方式
    DriverManager工具类,用于管理驱动,可以获取数据库的链接
    Connection表示Java与数据库建立的连接对象(接口)
    ResultSet结果集,用于获取查询语句的结果
    Statement提供了执行语句和获取结果的基本方法
    PreparedStatement发送SQL语句的工具

    4.JDB编程六步(牢记)

    第一步:注册驱动

    	告诉Java程序,即将要连接的是哪个品牌的数据库!
    
    	有两种方式:
    		第一种注册驱动的方式
               		 	/*DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());*/
                		第二种注册驱动的方式(类加载方式)常用!
               		 	Class.forName("com.mysql.cj.jdbc.Driver");
    		第二种方式常用,因为参数是一个字符串,将字符串可以写到xxx.properties文件中。
    
    	通过资源绑定器绑定属性配置文件:
    			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");
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    第二步:获取连接

    	表示JVM的进程和数据库进程之间的通道打开了
    	属于进程间的通信,重量级的,使用完一定要关闭通道
    	
    	方式:
    		Connection conn=DriverManager.getConnection(url,user,password);
    		
    	url解析:
    		url:统一资源定位器
    		https://www.baidu.com/ 这就是url
    		url包括哪几部分?
    			协议
    			IP
    			端口号
    			资源名
    		
    		http://182.61.200.7:80/index.html
    			http:// 通信协议
    			182.61.200.7 服务器IP地址
    			80 服务器上软件的端口
    			index.html 是服务器上某个资源名
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    第三步:获取数据库操作对象

    	专门执行sql语句的对象
    	
    	方式:
    		Statement stmt=conn.createStatement();
    
    • 1
    • 2
    • 3
    • 4

    第四步:执行SQL语句

    	DML:
    		String sql="insert into dept2 values(50,'项目经理','山东')";
               		int count=stmt.executeUpdate(sql);
               		System.out.println(count==1 ?"插入成功" :"插入失败");
    	
    		executeUpdate()返回值类型为int类型,为执行受到影响的行数
    
    	DQL:
    		String sql2="select * from dept2";
                		ResultSet rs=stmt.executeQuery(sql2);
    
    		executeQuery()返回值类型为ResultSet类型,为查询结果集
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    第五步:处理查询结果集

    	当第四步执行的是select语句的时候,才有第五步处理查询结果集
    	
    	遍历方式:
    		while(rs.next()){
               	String deptno=rs.getString("deptno");
    			//int deptno=rs.getInt("deptno");
               	String dname=rs.getString("dname");
                String loc=rs.getString("loc");
                System.out.println(deptno+","+dname+","+loc);
         	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    第六步:释放资源

    	使用完资源之后一定要关闭资源。
    	Java和数据库属于进程间的通信,开启之后一定要关闭。
    	
    	方式:
    			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) {
                        throw new RuntimeException(e);
                    }
                }
    	
    	释放规则:
    		打开时:Connection -> Statement -> ResultSet
    		关闭时:ResultSet-> Statement -> Connection
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    演示完整样例

    import java.sql.*;
    
    public class Main {
        public static void main(String[] args) {
            Connection conn=null;
            Statement stmt=null;
            ResultSet rs=null;
            try {
                //1.注册驱动
                //第一种注册驱动的方式
                /*DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());*/
                //第二种注册驱动的方式(类加载方式)
                Class.forName("com.mysql.cj.jdbc.Driver");
    
                //2.获取连接
                //统一资源定位器
                String url="jdbc:mysql://localhost:3306/jmpower";
                String user="root";
                String password="123";
                conn=DriverManager.getConnection(url,user,password);
    
                //3.获取数据库操作对象
                stmt=conn.createStatement();
    
                //4.执行SQL语句(DML,DQL)
                //DML语句
                /*String sql="insert into dept2 values(50,'项目经理','山东')";
                int count=stmt.executeUpdate(sql);
                System.out.println(count==1 ?"插入成功" :"插入失败");*/
                //DQL语句
                String sql2="select * from dept2";
                rs=stmt.executeQuery(sql2);
    
                //5.处理查询结果集
                while(rs.next()){
                    String deptno=rs.getString("deptno");
                    String dname=rs.getString("dname");
                    String loc=rs.getString("loc");
                    System.out.println(deptno+","+dname+","+loc);
                }
    
            } catch (SQLException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException 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) {
                        throw new RuntimeException(e);
                    }
                }
            }
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73

    5.SQL注入问题

    import java.sql.*;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Scanner;
    
    /*
    实现功能:
        1.需求:
            模拟用户登录功能的实现。
        2.业务描述:
            程序运行的时候,提供一个输入的入口,可以让用户输入用户名和密码
            用户输入用户名和密码之后,提交信息,java程序收集到用户信息
            java程序连接数据库验证用户名和密码是否合法
            合法:显示用户登陆成功
            不合法:显示用户登陆失败
        3.数据的准备:
             在实际开发中,表的设计会使用专业的建模工具,我们这里安装一个建模工具,PowerDesigner
             使用PD工具来进行数据库表的设计。
        4.当前程序存在的问题:
            用户名:sdaf
            密码:sdaf' or '1'='1
            登陆成功
            这种现象被称为SQL注入(存在安全隐患)(黑客常用)
        5.导致SQL注入的根本原因是什么?
            用户输入的信息中含有sql语句的关键字,并且这些关键字参与sql语句的编写过程,
            导致sql语句的原意被扭曲,进而达到sql注入。
     */
    public class JDBCTest01 {
    
        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 {
                //注册驱动
                Class.forName("com.mysql.cj.jdbc.Driver");
                //获取连接
                conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/jmpower","root","123");
                //获取数据库操作对象
                stmt=conn.createStatement();
                //执行SQL语句
                String sql="select * from t_user where loginName='"+loginName+"' and loginPwd='"+loginPwd+"'";
                rs=stmt.executeQuery(sql);
                //以上正好完成了sql与语句的拼接,以下代码的含义是,发送sql语句给DBMS,DBMS进行sql编译
                //正好将用户提供的"非法信息"编译进去,导致了原sql语句的含义被扭曲了。
                //处理查询结果集
                if(rs.next()){
                    //登陆成功
                     loginSuccess=true;
                }
            } 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();
                    }
                    ;
                }
            }
            return loginSuccess;
        }
    
        /**
         * 初始化用户界面
         * @return 用户输入的用户名和密码等登录信息
         */
        private static Map<String, String> initUI() {
            Scanner sc=new Scanner(System.in);
            System.out.print("用户名: ");
            String loginName=sc.nextLine();
            System.out.print("密码: ");
            String loginPwd=sc.nextLine();
            Map<String, String> userLoginInfo=new HashMap<>();
            userLoginInfo.put("loginName",loginName);
            userLoginInfo.put("loginPwd",loginPwd);
            return userLoginInfo;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117

    1)当前程序存在的问题:
    用户名:sdaf
    密码:sdaf’ or ‘1’='1
    登陆成功
    这种现象被称为SQL注入(存在安全隐患)(黑客常用)
    2)导致SQL注入的根本原因是什么?
    用户输入的信息中含有sql语句的关键字,并且这些关键字参与sql语句的编写过程,
    导致sql语句的原意被扭曲,进而达到sql注入。

    6.解决SQL注入问题?

    只要用户提供的信息不参与sql语句的编译过程,问题就解决了。
    即使用户提供的信息中含有sql语句的关键字,但是没有参与编译,不起作用。
    要想用户信息不参与sql语句的编译,那么必须使用java.sql.PreparedStatement接口。
    
    java.sql.PreparedStatement:
    	PreparedStatement是属于预编译的数据库操作对象。
    	PreparedStatement的原理是:预先对sql语句的框架进行编译,然后再给sql语句穿"值"。
    
    关键:用户提供的信息中即使含有sql语句的关键字,但是这些关键字并没有参与编译,不起作用。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    演示代码:

    import java.sql.*;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Scanner;
    
    //解决sql注入后的代码
    /*
        用户名:fdsa
        密码:fdsa' or '1'='1
        登陆失败
     */
    public class JDBCTest01 {
    
        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;
            ResultSet rs=null;
            try {
                //注册驱动
                Class.forName("com.mysql.cj.jdbc.Driver");
                //获取连接
                conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/jmpower","root","123");
                //获取预编译的数据库操作对象
                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);
                //执行SQL语句
                rs=ps.executeQuery();
                //处理查询结果集
                if(rs.next()){
                    //登陆成功
                     loginSuccess=true;
                }
            } 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();
                    }
                    ;
                }
            }
            return loginSuccess;
        }
    
        /**
         * 初始化用户界面
         * @return 用户输入的用户名和密码等登录信息
         */
        private static Map<String, String> initUI() {
            Scanner sc=new Scanner(System.in);
            System.out.print("用户名: ");
            String loginName=sc.nextLine();
            System.out.print("密码: ");
            String loginPwd=sc.nextLine();
            Map<String, String> userLoginInfo=new HashMap<>();
            userLoginInfo.put("loginName",loginName);
            userLoginInfo.put("loginPwd",loginPwd);
            return userLoginInfo;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103

    7.Statement和PreparedStatement对比

    1.对比
    	1Statement存在sql注入问题,PreparedStatement解决了sql注入问题
    	2Statement是编译一次执行一次,PreparedStatement是编译一次,可执行n次。PreparedStatement效率较高一些。
    	3PreparedStatement会在编译阶段做类型的安全检查。
    
    2.什么情况下必须使用Statement呢?
    	业务方面要求必须支持sql注入的时候。
    	Statement支持sql注入,凡是业务方面要求是需要进行sql语句拼接的,必须使用Statement。
    	why?
    	Statement进行sql语句拼接时,拼接的字符串不带左右两边的单引号,而
    	PreparedStatement语句中?set时,会传入字符串左右两边的单引号。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    必须使用Statement的业务

    import javax.swing.plaf.nimbus.State;
    import java.sql.*;
    import java.util.Scanner;
    
    public class JDBCTest02 {
        public static void main(String[] args) {
            //用户输入
            Scanner sc=new Scanner(System.in);
            System.out.println("输入desc或asc,desc表示降序,asc表示升序");
            System.out.print("请输入: ");
            String keyWords=sc.nextLine();
    
            //JDBC语句
            Connection conn=null;
            Statement stmt=null;
            ResultSet rs=null;
            try {
                //注册驱动
                Class.forName("com.mysql.cj.jdbc.Driver");
                //获取连接
                conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/jmpower","root","021107");
                //获取数据库操作对象
                stmt=conn.createStatement();
                //执行sql语句
                String sql="select * 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();
                    }
                }
            }
    
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    8.演示jdbc事务自动提交机制

            Connection conn=null;
            PreparedStatement ps=null;
            ResultSet rs=null;
            try {
                //注册驱动
                Class.forName("com.mysql.cj.jdbc.Driver");
                //获取连接
                conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/jmpower","root","123");
                //获取预编译的数据库操作对象
                String sql="update dept2 set dname=? where deptno=?";
                ps=conn.prepareStatement(sql);
                //第一次给占位符传值
                ps.setString(1,"x部门");
                ps.setString(2,"30");
                int count=ps.executeUpdate();//执行第一条update语句
                System.out.println(count);// 1
    
                //第二次给占位符传值
                ps.setString(1,"y部门");
                ps.setString(2,"20");
                count=ps.executeUpdate();//执行第二条update语句
                System.out.println(count);// 1
            } 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();
                    }
                }
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    1)只要执行任意一条DML语句,则自动提交一次,这是jdbc默认的事务行为。但是在实际的业务当中,通常都是N条DML语句共同联合才能完成的,必须保证他们这些DML语句在同一个事务中同时成功或者同时失败。
    2)以上程序的测试结果: jdbc中只要执行任意一条DML语句,就提交一次。

    9.账户转账演示事务(重点三行代码)

    import java.sql.*;
    
    /*
    sql脚本:
        drop table if exists t_act;
        create table t_act{
            actno int,
            balance double(7,2)
        };
        insert into t_act(actno,balance) values(111,20000);
        insert into t_act(actno,balance) values(222,0);
        commit;
        select * from t_user;
    
        重点三行代码?
            conn.setAutoCommit(false);
            conn.commit();
            conn.rollback();
     */
    public class JDBCTest04 {
        public static void main(String[] args) {
            Connection conn=null;
            PreparedStatement ps=null;
            try {
                //注册驱动
                Class.forName("com.mysql.cj.jdbc.Driver");
                //获取连接
                conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/jmpower","root","123");
                //将自动提交机制改为手动
                conn.setAutoCommit(false);//开启事务
                //获取预编译的数据库操作对象
                String sql="update t_act set balance=? where actno=?";
                ps=conn.prepareStatement(sql);
    
                //给?传值
                ps.setInt(1, 10000);
                ps.setInt(2,111);
                int count=ps.executeUpdate();//执行sql语句
    
                //给?传值
                ps.setInt(1,10000);
                ps.setInt(2,222);
                count+=ps.executeUpdate();//执行sql语句
    
                System.out.println(count==2 ?"转账成功" :"转账失败");// 转账成功
    
                //程序能够走到这里说明以上程序没有异常,事务结束,手动提交数据
                conn.commit();
    
            } catch (Exception e) {
                //出现异常,回滚事务
                if(conn!=null){
                    try {
                        conn.rollback();
                    } catch (SQLException ex) {
                        e.printStackTrace();
                    }
                }
                e.printStackTrace();
            } finally {
                //释放资源
                if (ps != null) {
                    try {
                        ps.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if (conn != null) {
                    try {
                        conn.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            }
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79

    10.JDBC工具类

    工具类

    import java.sql.*;
    
    /*
        JDBC工具类
     */
    public class JDBCUtils {
        /**
         *工具类中的构造方法都是私有的。
         *因为工具类当中的方法是静态的,不需要new对象,直接采用类名调用。
         */
        private JDBCUtils(){}
    
        //静态代码块在类加载时执行,并且只执行一次
        //只需要注册一次驱动
        static {
            try {
                Class.forName("com.mysql.cj.jdbc.Driver");
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 获取数据库连接对象
         * @return 连接对象
         * @throws SQLException
         */
        public static Connection getConnection() throws SQLException {
            return DriverManager.getConnection("jdbc:mysql://localhost:3306/jmpower","root","123");
        }
    
        /**
         * 释放资源
         * @param conn 连接对象
         * @param ps 数据库操作对象
         * @param rs 查询结果集
         */
        public static void close(Connection conn, PreparedStatement 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();
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    测试工具类

    import java.sql.*;
    
    /*
    这个程序完成两个任务:
        1.测试JDBCUtils类
        2.模糊查询
     */
    public class JDBCTest06 {
        public static void main(String[] args) {
            Connection conn=null;
            PreparedStatement ps=null;
            ResultSet rs=null;
            try {
                //获取连接
                conn=JDBCUtils.getConnection();
                //获取预编译的数据库操作对象
                String sql="select ename from emp where ename like ?";
                ps=conn.prepareStatement(sql);
                //给?传值,查询emp表中名字第二位是A的人
                ps.setString(1,"_A%");
                //执行sql语句
                rs=ps.executeQuery();
                //处理查询结果集
                while(rs.next()){
                    System.out.println(rs.getString("ename"));
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                //释放资源
                JDBCUtils.close(conn,ps,rs);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    11.悲观锁(行级锁)

    悲观锁(Pessimistic Lock),指在应用程序中显示地为数据资源加锁。悲观锁,顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。悲观锁假定当前事务操纵数据资源时,肯定还有其他事务同时访问该数据资源,为了避免当前事务的操作受到干扰,先锁定资源。

    尽管悲观锁能够防止丢失更新和不可重复读这类并发问题,但是它会影响并发性能,因此应该很谨慎地使用悲观锁。

    select * from emp where sal>2500 for update;
    
    • 1

    不加悲观锁

    时间取款事务转账事务
    T1开始事务
    T2开始事务
    T3select * from ACCOUNT WHERE ID=1;查询结果显示存款余额为1000元
    T4select * from ACCOUNT WHERE ID=1;查询结果显示存款余额为1000元
    T5取出100元,存款余额修改为900元
    T6转入100元,1000+100=1100元,修改存款余额为1100元
    T7提交事务
    T8提交事务

    加悲观锁

    时间取款事务转账事务
    T1开始事务
    T2开始事务
    T3select * from ACCOUNT WHERE ID=1 for update;查询结果显示存款余额为1000元,这条记录被锁定
    T4select * from ACCOUNT WHERE ID=1 for update;执行该语句时,事务停下来等待取款事务解除对该记录的锁定
    T5取出100元,存款余额修改为900元
    T6提交事务
    T7事务恢复运行,查询余额为900元,这条记录被锁定
    T8转入100元,900+100=1000元,修改存款余额为1000元,
    T9提交事务
  • 相关阅读:
    QT--气泡框的实现
    Linux入门之多线程|线程的同步|生产消费模型
    Cadence OrCAD Capture “网络名”相同,但是未连接或连接错误的解放方案之nodename的用法
    浅谈 kafka
    EasyEdge 智能边缘控制台通过sdk发布应用
    Charles 抓包工具教程(五) Charles 如何进行Mock
    win10电脑不小心卸载的软件怎么恢复
    AI虚拟人物 数字人直播,不用出镜,不用露脸的直播方式(附教程 软件)
    二叉树常见算法
    4d动感影院座椅5d动感影院体验馆大型7D互动影院
  • 原文地址:https://blog.csdn.net/m0_66689823/article/details/126149737