【横扫JDBC】JDBC 系列(老杜B站视频笔记整理)

目录

001-编写程序模拟JDBC本质

1、什么是JDBC?

2、JDBC相关的类库在哪里?

3、JDBC本质上是一堆什么呢?

4、JDBC开发之前的准备工作?

002-JDBC编程六步

1、JDBC编程六步

2、URL统―资源定位符

3、注册驱动的第二种方式:类加载注册

4、读取属性配置文件

5、处理查询结果集(遍历结果集)

003-模拟用户登录功能的实现

1、实现功能

2、用户登录login方法

3、初始化用户界面

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

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

004-SQL注入

1、解决SQL注入问题?

2、测试结果:

3、解决SQL注入的关键是什么?

4、对比一下Statement和PreparedStatement?

5、什么情况下必须使用Statement呢?

6、整体代码:

005-PrepareStatement使用场景

1、PrepareStatement适合传值

2、Statement适合字符串的拼接

006-CUD操作

1、增删改

2、整体代码:

007-JDBC事务机制

1、JDBC中的事务是自动提交的,什么是自动提交?

2、以下程序先来验证一下JDBC的事务是否是自动提交机制!

3、三行重点代码

008-DBUtil工具类

009-模糊查询

010-悲观锁和乐观锁机制


*

001-编写程序模拟JDBC本质

1、什么是JDBC?

Java DataBase Connectivity
在java语言中编写sql语句,对mysql数据库中的数据进行CRUD操作。

2、JDBC相关的类库在哪里?

java.sql.*;

3、JDBC本质上是一堆什么呢?

java.sql. * ;
这个包下都是JDBC的接口,SUN公司制定的!
JDBC是体现"接口作用"的非常经典的例子。
JDBC降低了耦合度,提高了扩展力。
对于java程序员来说,不需要关心数据库是哪个品牌。只要面向JDBC接口编程就行!

JDBC整个程序的结构当中有三波人????

  1. 第一波:SUN公司,负责制定JDBC接口。这些接口已经写好了,在java.sql.;

  2. 第二波:java.sql.*下面的所有接口都要有实现类,这些实现类是数据库厂家编写的。

    我们连接的是mysql数据库,mysql数据库厂家的实现类在哪里呢?
    mysql-connector-java-5.1.23-bin.jar
    jar包中很多.class字节码文件,这是mysql数据库厂家写的接口实现!

    注意:如果连接的是oracle数据库,你需要从网上下载oracle的jar包。

    mysql-connector-java-5.1.23-bin.jar
    这个jar包有一个专业的术语,大家记住就行:mysql的驱动。
    如果是oracle的jar,被称为oracle的驱动。

  3. 第三波:我们java程序员,面向JDBC接口写代码就行!

4、JDBC开发之前的准备工作?

  • mysql的驱动jar包,需要配置到classpath当中吗?
    mysql-connector-java-5.1.23-bin.jar里是字节码,是class文件。

  • Java虚拟机的类加载器会去加载class文件,类加载器怎么能够找到这些class文件呢?
    classpath没有配置的情况下,默认从当前路径下加载class。
    classpath如果配置死了,例如:classpath-D:\abc,则表示固定只从d:\abc目录下找class
    classpath=. ;D: \course\04-JDBC\resources \MySql Connector Java 5.1.23\mysql-connector-java-5.1.23-bin.jar

  • “ . ”代表什么?
    当前路径。

  • 以上的classpath什么意思?

    ​ 类加载器从当前路径下加载class,如果当前路径下没找到,则去D:\course\04-JDBC\resources\MySql Connector Java 5.1.23\mysql-connector-java-5.1.23-bin.jar找class文件。

  • jar包需要解压吗?
    不需要解压,java虚拟机的类加载器有这个能力找到class文件。

//这就是我们面向JDBC接口调用方法连接数据库
//我们不需要关心底层是mysql还是oraole,还是DB2,还是sybase.
//我们都不需要关心,因为JDBC接口的出现让程序降低了耦合度!!!
//我们Java程序员写一套JDBC程序,可以连接你任何一个品牌的数据库。
public class JavaProgranmer{
	public static void main(string[] args)(
		//面向JDBC接口写代码
		//JDBC jdbc = now Oracle() ;
		JDBC jdbc = new MySQL() ;
		//连接数据库
		//调用方法的时候,面向JDBC接口调用,JDBC接口中有什么方法就调什么方法。
		jdbc.getConnection();
		//...以下几百行代码都是面向jdbo接口调用的,不需要修改!
		//...
        }
}

002-JDBC编程六步

1、JDBC编程六步

  1. 注册驱动
    (通知java程序我们即将要连接的是哪个品牌的数据库)
  2. 获取数据库连接
    (java进程和mysgl进程,两个进程之间的通道开启了)
    (java进程可能在北京,mysql进程在上海)
  3. 获取数据库操作对象
    这个对象很重要,用这个对象执行SQL的。
  4. 执行SQL语句
    执行CRUD操作
  5. 处理查询结果集
    如果第四步是select语句,才有这个第五步
  6. 释放资源
    关闭所有的资源(因为JDBC毕竟是进程之间的通信,占用很多资源的,需要关闭!)
import java.sql.DriverManager;
import java.sql.Driver;
import java.sql.SQLException;
	
public class JDBCTest01{
	public static void main(String[] args){
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        try{
            //1、注册驱动(通知java程序我们即将要连接的是哪个品牌的数据库)
            //com.mysql.jdbc.Driver是mysql数据库厂家写的,实现了java.sql.Driver接口。
            //如果是oracle数据库的话,类名就不一样了:oracle.jdbc.driver.oracleDriver
            Driver driver = new com.mysql.jdbc.Driver() ;//mysql的
            DriverManager.registerDriver(driver) ;
            //等同于DriverManager.registerDriver(new com.mysql.jdbc.Diver());
            
            // Draver driver = new oracle.jdbo.driver.OracleDriver();//oracle的
            
            //2、获取数据库连接
            String url = "jdbc:mysql://localhost:3306/bjpowernode";
            String user = "root";
            String password = "123456";
            conn = DriverManager.getConnection (url,user,password) ;
            //conn = 	DriverManager.getConnection("jdbc:mysq1://127.0.0.1:3306/bjpowernode","root","123456");
            //输出连接对象的内存地址
            // com.mysql.jdbo.JDBC4Connection02aaf7cc2
            // com.mysql.jdbc.JDBc4Connection类实现了java.sql.Connection接口。
            //实际上我们后续的开发不需要关心底层具体是哪个对象,因为面向接口编程。
            //System.out.println(conn);

            //3、获取数据库操作对象
            stmt = conn.createstatement() ;
            //System.out.println(stmt);
            
            //通过一个连接对象connection是可以创建多个statement对象的
            //statement stmt2 = conn.createstatement();
            //System.out.println(stmt2);
            
            //4、执行SQL语句
            // insert delete update
            /*		insert
            string insertsql="insert into dept(deptno,dname,loc)values(50,'销售部','北京')";
            // Statement接口中的executeUpdate方法专门来执行DML语句的。
            //该方法的返回值表示:影响了数据库表中的总记录条数!
            int count = stmt.executeUpdate (insertSql);
            System.out.println(count); // 1
            */
            /*		updata
            string updatesql="update dept set dname='人事部',loc='天津' where deptno=50";
            int count = stmt.executeUpdate (updateSql) ;
            System.out.println(count); // 1
            */
            //       delete
            String deleteSql = "delete from dept where deptno = 50";
            int count = stmt.executeUpdate (deleteSql);
            System.out.println(count);
            
            //		执行查询语句
            //JFBC当中的sql语句不需要以";"结尾
            String sql = "select empno,ename,sal from emp order by sal deso";
            // 执行查询语句是这个方法:executeQuery
            // Resultset就是查询结果集对象,查询的结果都在这个对象当中。
            rs = stmt.executeQuery (sql);
            //5、处理查询结果集
            //目前没什么好处理的,直接把结果集中的数据遍历输出吧。
            
      	    //调用ResultSet接口中相应的方法来遍历结果集
            boolean has=rs.next();//光标向前移动一位
            while(rs.next()){
                //条件成立表示光标指向的行有记录
                //取当前行的第1个值
                String empno=rs.getstring(1);//注意:getstring()这个方法是不管底层数据
                //取当前行的第2个值
                String ename = rs.getString(2);
                //取当前行的第3个信~
                String sal = rs.getstring(3);
                System.out.println (empno + "," + ename + "," + sal);
                //也可以用特定类型取出
                
                //根据查询结果的列名可以取吗?
                //以后这种方式是常用的,健壮
                int empno = rs.getInt("empno");
                String ename = rs.getString("ename");
                double sal = rs.getDouble("sal");
                System.out.println (empno + "," + ename + "," + (sal + 100));

            }
		}catch(SQLException e){
			e.printStackTrace();
        }finally{
             // 释放资源
             // 6、先关闭ResultSet,先释放statement,再释放connection
             //分别进行try..catch处理
             //放到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();
                }
            }
        }
    }
}

【横扫JDBC】JDBC 系列(老杜B站视频笔记整理)_第1张图片

2、URL统―资源定位符

​ 任何一个URL都包括:协议+IP地址+端口号port+资源名
​ http://192.168.100.2:8888/abc
协议:
​ 是一个提前规定好的数据传输格式。通信协议有很多:http、https.
​ 在传送数据之前,提前先商量好数据传送的格式。
​ 这样对方接收到数据之后,就会按照这个格式去解析,拿到有价值的数据。
IP地址:
​ 网络当中定位某台计算机的。
PORT端口号:
​ 定位这台计算机上某个服务的。
资源名:
​ 这个服务下的某个资源。

 /*

        jdbc:mysql://		这是java程序和mysql通信的协议。      
        localhost			这是本机IP地址,本机IP地址还可以写成:127.0.0.1    
        3306				mysql数据库端口号
        bjpowernode			mysql数据库的名称
        
        jdbc:mysq1://192.168.111.123:3306/abc
        
        如果是oracle数据库的话:
        	oracle: jdbc:thin:0localhost:1521:bjpowernode
        	oracle:jdbc:thin:0		  这是oracle和java的通信协议
            localhost				  这是本机IP地址。     
            1521					  oracle默认端口
            bjpowernode				  oracle中数据库的名字
        
        1ocalhost和127.0.0.1都是本机IP地址。死记硬背。
*/

3、注册驱动的第二种方式:类加载注册

package com.bjpowernode.jdbc;
/*
注册驱动的第二种方式:类加载注册

mysql的厂家写的类:
class com.mysql.jdbc.Dniver {
	static {
		try {
			java.sql.DriverManager.registerDriver(new Driver());
		} catch (SQLException E) {
			throw new RuntimeException("Can't register driver!");
		}
	}
}
*/
public class JDBCTest04 {
	public static void main(String[] args) {
		try {
			//1、注册驱动
            Class.forName("com.mysq1.jdbc.Driver");
            //2、获取连接
            conn = 	DriverManager.getConnection("jdbc:mysq1://127.0.0.1:3306/bjpowernode","root","123456");
			//3、获取数据库对象
            stmt = conn.createStatement();
            //4、执行SQL
            String sql = "select e.ename,d.dname from emp e join dept d on e.deptno = d.deptno";
            rs = stmt.executeQuery(sql);
            //5、处理查询结果集
            while(rs.next()){
                String ename = rs.getString("ename");
                String dname = rs.getString("dname");
                System.out.println(ename+","+dname);

            }
		} catch (ClassNotFoundException 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();
                }
            }
        }
    }
}

4、读取属性配置文件

思想:
将连接数据库的可变化的4条信息写到配置文件中。
以后想连接其他数据库的时候,可以直接修改配置文件,不用修改java程序。
四个信息是什么?
driver
url
user
password

src下新建一个Package名叫“resources”,新建一个fille叫"db.properties"

【横扫JDBC】JDBC 系列(老杜B站视频笔记整理)_第2张图片

#######mysql connectivity configuration###############
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/bjpowernode
user=root
password=123456

#######oracle connectivity configuration###############
driver=oracle.jdbc.driver.OracleDriver
url=jdbc:oracle:thin:@localhost:1521:bjpowernode
user=scott
password=tiger

【横扫JDBC】JDBC 系列(老杜B站视频笔记整理)_第3张图片

【横扫JDBC】JDBC 系列(老杜B站视频笔记整理)_第4张图片

//资源绑定器
ResourceBundle bundle = ResourceBundle.getBundle("resources/db");
//通过属性配置文件拿到信息
String driver = bundle.getString( key: "driver");
String url = bundle.getString( key: "url");
String user = bundle.getString( key:"user");
String password = bundle.getString( key: "password");

5、处理查询结果集(遍历结果集)

ResultSet 类对象 rs 中当中有一个next()方法,返回值为boolean类型,表示光标指向的行有数据,如果只是一行数据可以用 if 进行判断,如果是多行数据,可以用while循环。

			boolean flag1 = rs.next();
			System.out.println(flag1); // true
    //单行
	if(rs.next()){
        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);
    }

ResultSet 类对象 rs 中当中有getString()、geitInt()等方法。
getString()方法的特点是:不管数据库中的数据类型是什么,都以String的形式取出。int同理

        int empno = rs.getInt(1);
        String ename = rs.getString(2);
        double sal = rs.getDouble(3);
        System.out.println(empno + "," + ename + "," + (sal + 100));

getString()内的参数情况:
**第一种:**该行记录的第几个数据,注意 JDBC中所有下标从1开始。不是从0开始

    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);
    }

**第二种:**以列的名字获取,注意:列名称不是表中的列名称,是查询结果集的列名称

        String empno = rs.getString("a"); 
        String ename = rs.getString("ename");
        String sal = rs.getString("sal");
        System.out.println(empno + "," + ename + "," + sal);

003-模拟用户登录功能的实现

1、实现功能

​ 1、需求:
​ 模拟用户登录功能的实现。

​ 2、业务描述:
​ 程序运行的时候,提供一个输入的入口,可以让用户输入用户名和密码,
​ 用户输入用户名和密码之后,提交信息,java程序收集到用户信息。
​ Java程序连接数据库验证用户名和密码是否合法。
​ 合法:显示登录成功
​ 不合法:显示登录失败

​ 3、数据的准备:
​ 在实际开发中,表的设计会使用专业的建模工具,我们这里安装一个建模工具:PowerDesigner
​ 使用PD工具来进行数据库表的设计。(参见user-login.sql脚本)

package com.bjpowernode.jdbc;

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

public class JDBCTest06 {
    public static void main(String[] args) {
        // 初始化一个界面
        Map userLoginInfo = initUI();
        // 验证用户名和密码
        boolean loginSuccess = login(userLoginInfo);
        // 最后输出结果
        System.out.println(loginSuccess ? "登录成功" : "登录失败");
    }

2、用户登录login方法

	private static boolean login(Map 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/bjpowernode", "root", "333");
            // 3、获取数据库操作对象
            stmt = conn.createStatement();
            // 4、执行sql
            String sql = "select * from t_user where loginName = '"+loginName+"' and loginPwd = '"+loginPwd+"'";     //fdsa' or '1'='1
            // 以上正好完成了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;
    }

}

3、初始化用户界面

用户输入的用户名和密码等登录信息

	private static Map initUI() {
        Scanner s = new Scanner(System.in);

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

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

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

        return userLoginInfo;
    }

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

​ 用户名:fdsa
​ 密码:fdsa’ or ‘1’='1
​ 登录成功
​ 这种现象被称为SQL注入(安全隐患)。(黑客经常使用)

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

​ 用户输入的信息中含有sql语句的关键字,并且这些关键字参与sql语句的编译过程,导致sql语句的原意被扭曲,进而达到sql注入。

004-SQL注入

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。

            // 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();

6、整体代码:

package com.bjpowernode.jdbc;

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

public class JDBCTest07 {
    public static void main(String[] args) {
        // 初始化一个界面
        Map userLoginInfo = initUI();
        // 验证用户名和密码
        boolean loginSuccess = login(userLoginInfo);
        // 最后输出结果
        System.out.println(loginSuccess ? "登录成功" : "登录失败");
    }

    /**
     * 用户登录
     * @param userLoginInfo 用户登录信息
     * @return false表示失败,true表示成功
     */
    private static boolean login(Map 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/bjpowernode", "root", "333");
            
 --------------------------------------重点-------------------------------------------------     
            // 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 initUI() {
        Scanner s = new Scanner(System.in);

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

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

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

        return userLoginInfo;
    }
}

005-PrepareStatement使用场景

1、PrepareStatement适合传值

			// 获取预编译的数据库操作对象
            String sql = "select ename from emp order by ename ?";
            ps = conn.prepareStatement(sql);
            ps.setString(1, keyWords);

如果是上述的这种方式就不行了,传进去的是字符串,是值,带单引号!!!

String sql = "select ename from emp order by ename 'esc' ";

这种形式,会报错

2、Statement适合字符串的拼接

 String sql = "select ename from emp order by ename " + keyWords;

只能采取上述这种方式,会导致sql注入,但是可以采用不让用户输入的方式实现该功能

package com.bjpowernode.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;
        Statement stmt = null;
        ResultSet rs = null;
        try {
            // 注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            // 获取连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode", "root","333");
            // 获取数据库操作对象
            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();
                }
            }
        }

    }
}

006-CUD操作

增加(Create)、查询(Retrieve)、更新(Update)、删除(Delete)。简称CRUD

1、增删改

		   //增
            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);

2、整体代码:

package com.bjpowernode.jdbc;

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

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/bjpowernode", "root","333");
            // 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();
                }
            }
        }
    }
}

007-JDBC事务机制

1、JDBC中的事务是自动提交的,什么是自动提交?

  • 只要执行任意一条DML语句,则自动提交一次。这是JDBC默认的事务行为。
  • 但是在实际的业务当中,通常都是N条DML语句共同联合才能完成的,必须
  • 保证他们这些DML语句在同一个事务中同时成功或者同时失败。

2、以下程序先来验证一下JDBC的事务是否是自动提交机制!

  • 测试结果:JDBC中只要执行任意一条DML语句,就提交一次。

3、三行重点代码

	 conn.setAutoCommit(false);
      conn.commit();
      conn.rollback();
package com.bjpowernode.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;
 *
 */
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/bjpowernode", "root","333");
            // 将自动提交机制修改为手动提交
            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();
                }
            }
        }
    }
}

008-DBUtil工具类

JDBC工具类,简化JDBC编程。

package com.bjpowernode.jdbc.utils;

import java.sql.*;

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/bjpowernode", "root", "333");
    }

    /**
     * 关闭资源
     * @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();
            }
        }
    }

}

009-模糊查询

 		   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 '_?%'";

完整代码

package com.bjpowernode.jdbc;

import com.bjpowernode.jdbc.utils.DBUtil;

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

public class JDBCTest12 {
    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 (Exception e) {
            e.printStackTrace();
        } finally{
            // 释放资源
            DBUtil.close(conn, ps, rs);
        }

    }
}

010-悲观锁和乐观锁机制

悲观锁:又称为行级锁 for update事务必须排队执行。数据锁住了,不允许井发。(行级锁:select后面添加for update)
乐观锁:支持并发,事务也不需要排队,只不过需要一个版本号。

事务1–>读取到版本号1.1
事务2—>读取到版本号1.1

其中事务1先修改了,修改之后看了版本号是1.1,于是提交修改的数据,将版本号修改为1.2
其中事务2后修改的,修改之后准备提交的时候,发现版本号是1.2,和它最初读的版本号不一致。回滚。

你可能感兴趣的:(数据库,Java,硬核,数据库,mysql,oracle,jdbc,java)