JDBC笔记(连接数据库,用Java操作数据库进行增删改查,注入攻击,预编译)

JDBC笔记

2019/8/30
学习内容:连接数据库,用Java操作数据库进行增删改查,注入攻击,预编译

一、JDBC是什么

  1. JDBC(Java DataBase Connectivity)即Java数据库连接,是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,使数据库开发人员能够编写数据库应用程序。
  2. JDBC 可做三件事:与数据库建立连接、发送 操作数据库的语句并处理结果。

二、怎么使用JDBC让Java连接MySQL数据库

注意,首先要保证MySQL数据库服务为开启状态(系统服务中的MySQL57服务是打开的)

1. 加载MySQL的驱动类 com.mysql.jdbc.Driver

(这里需要导入第三方jar包,可在mvnrepository仓库查找并下载)

使用Class.forName(String className)来加载导入的Driver类,参数放Driver的包名,需要捕获ClassNotFoundException异常

Class.forName("com.mysql.jdbc.Driver");
2. 获取数据库连接对象

使用DriverManager.getConnection(url, user, password)方法

参数是三个字符串,对应连接数据库需要的四要素 host port user password
其中,url的格式是
jdbc:mysql://主机地址:端口号/数据库名?字符集
(字符集也可以不写,但有可能出现乱码)

获取的连接对象用Connection来接,具体操作为

String url = "jdbc:mysql://localhost:3306/ishopn?characterEncoding=utf8";
String user = "root";
String password = "1234";
Connection conn = DriverManager.getConnection(url, user, password);

这里又需要捕获SQLException异常,可以直接在所有代码最外层套一个try catch捕获Exception异常

另外,在我们自己的测试中,Java代码运行完虚拟机就关闭了,但在真正公司中的数据库一般不会随意关闭,连接对象conn会一直开着,需要在程序最后,调用 conn.close() 来关闭。

还有一个问题,代码执行过程中如果出现问题,close()方法可能执行不到,所以最好的方法是将其放在finally语句里,把连接对象conn的声明和赋初始值放在try catch外面

连接MySQL的步骤整体优化后是这样的:

Connection conn = null;
  try {
   Class.forName("com.mysql.jdbc.Driver");
   String url = "jdbc:mysql://localhost:3306/ishopn?characterEncoding=utf8";
   String user = "root";
   String password = "1234";
   conn = DriverManager.getConnection(url, user, password);
   
   // ......实现数据库的操作的代码......
   
  } catch (Exception e) {
   e.printStackTrace();
  } finally {
   try {
    conn.close();
   } catch (SQLException e) {
    e.printStackTrace();
   }
  }

然后就可以开始对数据库进行操作了

3. 实现数据库的操作

(1)首先需要获取执行对象,用conn.createStatement()

Statement state = conn.createStatement();

(2)然后可以开始编写需要执行的sql语句的字符串,如添加一条数据

String insert_sql = "insert into commoditytype(ct_id,ct_name) values (7,'电脑配件')";

(3)执行sql语句,用state.executeUpdate(String sql),该方法如果执行成功,返回值为数据库中受到影响的行数,如果执行失败则返回0;参数里面放的就是需要执行的sql语句

int s = state.executeUpdate(insert_sql);

对数据库的增删改都是通过同样的方法来实现

(4)关于数据的查询,用到的是state.executeQuery(String sql)方法,返回值为ResultSet类,表示数据库查询的结果集。

调用next()方法使指针移动到有下一行,如果有数据返回true,没有数据返回false,因此可以在 while 循环中使用它来迭代结果集。

可以根据列的索引来获取字段值,调用getXXX(int fieldIndex)方法,其中要注意索引是从1开始的

   String query_sql = "select * from customer";
   ResultSet rs = state.executeQuery(query_sql);
   while (rs.next()) {
    // 迭代数据集中的每一行,根据列的索引获取数据
    String cu_id = rs.getString(1); // 索引从1开始
    String cu_name = rs.getString(2);
    String cu_phone = rs.getString(3);
    int cu_gender = rs.getInt(4);
    String cu_address = rs.getString(5);
    System.out.println(cu_id + "|" + cu_name + "|" + cu_phone + "|" + cu_gender + "|" + cu_address);
   }

也可以根据字段名来获取字段值,调用getXXX(String columnName)方法。

while (rs.next()) {
    String cu_id = rs.getString("cu_id");
    String cu_name = rs.getString("cu_name");
    String cu_phone = rs.getString("cu_phone");
    int cu_gender = rs.getInt("cu_gender");
    String cu_address = rs.getString("cu_address");
    System.out.println(cu_id + "|" + cu_name + "|" + cu_phone + "|" + cu_gender + "|" + cu_address);
}

三、模拟登录功能(PreparedStatement类)

下面是使用JDBC实现登录功能(连接mysql部分省略),用户从控制台输入姓名name和手机号phone,通过查询语句匹配数据库中是否有该用户的数据

   // 使用用户输入的name和phone拼接sql的查询语句
   String sql = "select count(*) from customer where cu_name='" + name + "' and cu_phone='" + phone + "'";
   // 执行sql语句
   ResultSet rs = state.executeQuery(sql);
   // 使结果集的指针移动到下一行,因为一般只会有一条数据,不需要遍历
   rs.next();
   // 获取结果的第一列数值
   int cnt = rs.getInt(1);
    // 如果大于0代表是匹配到有值的,登录成功
   if(cnt>0) {
    System.out.println("登录成功");
   }else {
    System.out.println("登录失败,用户名或密码错误");    
   }

但是用这种拼接的方法,sql语句可以受用户影响,如果用户输入用户名为"
’ or 1=1 # ",密码随意输入一个,拼接出来的sql语句就变成了

select count(*) from customer where cu_name=’ ’ or 1=1 #’ and cu_phone=’ …(后面都被#注释掉了)

由于1=1恒成立,#号后面密码判断也不再需要,这样也能查询出结果(而且是所有数据),用户能登录成功,这种操作被称为 “注入攻击”

但现在基本上没有网站会真的有这种空子可钻,因为JDBC提供了一个预编译的方法conn.prepareStatement(String sql),获取到的是预执行对象PreparedStatement类

与之对应的sql语句可以这样写,需要获取用户输入的字段用问号代替

String sql = "select count(*) from customer where cu_name=? and cu_phone=?";
// 获取执行对象
PreparedStatement ps = conn.prepareStatement(sql);
// 把用户输入的name和phone传入ps,这里要注意,顺序要与填进问号的顺序一致
ps.setString(1, name);
ps.setString(2, phone);
// 执行sql语句,返回结果集
ResultSet rs = ps.executeQuery();

与之前直接用Statement有点不同,PreparedStatement是先在获取执行对象时就要传入sql语句,而Statement是在执行时才用传入sql语句

使用PreparedStatement类对数据库的增删改也是同样的过程。

你可能感兴趣的:(数据库)