• 【横扫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文件。

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

    002-JDBC编程六步

    1、JDBC编程六步

    1. 注册驱动
      (通知java程序我们即将要连接的是哪个品牌的数据库)
    2. 获取数据库连接
      (java进程和mysgl进程,两个进程之间的通道开启了)
      (java进程可能在北京,mysql进程在上海)
    3. 获取数据库操作对象
      这个对象很重要,用这个对象执行SQL的。
    4. 执行SQL语句
      执行CRUD操作
    5. 处理查询结果集
      如果第四步是select语句,才有这个第五步
    6. 释放资源
      关闭所有的资源(因为JDBC毕竟是进程之间的通信,占用很多资源的,需要关闭!)
    1. import java.sql.DriverManager;
    2. import java.sql.Driver;
    3. import java.sql.SQLException;
    4. public class JDBCTest01{
    5. public static void main(String[] args){
    6. Connection conn = null;
    7. Statement stmt = null;
    8. ResultSet rs = null;
    9. try{
    10. //1、注册驱动(通知java程序我们即将要连接的是哪个品牌的数据库)
    11. //com.mysql.jdbc.Driver是mysql数据库厂家写的,实现了java.sql.Driver接口。
    12. //如果是oracle数据库的话,类名就不一样了:oracle.jdbc.driver.oracleDriver
    13. Driver driver = new com.mysql.jdbc.Driver() ;//mysql的
    14. DriverManager.registerDriver(driver) ;
    15. //等同于DriverManager.registerDriver(new com.mysql.jdbc.Diver());
    16. // Draver driver = new oracle.jdbo.driver.OracleDriver();//oracle的
    17. //2、获取数据库连接
    18. String url = "jdbc:mysql://localhost:3306/bjpowernode";
    19. String user = "root";
    20. String password = "123456";
    21. conn = DriverManager.getConnection (url,user,password) ;
    22. //conn = DriverManager.getConnection("jdbc:mysq1://127.0.0.1:3306/bjpowernode","root","123456");
    23. //输出连接对象的内存地址
    24. // com.mysql.jdbo.JDBC4Connection02aaf7cc2
    25. // com.mysql.jdbc.JDBc4Connection类实现了java.sql.Connection接口。
    26. //实际上我们后续的开发不需要关心底层具体是哪个对象,因为面向接口编程。
    27. //System.out.println(conn);
    28. //3、获取数据库操作对象
    29. stmt = conn.createstatement() ;
    30. //System.out.println(stmt);
    31. //通过一个连接对象connection是可以创建多个statement对象的
    32. //statement stmt2 = conn.createstatement();
    33. //System.out.println(stmt2);
    34. //4、执行SQL语句
    35. // insert delete update
    36. /* insert
    37. string insertsql="insert into dept(deptno,dname,loc)values(50,'销售部','北京')";
    38. // Statement接口中的executeUpdate方法专门来执行DML语句的。
    39. //该方法的返回值表示:影响了数据库表中的总记录条数!
    40. int count = stmt.executeUpdate (insertSql);
    41. System.out.println(count); // 1
    42. */
    43. /* updata
    44. string updatesql="update dept set dname='人事部',loc='天津' where deptno=50";
    45. int count = stmt.executeUpdate (updateSql) ;
    46. System.out.println(count); // 1
    47. */
    48. // delete
    49. String deleteSql = "delete from dept where deptno = 50";
    50. int count = stmt.executeUpdate (deleteSql);
    51. System.out.println(count);
    52. // 执行查询语句
    53. //JFBC当中的sql语句不需要以";"结尾
    54. String sql = "select empno,ename,sal from emp order by sal deso";
    55. // 执行查询语句是这个方法:executeQuery
    56. // Resultset就是查询结果集对象,查询的结果都在这个对象当中。
    57. rs = stmt.executeQuery (sql);
    58. //5、处理查询结果集
    59. //目前没什么好处理的,直接把结果集中的数据遍历输出吧。
    60. //调用ResultSet接口中相应的方法来遍历结果集
    61. boolean has=rs.next();//光标向前移动一位
    62. while(rs.next()){
    63. //条件成立表示光标指向的行有记录
    64. //取当前行的第1个值
    65. String empno=rs.getstring(1);//注意:getstring()这个方法是不管底层数据
    66. //取当前行的第2个值
    67. String ename = rs.getString(2);
    68. //取当前行的第3个信~
    69. String sal = rs.getstring(3);
    70. System.out.println (empno + "," + ename + "," + sal);
    71. //也可以用特定类型取出
    72. //根据查询结果的列名可以取吗?
    73. //以后这种方式是常用的,健壮
    74. int empno = rs.getInt("empno");
    75. String ename = rs.getString("ename");
    76. double sal = rs.getDouble("sal");
    77. System.out.println (empno + "," + ename + "," + (sal + 100));
    78. }
    79. }catch(SQLException e){
    80. e.printStackTrace();
    81. }finally{
    82. // 释放资源
    83. // 6、先关闭ResultSet,先释放statement,再释放connection
    84. //分别进行try..catch处理
    85. //放到finally中关闭
    86. if (rs != null) {
    87. try {
    88. rs.close();
    89. } catch (SQLException e) {
    90. e.printStackTrace();
    91. }
    92. }
    93. if (stmt != null) {
    94. try {
    95. stmt.close();
    96. } catch (SQLException e) {
    97. e.printStackTrace();
    98. }
    99. }
    100. if (conn != null) {
    101. try {
    102. conn.close();
    103. } catch (SQLException e) {
    104. e.printStackTrace();
    105. }
    106. }
    107. }
    108. }
    109. }

    2、URL统―资源定位符

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

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

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

    1. package com.bjpowernode.jdbc;
    2. /*
    3. 注册驱动的第二种方式:类加载注册
    4. mysql的厂家写的类:
    5. class com.mysql.jdbc.Dniver {
    6. static {
    7. try {
    8. java.sql.DriverManager.registerDriver(new Driver());
    9. } catch (SQLException E) {
    10. throw new RuntimeException("Can't register driver!");
    11. }
    12. }
    13. }
    14. */
    15. public class JDBCTest04 {
    16. public static void main(String[] args) {
    17. try {
    18. //1、注册驱动
    19. Class.forName("com.mysq1.jdbc.Driver");
    20. //2、获取连接
    21. conn = DriverManager.getConnection("jdbc:mysq1://127.0.0.1:3306/bjpowernode","root","123456");
    22. //3、获取数据库对象
    23. stmt = conn.createStatement();
    24. //4、执行SQL
    25. String sql = "select e.ename,d.dname from emp e join dept d on e.deptno = d.deptno";
    26. rs = stmt.executeQuery(sql);
    27. //5、处理查询结果集
    28. while(rs.next()){
    29. String ename = rs.getString("ename");
    30. String dname = rs.getString("dname");
    31. System.out.println(ename+","+dname);
    32. }
    33. } catch (ClassNotFoundException e){
    34. e.printStackTrace();
    35. } finally {
    36. //6、释放资源
    37. if (rs != null) {
    38. try {
    39. rs.close();
    40. } catch (SQLException e) {
    41. e.printStackTrace();
    42. }
    43. }
    44. if (stmt != null) {
    45. try {
    46. stmt.close();
    47. } catch (SQLException e) {
    48. e.printStackTrace();
    49. }
    50. }
    51. if (conn != null) {
    52. try {
    53. conn.close();
    54. } catch (SQLException e) {
    55. e.printStackTrace();
    56. }
    57. }
    58. }
    59. }
    60. }

    4、读取属性配置文件

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

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

    1. #######mysql connectivity configuration###############
    2. driver=com.mysql.jdbc.Driver
    3. url=jdbc:mysql://localhost:3306/bjpowernode
    4. user=root
    5. password=123456
    6. #######oracle connectivity configuration###############
    7. driver=oracle.jdbc.driver.OracleDriver
    8. url=jdbc:oracle:thin:@localhost:1521:bjpowernode
    9. user=scott
    10. password=tiger

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

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

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

    1. boolean flag1 = rs.next();
    2. System.out.println(flag1); // true
    1. //单行
    2. if(rs.next()){
    3. String empno = rs.getString(1);
    4. String ename = rs.getString(2);
    5. String sal = rs.getString(3);
    6. System.out.println(empno + "," + ename + "," + sal);
    7. }
    8. //多行
    9. while(rs.next()){
    10. String empno = rs.getString(1);
    11. String ename = rs.getString(2);
    12. String sal = rs.getString(3);
    13. System.out.println(empno + "," + ename + "," + sal);
    14. }

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

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

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

    1. if(flag1){
    2. // 光标指向的行有数据
    3. // 取数据
    4. // getString()方法的特点是:不管数据库中的数据类型是什么,都以String的形式取出。
    5. String empno = rs.getString(1); // JDBC中所有下标从1开始。不是从0开始。
    6. String ename = rs.getString(2);
    7. String sal = rs.getString(3);
    8. System.out.println(empno + "," + ename + "," + sal);
    9. }

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

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

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

    1、实现功能

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

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

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

    1. package com.bjpowernode.jdbc;
    2. import java.sql.*;
    3. import java.util.HashMap;
    4. import java.util.Map;
    5. import java.util.Scanner;
    6. public class JDBCTest06 {
    7. public static void main(String[] args) {
    8. // 初始化一个界面
    9. Map<String,String> userLoginInfo = initUI();
    10. // 验证用户名和密码
    11. boolean loginSuccess = login(userLoginInfo);
    12. // 最后输出结果
    13. System.out.println(loginSuccess ? "登录成功" : "登录失败");
    14. }

    2、用户登录login方法

    1. private static boolean login(Map<String, String> userLoginInfo) {
    2. // 打标记的意识
    3. boolean loginSuccess = false;
    4. // 单独定义变量
    5. String loginName = userLoginInfo.get("loginName");
    6. String loginPwd = userLoginInfo.get("loginPwd");
    7. // JDBC代码
    8. Connection conn = null;
    9. Statement stmt = null;
    10. ResultSet rs = null;
    11. try {
    12. // 1、注册驱动
    13. Class.forName("com.mysql.jdbc.Driver");
    14. // 2、获取连接
    15. conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode", "root", "333");
    16. // 3、获取数据库操作对象
    17. stmt = conn.createStatement();
    18. // 4、执行sql
    19. String sql = "select * from t_user where loginName = '"+loginName+"' and loginPwd = '"+loginPwd+"'"; //fdsa' or '1'='1
    20. // 以上正好完成了sql语句的拼接,以下代码的含义是,发送sql语句给DBMS,DBMS进行sql编译。
    21. // 正好将用户提供的“非法信息”编译进去。导致了原sql语句的含义被扭曲了。
    22. rs = stmt.executeQuery(sql);
    23. // 5、处理结果集
    24. if(rs.next()){
    25. // 登录成功
    26. loginSuccess = true;
    27. }
    28. } catch (Exception e) {
    29. e.printStackTrace();
    30. } finally {
    31. // 6、释放资源
    32. if (rs != null) {
    33. try {
    34. rs.close();
    35. } catch (SQLException e) {
    36. e.printStackTrace();
    37. }
    38. }
    39. if (stmt != null) {
    40. try {
    41. stmt.close();
    42. } catch (SQLException e) {
    43. e.printStackTrace();
    44. }
    45. }
    46. if (conn != null) {
    47. try {
    48. conn.close();
    49. } catch (SQLException e) {
    50. e.printStackTrace();
    51. }
    52. }
    53. }
    54. return loginSuccess;
    55. }
    56. }

    3、初始化用户界面

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

    1. private static Map<String, String> initUI() {
    2. Scanner s = new Scanner(System.in);
    3. System.out.print("用户名:");
    4. String loginName = s.nextLine();
    5. System.out.print("密码:");
    6. String loginPwd = s.nextLine();
    7. Map<String,String> userLoginInfo = new HashMap<>();
    8. userLoginInfo.put("loginName", loginName);
    9. userLoginInfo.put("loginPwd", loginPwd);
    10. return userLoginInfo;
    11. }

    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。

    1. // 3、获取预编译的数据库操作对象
    2. // SQL语句的框子。其中一个?,表示一个占位符,一个?将来接收一个“值”
    3. //注意:占位符不能使用单引号括起来。
    4. String sql = "select * from t_user where loginName = ? and loginPwd = ?";
    5. // 程序执行到此处,会发送sql语句框子给DBMS,然后DBMS进行sql语句的预先编译。
    6. ps = conn.prepareStatement(sql);
    7. // 给占位符?传值(第1个问号下标是1,第2个问号下标是2,JDBC中所有下标从1开始。)
    8. ps.setString(1, loginName);
    9. ps.setString(2, loginPwd);
    10. // 4、执行sql
    11. rs = ps.executeQuery();

    6、整体代码:

    1. package com.bjpowernode.jdbc;
    2. import java.sql.*;
    3. import java.util.HashMap;
    4. import java.util.Map;
    5. import java.util.Scanner;
    6. public class JDBCTest07 {
    7. public static void main(String[] args) {
    8. // 初始化一个界面
    9. Map<String,String> userLoginInfo = initUI();
    10. // 验证用户名和密码
    11. boolean loginSuccess = login(userLoginInfo);
    12. // 最后输出结果
    13. System.out.println(loginSuccess ? "登录成功" : "登录失败");
    14. }
    15. /**
    16. * 用户登录
    17. * @param userLoginInfo 用户登录信息
    18. * @return false表示失败,true表示成功
    19. */
    20. private static boolean login(Map<String, String> userLoginInfo) {
    21. // 打标记的意识
    22. boolean loginSuccess = false;
    23. // 单独定义变量
    24. String loginName = userLoginInfo.get("loginName");
    25. String loginPwd = userLoginInfo.get("loginPwd");
    26. // JDBC代码
    27. Connection conn = null;
    28. PreparedStatement ps = null; // 这里使用PreparedStatement(预编译的数据库操作对象)
    29. ResultSet rs = null;
    30. try {
    31. // 1、注册驱动
    32. Class.forName("com.mysql.jdbc.Driver");
    33. // 2、获取连接
    34. conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode", "root", "333");
    35. --------------------------------------重点-------------------------------------------------
    36. // 3、获取预编译的数据库操作对象
    37. // SQL语句的框子。其中一个?,表示一个占位符,一个?将来接收一个“值”
    38. //注意:占位符不能使用单引号括起来。
    39. String sql = "select * from t_user where loginName = ? and loginPwd = ?";
    40. // 程序执行到此处,会发送sql语句框子给DBMS,然后DBMS进行sql语句的预先编译。
    41. ps = conn.prepareStatement(sql);
    42. // 给占位符?传值(第1个问号下标是1,第2个问号下标是2,JDBC中所有下标从1开始。)
    43. ps.setString(1, loginName);
    44. ps.setString(2, loginPwd);
    45. // 4、执行sql
    46. rs = ps.executeQuery();
    47. --------------------------------------重点-------------------------------------------------
    48. // 5、处理结果集
    49. if(rs.next()){
    50. // 登录成功
    51. loginSuccess = true;
    52. }
    53. } catch (Exception e) {
    54. e.printStackTrace();
    55. } finally {
    56. // 6、释放资源
    57. if (rs != null) {
    58. try {
    59. rs.close();
    60. } catch (SQLException e) {
    61. e.printStackTrace();
    62. }
    63. }
    64. if (ps != null) {
    65. try {
    66. ps.close();
    67. } catch (SQLException e) {
    68. e.printStackTrace();
    69. }
    70. }
    71. if (conn != null) {
    72. try {
    73. conn.close();
    74. } catch (SQLException e) {
    75. e.printStackTrace();
    76. }
    77. }
    78. }
    79. return loginSuccess;
    80. }
    81. /**
    82. * 初始化用户界面
    83. * @return 用户输入的用户名和密码等登录信息
    84. */
    85. private static Map<String, String> initUI() {
    86. Scanner s = new Scanner(System.in);
    87. System.out.print("用户名:");
    88. String loginName = s.nextLine();
    89. System.out.print("密码:");
    90. String loginPwd = s.nextLine();
    91. Map<String,String> userLoginInfo = new HashMap<>();
    92. userLoginInfo.put("loginName", loginName);
    93. userLoginInfo.put("loginPwd", loginPwd);
    94. return userLoginInfo;
    95. }
    96. }

    005-PrepareStatement使用场景

    1、PrepareStatement适合传值

    1. // 获取预编译的数据库操作对象
    2. String sql = "select ename from emp order by ename ?";
    3. ps = conn.prepareStatement(sql);
    4. 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注入,但是可以采用不让用户输入的方式实现该功能

    1. package com.bjpowernode.jdbc;
    2. import java.sql.*;
    3. import java.util.Scanner;
    4. public class JDBCTest08 {
    5. public static void main(String[] args) {
    6. // 用户在控制台输入desc就是降序,输入asc就是升序
    7. Scanner s = new Scanner(System.in);
    8. System.out.println("输入desc或asc,desc表示降序,asc表示升序");
    9. System.out.print("请输入:");
    10. String keyWords = s.nextLine();
    11. // 执行SQL
    12. Connection conn = null;
    13. Statement stmt = null;
    14. ResultSet rs = null;
    15. try {
    16. // 注册驱动
    17. Class.forName("com.mysql.jdbc.Driver");
    18. // 获取连接
    19. conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode", "root","333");
    20. // 获取数据库操作对象
    21. stmt = conn.createStatement();
    22. // 执行SQL
    23. String sql = "select ename from emp order by ename " + keyWords;
    24. rs = stmt.executeQuery(sql);
    25. // 遍历结果集
    26. while(rs.next()){
    27. System.out.println(rs.getString("ename"));
    28. }
    29. } catch (Exception e) {
    30. e.printStackTrace();
    31. } finally {
    32. if (rs != null) {
    33. try {
    34. rs.close();
    35. } catch (SQLException e) {
    36. e.printStackTrace();
    37. }
    38. }
    39. if (stmt != null) {
    40. try {
    41. stmt.close();
    42. } catch (SQLException e) {
    43. e.printStackTrace();
    44. }
    45. }
    46. if (conn != null) {
    47. try {
    48. conn.close();
    49. } catch (SQLException e) {
    50. e.printStackTrace();
    51. }
    52. }
    53. }
    54. }
    55. }

    006-CUD操作

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

    1、增删改

    1. //增
    2. String sql = "insert into dept(deptno,dname,loc) values(?,?,?)";
    3. ps = conn.prepareStatement(sql);
    4. ps.setInt(1, 60);
    5. ps.setString(2, "销售部");
    6. ps.setString(3, "上海");
    7. //改
    8. String sql = "update dept set dname = ?, loc = ? where deptno = ?";
    9. ps = conn.prepareStatement(sql);
    10. ps.setString(1, "研发一部");
    11. ps.setString(2, "北京");
    12. ps.setInt(3, 60);
    13. //删
    14. String sql = "delete from dept where deptno = ?";
    15. ps = conn.prepareStatement(sql);
    16. ps.setInt(1, 60);

    2、整体代码:

    1. package com.bjpowernode.jdbc;
    2. import java.sql.Connection;
    3. import java.sql.DriverManager;
    4. import java.sql.PreparedStatement;
    5. import java.sql.SQLException;
    6. public class JDBCTest09 {
    7. public static void main(String[] args) {
    8. Connection conn = null;
    9. PreparedStatement ps = null;
    10. try {
    11. // 1、注册驱动
    12. Class.forName("com.mysql.jdbc.Driver");
    13. // 2、获取连接
    14. conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode", "root","333");
    15. // 3、获取预编译的数据库操作对象
    16. //增
    17. String sql = "insert into dept(deptno,dname,loc) values(?,?,?)";
    18. ps = conn.prepareStatement(sql);
    19. ps.setInt(1, 60);
    20. ps.setString(2, "销售部");
    21. ps.setString(3, "上海");
    22. //改
    23. String sql = "update dept set dname = ?, loc = ? where deptno = ?";
    24. ps = conn.prepareStatement(sql);
    25. ps.setString(1, "研发一部");
    26. ps.setString(2, "北京");
    27. ps.setInt(3, 60);
    28. //删
    29. String sql = "delete from dept where deptno = ?";
    30. ps = conn.prepareStatement(sql);
    31. ps.setInt(1, 60);
    32. // 4、执行SQL
    33. int count = ps.executeUpdate();
    34. System.out.println(count);
    35. } catch (Exception e) {
    36. e.printStackTrace();
    37. } finally {
    38. // 6、释放资源
    39. if (ps != null) {
    40. try {
    41. ps.close();
    42. } catch (SQLException e) {
    43. e.printStackTrace();
    44. }
    45. }
    46. if (conn != null) {
    47. try {
    48. conn.close();
    49. } catch (SQLException e) {
    50. e.printStackTrace();
    51. }
    52. }
    53. }
    54. }
    55. }

    007-JDBC事务机制

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

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

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

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

    3、三行重点代码

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

    008-DBUtil工具类

    JDBC工具类,简化JDBC编程。

    1. package com.bjpowernode.jdbc.utils;
    2. import java.sql.*;
    3. public class DBUtil {
    4. /**
    5. * 工具类中的构造方法都是私有的。
    6. * 因为工具类当中的方法都是静态的,不需要new对象,直接采用类名调用。
    7. */
    8. private DBUtil() {
    9. }
    10. // 静态代码块在类加载时执行,并且只执行一次。
    11. static {
    12. try {
    13. Class.forName("com.mysql.jdbc.Driver");
    14. } catch (ClassNotFoundException e) {
    15. e.printStackTrace();
    16. }
    17. }
    18. /**
    19. * 获取数据库连接对象
    20. *
    21. * @return 连接对象
    22. * @throws SQLException
    23. */
    24. public static Connection getConnection() throws SQLException {
    25. return DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode", "root", "333");
    26. }
    27. /**
    28. * 关闭资源
    29. * @param conn 连接对象
    30. * @param ps 数据库操作对象
    31. * @param rs 结果集
    32. */
    33. public static void close(Connection conn, Statement ps, ResultSet rs){
    34. if(rs != null){
    35. try {
    36. rs.close();
    37. } catch (SQLException e) {
    38. e.printStackTrace();
    39. }
    40. }
    41. if(ps != null){
    42. try {
    43. ps.close();
    44. } catch (SQLException e) {
    45. e.printStackTrace();
    46. }
    47. }
    48. if(conn != null){
    49. try {
    50. conn.close();
    51. } catch (SQLException e) {
    52. e.printStackTrace();
    53. }
    54. }
    55. }
    56. }

    009-模糊查询

    1. String sql = "select ename from emp where ename like ?";
    2. ps = conn.prepareStatement(sql);
    3. ps.setString(1, "_A%");

    以下为错误写法:

    String sql = "select ename from emp where ename like '_?%'";
    

    完整代码

    1. package com.bjpowernode.jdbc;
    2. import com.bjpowernode.jdbc.utils.DBUtil;
    3. import java.sql.Connection;
    4. import java.sql.PreparedStatement;
    5. import java.sql.ResultSet;
    6. public class JDBCTest12 {
    7. public static void main(String[] args) {
    8. Connection conn = null;
    9. PreparedStatement ps = null;
    10. ResultSet rs = null;
    11. try {
    12. // 获取连接
    13. conn = DBUtil.getConnection();
    14. // 获取预编译的数据库操作对象
    15. // 错误的写法
    16. /*
    17. String sql = "select ename from emp where ename like '_?%'";
    18. ps = conn.prepareStatement(sql);
    19. ps.setString(1, "A");
    20. */
    21. String sql = "select ename from emp where ename like ?";
    22. ps = conn.prepareStatement(sql);
    23. ps.setString(1, "_A%");
    24. rs = ps.executeQuery();
    25. while(rs.next()){
    26. System.out.println(rs.getString("ename"));
    27. }
    28. } catch (Exception e) {
    29. e.printStackTrace();
    30. } finally{
    31. // 释放资源
    32. DBUtil.close(conn, ps, rs);
    33. }
    34. }
    35. }

    010-悲观锁和乐观锁机制

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

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

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

  • 相关阅读:
    vue3中的Props
    tf.nn
    Kafka之Producer网络传输
    信用卡客户风险分析与评价
    7.DesignForSilkscreen\SilkscreenCheckAll
    postgres查看是否锁表并释放
    学习C语言的好处:
    DuDuTalk:4G语音工牌,如何实现家庭上门维修服务过程的智能化管理?
    蓝牙技术|ESL蓝牙电子价签将成为智能市场新的增长点
    说一说MVCC多版本并发控制器?
  • 原文地址:https://blog.csdn.net/m0_61163395/article/details/125445395