> 文档中心 > JDBC学习笔记

JDBC学习笔记

文章目录

  • 1.前言
  • 2. JDBC编程六步骤
    • 2.1示例
  • 3.进阶写法
  • 4.处理查询结果集
  • 5.关于SQL注入
    • 5.1代码展示
    • 5.2关于Statement和PreparedStatement的区别
  • 6.JDBC事务自动提交机制
    • 6.1代码演示
    • 6.2手动修改事务
  • 7.JDBC工具类的封装
    • 7.1封装代码
    • 7.2使用JDBC实现模糊查询
  • 8.悲观锁和乐观锁的概念
    • 8.1悲观锁(行级锁)
    • 8.2乐观锁

1.前言

(1)JDBC概念:Java DataBase Connectivity(Java语言连接数据库)

(2)本质:是SUN公司制定的一套接口

2. JDBC编程六步骤

(1)注册驱动(告诉Java程序,即将要连接的是哪个品牌的数据库)

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

(3)获取数据库操作对象(专门执行sql语句的对象)

(4)执行SQL语句(DQL,DML)

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

(6)释放资源(使用完资源以后一定要关闭资源。Java和数据库属于进程间的通信,使用完一定要关闭)

2.1示例

import java.sql.*;public class JDBCTest {    public static void main(String[] args) { Connection conn = null; Statement stmt = null; try{     //1.注册驱动     Driver driver = com.mysql.jdbc.Driver();     DriverManager.registerDriver(driver);     //2.获取连接     /**      * url:统一资源定位符      * 比如一个网址就是url      * URL由协议、IP、Port、资源名字 组成      *      * http://182.61.200.7:80/index.html      *      http://:通信协议      *      182.61.200.7:80:是IP地址      *      80:是端口号      *      index.html:是服务器上的某个资源名      *      * jdbc:mysql://127.0.0.1:3306/bjpowernode      *      jdbc:mysql://:是协议      *      127.0.0.1是本机IP地址      *      3306是MySQL服务端口号      *      bjpowernode是需要访问的资源      *      * 所谓通信协议就是组织好的数据传输格式      *      * */     String url = "jdbc:mysql://127.0.0.1:3306/bjpowernode";     String user = "root";     String password = "1017";     conn = DriverManager.getConnection(url,user,password);     //3.获取数据库操作对象,Statement专门执行sql语句。     stmt = conn.createStatement();     //4.执行sql语句     String sql = "insert into dept(deptno,dname,loc) values(50,'人事部','北京')";     /**      * 专门执行DML语句(update,delete,insert)      * 返回值是影响数据库中的数据条数(比如插入三条就返回3)      * */     int count = stmt.executeUpdate(sql);     System.out.println(count == 1?"保存成功":"保存失败");     //5.处理查询结果集 }catch (SQLException e){     e.printStackTrace(); }finally {     //6.释放资源     /**      * 为了保证资源一定释放,在finally中释放资源      * 释放资源要保证从小到大依次释放。比如statement是由connection对象产生的,我们要先关闭statement再关闭connection对象      * 释放资源要写在try...catch块里      * */     try {  if (stmt!=null)      stmt.close();     }catch (SQLException e){  e.printStackTrace();     }     try {  if (conn!=null)      conn.close();     }catch (SQLException e){  e.printStackTrace();     } }    }}

3.进阶写法

import java.sql.Connection;import java.sql.DriverManager;import java.sql.SQLException;import java.sql.Statement;import java.util.ResourceBundle;public class JDBCTest02 {    /**     * 注册驱动最常用的一种方法     *     * 在实际开发中,不建议把连接数据库的信息写死到程序里面去     * */    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.注册驱动     /**      * 这种方式常用是因为参数是字符串,字符串可以写入xxx.properties文件中      * */     Class.forName(driver);//使用反射机制将驱动加载到内存当中。     //2.获取连接     conn = DriverManager.getConnection(url,user,password);     //3.获取数据库操作对象,Statement专门执行sql语句。     stmt = conn.createStatement();     //4.执行sql语句     String sql = "insert into dept(deptno,dname,loc) values(50,'人事部','北京')";     int count = stmt.executeUpdate(sql);     System.out.println(count == 1?"保存成功":"保存失败");     //5.处理查询结果集 }catch (SQLException e){     e.printStackTrace(); }catch (ClassNotFoundException e){     e.printStackTrace(); }finally {     //6.释放资源     try {  if (stmt!=null)      stmt.close();     }catch (SQLException e){  e.printStackTrace();     }     try {  if (conn!=null)      conn.close();     }catch (SQLException e){  e.printStackTrace();     } }    }}

4.处理查询结果集

import java.sql.*;public class JDBCTest03 {    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/bjpowernode",      "root","1017");     //3.获取数据库操作对象,Statement专门执行sql语句。     stmt = conn.createStatement();     //4.执行sql语句     String sql = "select empno,ename,sal from emp";     //int count = stmt.executeUpdate(sql);<---专门处理DML语句的方法     rs = stmt.executeQuery(sql);     //5.处理查询结果集     boolean flag1 = rs.next();  /**   * getString()方法的特点是不管数据库中的数据类型是什么,都是以String的形式取出.   * 并且其中要求的参数是列的下标(getString(1))或者是列名(getString("ename"),如果列名被重写必须要使用被重写的列名)   *   * 除了可以以String类型取出之外,还可以以特定的类型取出   * 比如:int empno = rs.getInt("empno")   *   * 不过要注意的是,jdbc中的下标从1开始不是从0开始   * */     while (rs.next()){  String empno = rs.getString(1);  String ename = rs.getString(2);  String sal = rs.getNString(3);  System.out.println(empno+","+ename+","+sal);     } }catch (SQLException e){     e.printStackTrace(); }catch (ClassNotFoundException e){     e.printStackTrace(); }finally {     //6.释放资源     try {  if (rs!=null)      rs.close();     }catch (SQLException e){  e.printStackTrace();     }     try {  if (stmt!=null)      stmt.close();     }catch (SQLException e){  e.printStackTrace();     }     try {  if (conn!=null)      conn.close();     }catch (SQLException e){  e.printStackTrace();     } }    }}

5.关于SQL注入

5.1代码展示

import java.sql.*;import java.util.HashMap;import java.util.Map;import java.util.Scanner;/** * 如何解决SQL注入的问题: * * 使用java.sql.PreparedStatement * 该类继承了statement,其属于预编译的数据库操作对象。 * 其原理是:预先对SQL语句的框架进行编译,然后再给sql语句传值。 * * */public class JDBCTest05 {    public static void main(String[] args) { Map<String, String> userLoginInfo = init(); boolean loginSuccess = login(userLoginInfo); System.out.println(loginSuccess?"登录成功":"登录失败");    }    private static boolean login(Map<String,String> userLoginInfo) { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; String userName = userLoginInfo.get("userName"); String passWord = userLoginInfo.get("passWord"); boolean loginSuccess = false; try {     Class.forName("com.mysql.jdbc.Driver");     conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode",      "root","1017");     String sql = "select * from t_user where loginName=? and loginPwd=?";     ps = conn.prepareStatement(sql);     ps.setString(1,userName);     ps.setString(2,passWord);     rs = ps.executeQuery();     if (rs.next()){  loginSuccess = true;     } } catch (ClassNotFoundException e) {     e.printStackTrace(); } catch (SQLException e) {     e.printStackTrace(); }finally {     if (rs!=null){  try {      rs.close();  }catch (Exception e){      e.printStackTrace();  }     }     if (ps!=null){  try {      ps.close();  }catch (Exception e){      e.printStackTrace();  }     }     if (conn!=null){  try {      conn.close();  }catch (Exception e){      e.printStackTrace();  }     } } return loginSuccess;    }    private static Map<String, String> init() { Scanner in = new Scanner(System.in); System.out.println("用户名:"); String userName = in.nextLine(); System.out.println("密码:"); String passWord = in.nextLine(); Map<String, String> userLoginInfo = new HashMap<>(); userLoginInfo.put("userName",userName); userLoginInfo.put("passWord",passWord); return userLoginInfo;    }}

5.2关于Statement和PreparedStatement的区别

Statement存在注入问题;而PreparedStatement存在注入问题。

Statement是编译一次执行一次;而PreparedStatement是编译一次执行N次。

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

大多数情况下使用PreparedStatement,只有在业务要求SQL注入的时候才会使用Statement。

6.JDBC事务自动提交机制

6.1代码演示

import java.sql.Connection;import java.sql.DriverManager;import java.sql.PreparedStatement;/** * JDBC事务机制: *  1.JDBC中的事务是自动提交,什么是自动提交? *      只要执行任意一条DML语句,则自动提交一次。这是JDBC默认的事务行为。 *      但是在实际的业务当中,通常都是N条DML语句共同联合才能完成的,必须 *      保证他们这些DML语句在同一个事务当中同时成功或者同时失败。 * */public class JDBCTest06 {    public static void main(String[] args) { Connection conn = null; PreparedStatement ps = null; try {     Class.forName("com.mysql.jdbc.Driver");     conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode",      "root","1017");     String sql = "update dept set dname = ? where deptno=?";     ps = conn.prepareStatement(sql);//操作1     ps.setString(1,"X部门");     ps.setInt(2,30);     int count = ps.executeUpdate();     System.out.println(count);//操作2     ps.setString(1,"Y部门");     ps.setInt(2,30);     int count2 = ps.executeUpdate();     System.out.println(count2);      } catch (Exception e) {     e.printStackTrace(); }finally{     if (ps!=null){  try {      ps.close();  }catch (Exception e){      e.printStackTrace();  }     }     if (conn!=null){  try {      conn.close();  }catch (Exception e){      e.printStackTrace();  }     } }    }}

我们可以对以上代码进行Debug,不难发现,当操作1代码执行完毕以后我们的数据库就已经发生了改变。

6.2手动修改事务

//将Connection对象禁用自动提交机制。conn.setAutoCommit(false);//在操作完成以后我们使用commit()方法手动提交。conn.commit();//为了保证数据安全,最后还要在catch块中手动调用事务回滚 catch (Exception e) {     conn.rollback();     e.printStackTrace(); }

7.JDBC工具类的封装

7.1封装代码

public class DBUtil {    private DBUtil(){}    static { try {     Class.forName("com.mysql.jdbc.Driver"); } catch (ClassNotFoundException e) {     e.printStackTrace(); }    }    public static Connection getConnection() throws SQLException { return DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode",  "root","1017");    }    public static void close(Connection conn, Statement ps, ResultSet rs){ if (rs != null){     try {  rs.close();     }catch (Exception e){  e.printStackTrace();     } } if (ps != null){     try {  ps.close();     }catch (Exception e){  e.printStackTrace();     } } if (conn != null){     try {  conn.close();     }catch (Exception e){  e.printStackTrace();     } }    }}

7.2使用JDBC实现模糊查询

public class DBUtil {    private DBUtil(){}    static { try {     Class.forName("com.mysql.jdbc.Driver"); } catch (ClassNotFoundException e) {     e.printStackTrace(); }    }    public static Connection getConnection() throws SQLException { return DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode",  "root","1017");    }    public static void close(Connection conn, Statement ps, ResultSet rs){ if (rs != null){     try {  rs.close();     }catch (Exception e){  e.printStackTrace();     } } if (ps != null){     try {  ps.close();     }catch (Exception e){  e.printStackTrace();     } } if (conn != null){     try {  conn.close();     }catch (Exception e){  e.printStackTrace();     } }    }}

8.悲观锁和乐观锁的概念

8.1悲观锁(行级锁)

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

在sql语句末尾加上for update表示悲观锁,在事务结束之前,其他线程无法对悲观锁锁上的数据进行操作,所有事务都必须排队执行,不允许并发。

8.2乐观锁

支持并发,事务也不需要排队,只不过需要一个版本号

比如说一条记录具有版本号1.1。有两个事务分别是:A、B其中事务A先进行修改,完成以后版本号变为1.2,事务B是后进行修改的,修改以后准备提交发现版本号变为1.2,和他最初读的版本号不一样,所以它选择回滚。