JavaWeb——(5)JDBC

目录

一、JDBC概述

1.1JDBC介绍

1.2JDBC简单的使用

二、JUnit

三、JDBC常用的类和接口

3.1 java.sql.Drivermanager类

3.2 java.sql.Connection接口

3.3 java.sql.Statement接口

3.4 java.sql.ResultSet接口

3.4.1封装结果集

3.4.2可移动游标的方法

3.5资源的释放

四、JDBC实现增删改查操作

五、用户登录DEMO

六、SQL注入问题

6.1 SQL注入问题简介

6.2 PreparedStatement对象


一、JDBC概述

1.1JDBC介绍

JDBC指的是Java数据库连接,全称为Java Database Connectivity,

是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口提供了诸如查询和更新数据库中数据的方法

数据库驱动就是对JDBC的实现,不同的数据库有不同的数据库驱动,也对应着不同的实现,

JDBC与数据库驱动为接口和实现的关系,

JDBC规范(四个核心对象):

  • DriverManager:用于注册驱动
  • Connection:表示与数据库创建的连接
  • Statement:操作数据库sql语句的对象
  • ResultSet:结果集或一张虚拟表

其关系如下图所示,

1.2JDBC简单的使用

JDBC规范一般在JDK的java.sql.*javax.sql.*中,里面定义了访问数据库功能的接口,

二者的区别是前者为java的核心类库,后者是扩展类库,

而真正实现这些接口的是不同数据库厂商提供的驱动jar文件,

我们以IDEA为例,将驱动jar文件导入到java项目中,并通过JDBC访问数据库中的数据,

首先我们创建一个新的项目,然后新建一个lib文件夹,将jar文件拖入到文件夹中,

然后依次点击菜单File->Project Structure,然后选中左边的Modules,点击右边的小加号,选择第一个JARS,添加我们lib路径下的jar文件,

然后jar文件就可以使用了,接下来我们测试一个简单的代码,看看访问数据库的效果,

首先我们在数据库中添加一些数据,这里我们就是用之前创建的mydb1数据库user表的数据,

然后使用代码访问这个表中的数据,一共分为六步走:

  1. 注册驱动
  2. 创建连接
  3. 得到执行sql语句的statement对象
  4. 执行sql语句,并返回结果集resultset
  5. 处理结果
  6. 关闭资源

以下是代码实现,

import java.sql.*;

public class jdbcTest {
    public static void main(String[] args) throws SQLException {
        //注册驱动
        DriverManager.registerDriver(new com.mysql.jdbc.Driver());

        //获取连接connection
        //第一个参数为连接数据库的地址,第二个为用户名,第三个为密码
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb1","root","3837");

        //得到执行sql语句的对象statement
        Statement statement = connection.createStatement();

        //执行sql语句,并返回结果resultset
        ResultSet resultSet = statement.executeQuery("select * from user");

        //处理结果,next()方法得到结果集下一行,有数据则返回true,否则返回false
        while(resultSet.next()){
            System.out.print(resultSet.getObject(1)+"\t");//数据库的下标从1开始,输出第一列数据
            System.out.print(resultSet.getObject(2)+"\t");//输出第二列数据
            System.out.print(resultSet.getObject(3)+"\t");//输出第三列数据
            System.out.println();
        }
        
        //关闭资源
        resultSet.close();
        statement.close();
        connection.close();
    }
}

控制台输出如下:

二、JUnit

JUnit是一个Java语言的单元测试框架,可以测试不同的单元,

测试方法要求不能有返回值,也不能有参数

我们首先需要导入JUnit库,我是直接在代码中输入@测试名字,这里我用的是@Test,然后alt+enter进行自动补全JUnit4库,

package test;

import org.junit.Assert;
import org.junit.Test;

public class TestCalc {
    @Test
    public void test1(){
        Calc c=new Calc();
        //利用断言测试结果是否正确
        Assert.assertEquals(8,c.add(5,3));
    }

    @Test
    public void test2(){
        Calc c=new Calc();
        //利用断言测试结果是否正确
        Assert.assertEquals(3,c.div(10,3));
    }
}

运行结果:

三、JDBC常用的类和接口

3.1 java.sql.Drivermanager类

java.sql.Drivermanager类是用于创建数据库的连接,常用的接口包括注册驱动和建立连接,

  • 注册驱动
    • DriverManager.registerDriver(new com.mysql.jdbc.Driver());//注册给定驱动程序DriverManager,这种方法不建议使用
      • 这种方法驱动会被注册2次
      • 强烈依赖数据库的驱动jar
    • 解决办法:使用反射机制加载驱动类,Class.forName("com.mysql.jdbc.Driver");
  • 与数据库建立连接
    • static Connection getConnection(String url, String user, String password)//试图建立到给定数据库URL的连接
    • 例:getConnection("jdbc:mysql://localhost:3306/mydb1", "root", "3837");
      • URL:SUN公司与数据库厂商之间的一种协议
        • 协议:子协议://localhost:端口号/数据库名称
        • mysql写法:jdbc:mysql://localhost:3306/mydb1(这里的localhost:3306可以省略,不写默认为本机连接)
        • oracle写法:jdbc:oracle:thin:@localhost:1521:mydb1
      • user:数据库用户名
      • password:数据库密码
    • static Connection getConnection(String url, Properties info)
      • URL:连接数据库的地址
      • info:Properties类型的配置文件,使用键值对的方法存储用户名和密码信息
    • static Connection getConnection(String url)
      • URL:连接数据库的地址,地址中包括了用户名和密码信息
      • 例如:jdbc:mysql://localhost:3306/mydb1?user=root&password=3837

我们用单元测试的方法测试一下这三种建立连接访问数据库的数据,

import org.junit.Test;

import java.sql.*;
import java.util.Properties;

public class jdbcTest2 {
    @Test
    public void test1() throws Exception {
        System.out.println("----------Test1----------");
        //加载驱动
        Class.forName("com.mysql.jdbc.Driver");

        //获取连接connection
        //第一个参数为连接数据库的地址,第二个为用户名,第三个为密码
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb1","root","3837");

        //得到执行sql语句的对象statement
        Statement statement = connection.createStatement();

        //执行sql语句,并返回结果resultset
        ResultSet resultSet = statement.executeQuery("select * from user");

        //处理结果,next()方法得到结果集下一行,有数据则返回true,否则返回false
        while(resultSet.next()){
            System.out.print(resultSet.getObject(1)+"\t");//数据库的下标从1开始,输出第一列数据
            System.out.print(resultSet.getObject(2)+"\t");//输出第二列数据
            System.out.print(resultSet.getObject(3)+"\t");//输出第三列数据
            System.out.println();
        }

        //关闭资源
        resultSet.close();
        statement.close();
        connection.close();
    }

    @Test
    public void test2() throws Exception{
        System.out.println("----------Test2----------");
        //加载驱动
        Class.forName("com.mysql.jdbc.Driver");

        //获取连接connection
        //第一个参数为连接数据库的地址,第二个为配置信息,包括账户名和密码
        Properties info =new Properties();//创建配置文件
        info.setProperty("user","root");//添加用户名键值对
        info.setProperty("password","3837");//添加密码键值对
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb1",info);

        //得到执行sql语句的对象statement
        Statement statement = connection.createStatement();

        //执行sql语句,并返回结果resultset
        ResultSet resultSet = statement.executeQuery("select * from user");

        //处理结果,next()方法得到结果集下一行,有数据则返回true,否则返回false
        while(resultSet.next()){
            System.out.print(resultSet.getObject(1)+"\t");//数据库的下标从1开始,输出第一列数据
            System.out.print(resultSet.getObject(2)+"\t");//输出第二列数据
            System.out.print(resultSet.getObject(3)+"\t");//输出第三列数据
            System.out.println();
        }

        //关闭资源
        resultSet.close();
        statement.close();
        connection.close();
    }

    @Test
    public void test3() throws Exception{
        System.out.println("----------Test3----------");
        //加载驱动
        Class.forName("com.mysql.jdbc.Driver");

        //获取连接connection
        //参数为连接数据库的地址,地址中包括了用户名和密码信息
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb1?user=root&password=3837");

        //得到执行sql语句的对象statement
        Statement statement = connection.createStatement();

        //执行sql语句,并返回结果resultset
        ResultSet resultSet = statement.executeQuery("select * from user");

        //处理结果,next()方法得到结果集下一行,有数据则返回true,否则返回false
        while(resultSet.next()){
            System.out.print(resultSet.getObject(1)+"\t");//数据库的下标从1开始,输出第一列数据
            System.out.print(resultSet.getObject(2)+"\t");//输出第二列数据
            System.out.print(resultSet.getObject(3)+"\t");//输出第三列数据
            System.out.println();
        }

        //关闭资源
        resultSet.close();
        statement.close();
        connection.close();
    }
}

输出结果:

3.2 java.sql.Connection接口

其作用是与数据库取得连接,接口的实现在数据库驱动中,

所有与数据库交互都是基于连接对象的,我们可以通过createStatement()方法创建sql语句对象,

Statement statement=connection.createStatement();//创建操作sql语句的对象

3.3 java.sql.Statement接口

其作用为执行静态SQL语句,并返回它所生成的结果集对象,

同样该接口的实现也在数据库驱动中,常用的对数据库操作的方法如下:

ResultSet executeQuery(String sql);//根据查询语句返回结果集,注意只能执行select语句
int executeUpdate(String sql);//根据执行的DM(insert、update、delete)语句,返回受影响的行数
boolean execute(String sql);//此方法可以执行任意sql语句。返回boolean值,表示是否返回ResultSet结果集。仅当执行select语句,且有返回结果时返回true, 其它语句都返回false

我们同样用单元测试一下数据库的增删改查操作,

import org.junit.Test;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

public class jdbcTestCRUD {
    //Create、Read、Update、Delete
    @Test
    public void testInsert() throws Exception{
        //加载驱动
        Class.forName("com.mysql.jdbc.Driver");

        //获取连接connection
        //第一个参数为连接数据库的地址,第二个为用户名,第三个为密码
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb1","root","3837");

        //得到执行sql语句的对象statement
        Statement statement = connection.createStatement();

        //执行sql语句,并返回结果resultset
        int line = statement.executeUpdate("insert into user values('tz','male','wuhan')");
        if(line>0){
            System.out.println("数据插入成功");
        }

        //关闭资源
        statement.close();
        connection.close();
    }

    @Test
    public void testUpdate() throws Exception{
        //加载驱动
        Class.forName("com.mysql.jdbc.Driver");

        //获取连接connection
        //第一个参数为连接数据库的地址,第二个为用户名,第三个为密码
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb1","root","3837");

        //得到执行sql语句的对象statement
        Statement statement = connection.createStatement();

        //执行sql语句,并返回结果resultset
        int line = statement.executeUpdate("update user set address='beijing' where name='tz'");
        if(line>0){
            System.out.println("数据修改成功");
        }

        //关闭资源
        statement.close();
        connection.close();
    }

    @Test
    public void testDelete() throws Exception{
        //加载驱动
        Class.forName("com.mysql.jdbc.Driver");

        //获取连接connection
        //第一个参数为连接数据库的地址,第二个为用户名,第三个为密码
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb1","root","3837");

        //得到执行sql语句的对象statement
        Statement statement = connection.createStatement();

        //执行sql语句,并返回结果resultset
        int line = statement.executeUpdate("delete from user where name='username4'");
        if(line>0){
            System.out.println("数据删除成功");
        }

        //关闭资源
        statement.close();
        connection.close();
    }

    @Test
    public void testSelect() throws Exception{
        //加载驱动
        Class.forName("com.mysql.jdbc.Driver");

        //获取连接connection
        //第一个参数为连接数据库的地址,第二个为用户名,第三个为密码
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb1","root","3837");

        //得到执行sql语句的对象statement
        Statement statement = connection.createStatement();

        //执行sql语句,并返回结果resultset
        ResultSet resultSet = statement.executeQuery("select * from user");

        //处理结果,next()方法得到结果集下一行,有数据则返回true,否则返回false
        while(resultSet.next()){
            System.out.print(resultSet.getObject(1)+"\t");//数据库的下标从1开始,输出第一列数据
            System.out.print(resultSet.getObject(2)+"\t");//输出第二列数据
            System.out.print(resultSet.getObject(3)+"\t");//输出第三列数据
            System.out.println();
        }

        //关闭资源
        resultSet.close();
        statement.close();
        connection.close();
    }
}

运行结果:

3.4 java.sql.ResultSet接口

ResultSet接口用于存放sql语句执行后返回的结果集,主要有封装结果集和提供可移动游标的方法

3.4.1封装结果集

结果集提供一个游标,默认游标是指向结果集第一行之前,

调用一次next(),游标就向下移动一行,并且通过提供的getObject()方法来获取结果集中的数据,

Object getObject(int columnIndex);//根据列号下标取值,索引从1开始
Object getObject(String ColumnName);//根据列名取值

有时候我们还需要将结果集中的数据显示在前端网页中,这时就需要将数据封装到JavaBean中,

JavaBean是一种特殊的Java类,主要有以下几个特点:

  • 提供一个默认的无参构造函数
  • 需要被序列化并且实现了Serializable接口
  • 可能有一系列可读写属性
  • 可能有一系列的getter或setter方法

获得结果集的数据时,需要注意数据库的类型和java数据类型的对应关系,

java数据类型 数据库数据类型
byte tinyint
short smallint
int int
long bigint
float float
double double
String char、varchar
Date date

获取各个数据类型的方法如下:

boolean next();//将光标从当前位置向下移动一行

int getInt(int colIndex);//以int形式获取ResultSet结果集当前行指定列号值
int getInt(String colLabel);//以int形式获取ResultSet结果集当前行指定列名值

float getFloat(int colIndex);//以float形式获取ResultSet结果集当前行指定列号值
float getFloat(String colLabel);//以float形式获取ResultSet结果集当前行指定列名值

String getString(int colIndex);//以String形式获取ResultSet结果集当前行指定列号值
String getString(String colLabel);//以String形式获取ResultSet结果集当前行指定列名值

Date getDate(int columnIndex);//以Date形式获取ResultSet结果集当前行指定列号值
Date getDate(String columnName);//以Date形式获取ResultSet结果集当前行指定列名值

void close();//关闭ResultSet对象

我们用查询语句测试一下,首先我们创建一个User的对象存储数据库中每条记录的信息,

package entity;

public class User {
    //保持java类的变量名和数据库的列名相同
    private String name;
    private String gender;
    private String address;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", gender='" + gender + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

然后连接数据库进行查询,将得到的记录存储在User对象中,最后输出, 

import entity.User;
import org.junit.Test;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

public class jdbcTestCRUD {
    //Create、Read、Update、Delete

    @Test
    public void testSelect() throws Exception{
        //加载驱动
        Class.forName("com.mysql.jdbc.Driver");

        //获取连接connection
        //第一个参数为连接数据库的地址,第二个为用户名,第三个为密码
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb1","root","3837");

        //得到执行sql语句的对象statement
        Statement statement = connection.createStatement();

        //执行sql语句,并返回结果resultset
        ResultSet resultSet = statement.executeQuery("select * from user");

        //处理结果,next()方法得到结果集下一行,有数据则返回true,否则返回false
        List<User> list=new ArrayList<User>();
        while(resultSet.next()){
            User u=new User();
            u.setName(resultSet.getString("name"));
            u.setGender(resultSet.getString("gender"));
            u.setAddress(resultSet.getString("address"));
            list.add(u);//将一条用户记录添加到集合中
        }

        //关闭资源
        resultSet.close();
        statement.close();
        connection.close();

        //打印集合中用户信息
        for (User u:list) {
            System.out.println(u.toString());
        }
    }
}

输出结果如下:

3.4.2可移动游标的方法

结果集有一系列移动游标的方法,

boolean next();//将光标从当前位置向下移一行
boolean previous();//将光标从当前位置向上移一行
boolean absolute(int row);//参数是需要移动到指定行的索引(从1开始)
void afterLast();//将光标移动到末尾,正好位于最后一行之后
void beforeFirst();//将光标移动到开头,正好位于第一行之前

3.5资源的释放

在连接数据库的时候,我们申请了很多的资源,

这些资源需要得到正确的释放,如果仅仅将释放代码放在最后,那么程序出现异常时提前终止,这个时候资源就没有得到正确的释放,

如果其他用户这个时候需要访问数据库,资源没有释放被占用则会影响其他用户的操作,我们应该使用finally语句对资源进行释放,

正确的资源释放代码如下:

import org.junit.Test;
import java.sql.*;

public class jdbcTestRelease {
    @Test
    public void TestRelease(){
        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;
        try {
            //加载驱动
            Class.forName("com.mysql.jdbc.Driver");

            //获取连接connection
            //第一个参数为连接数据库的地址,第二个为用户名,第三个为密码
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb1","root","3837");

            //得到执行sql语句的对象statement
            statement = connection.createStatement();

            //执行sql语句,并返回结果resultset
            resultSet = statement.executeQuery("select * from user");

            //处理结果,next()方法得到结果集下一行,有数据则返回true,否则返回false
            while(resultSet.next()){
                System.out.print(resultSet.getObject(1)+"\t");//数据库的下标从1开始,输出第一列数据
                System.out.print(resultSet.getObject(2)+"\t");//输出第二列数据
                System.out.print(resultSet.getObject(3)+"\t");//输出第三列数据
                System.out.println();
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            //关闭资源
            if(resultSet!=null){
                try {
                    resultSet.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                resultSet=null;
            }
            if(statement!=null) {
                try {
                    statement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                statement=null;
            }
            if(connection!=null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                connection=null;
            }
        }
    }
}

四、JDBC实现增删改查操作

前面我们基本都实现了一遍,现在我们将代码进行优化,重复的代码优化提取为方法,

import entity.User;
import org.junit.Test;
import java.sql.*;
import java.util.*;

public class jdbcTestCRUDpro {
    @Test
    public void testSelect(){
        Connection connection=null;
        Statement statement=null;
        ResultSet resultSet=null;

        try {
            connection=DBUtils.getConnection();//获取连接
            statement=connection.createStatement();

            resultSet=statement.executeQuery("select * from user");
            List<User> list=new ArrayList<>();
            while(resultSet.next()){
                User u=new User();
                u.setName(resultSet.getString("name"));
                u.setGender(resultSet.getString("gender"));
                u.setAddress(resultSet.getString("address"));
                list.add(u);
            }

            for (User u:list) {
                System.out.println(u);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            DBUtils.closeAll(resultSet,statement,connection);//释放资源
        }
    }

    @Test
    public void testInsert(){
        Connection connection=null;
        Statement statement=null;

        try {
            connection=DBUtils.getConnection();//获取连接
            statement=connection.createStatement();

            int line=statement.executeUpdate("insert into user values('tz','male','wuhan')");
            if(line>0){
                System.out.println("数据插入成功");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            DBUtils.closeAll(null,statement,connection);//释放资源
        }
    }

    @Test
    public void testDelete(){
        Connection connection=null;
        Statement statement=null;

        try {
            connection=DBUtils.getConnection();//获取连接
            statement=connection.createStatement();

            int line=statement.executeUpdate("delete from user where name='tz'");
            if(line>0){
                System.out.println("数据删除成功");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            DBUtils.closeAll(null,statement,connection);//释放资源
        }
    }

    @Test
    public void testUpdate(){
        Connection connection=null;
        Statement statement=null;

        try {
            connection=DBUtils.getConnection();//获取连接
            statement=connection.createStatement();

            int line=statement.executeUpdate("update user set address='beijing' where name='username1'");
            if(line>0){
                System.out.println("数据删除成功");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            DBUtils.closeAll(null,statement,connection);//释放资源
        }
    }
}

class DBUtils{
    private static String driverClass;
    private static String url;
    private static String username;
    private static String password;

    static {
        //静态代码块,加载类的时候就加载配置文件
        ResourceBundle rb=ResourceBundle.getBundle("DBinfo");//加载属性资源文件properties
        driverClass=rb.getString("driverClass");
        url=rb.getString("url");
        username=rb.getString("username");
        password=rb.getString("password");

        //加载驱动
        try {
            Class.forName(driverClass);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    //得到数据库的连接
    public static Connection getConnection() throws Exception{
        return DriverManager.getConnection(url,username,password);
    }

    //关闭资源
    public static void closeAll(ResultSet resultSet, Statement statement, Connection connection){
        if(resultSet!=null){
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            resultSet=null;
        }
        if(statement!=null) {
            try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            statement=null;
        }
        if(connection!=null) {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            connection=null;
        }
    }
}

五、用户登录DEMO

我们简单分析一下用户登录需要的几个类,

  • User类:用于定义用户对象,包括用户名和密码
  • Client类:用于实现客户端,并调用判断用户是否合法的方法,告诉用户登录成功与否
  • DoLogin类:用于判断用户是否合法,在数据库中查找用户输入的用户名和密码是否正确
  • DBUtils类:用于实现一些使用频率较高的数据库公用方法(比如连接数据库,资源的释放等)

下面是代码实现,

User用户类:

package entity;

public class User {
    //保持java类的变量名和数据库的列名相同
    private String name;
    private String gender;
    private String address;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", gender='" + gender + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

DBUtils数据库常用方法类: 

package LoginDemo;

import java.sql.*;
import java.util.ResourceBundle;

public class DBUtils {
    private static String driverClass;
    private static String url;
    private static String username ;
    private static String password;

    static {
        //静态代码块,加载类的时候就加载配置文件
        ResourceBundle rb=ResourceBundle.getBundle("DBinfo");//加载属性资源文件properties
        driverClass=rb.getString("driverClass");
        url=rb.getString("url");
        username=rb.getString("username");
        password=rb.getString("password");

        //加载驱动
        try {
            Class.forName(driverClass);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    //得到数据库的连接
    public static Connection getConnection() throws Exception{
        return DriverManager.getConnection(url,username,password);
    }

    //关闭资源
    public static void closeAll(ResultSet resultSet, Statement statement, Connection connection){
        if(resultSet!=null){
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            resultSet=null;
        }
        if(statement!=null) {
            try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            statement=null;
        }
        if(connection!=null) {
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            connection=null;
        }
    }
}

Client客户端类: 

package LoginDemo;

import java.util.Scanner;

public class Client {
    public static void main(String[] args) {
        Scanner scanner=new Scanner(System.in);
        System.out.println("请输入用户名:");
        String username=scanner.nextLine();
        System.out.println("请输入密码:");
        String password=scanner.nextLine();

        User u=DoLogin.findUser(username,password);//在数据库中查询用户数据
        if(u!=null){
            System.out.println("欢迎"+u.getUsername());
        }else{
            System.out.println("用户名或密码错误");
        }
    }
}

DoLogin登录判断类: 

package LoginDemo;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;

public class DoLogin {
    public static User findUser(String name,String pwd){
        Connection connection=null;
        Statement statement=null;
        ResultSet resultSet=null;
        User u=null;

        try {
            connection=DBUtils.getConnection();
            statement=connection.createStatement();
            resultSet=statement.executeQuery("select * from user where username='"+name+"' and password='"+pwd+"'");

            if(resultSet.next()){
                u=new User();
                u.setUsername(resultSet.getString("username"));
                u.setPassword(resultSet.getString("password"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            DBUtils.closeAll(resultSet,statement,connection);
        }
        return u;
    }
}

然后我们运行Client.java,结果如图:

六、SQL注入问题

6.1 SQL注入问题简介

在完成上面的登录Demo后,我们用这样一组数据库中没有的username和password进行登录,

结果发现同样可以登录,这就是SQL注入问题,这是一个安全问题,

简单来说,SQL注入问题就是利用SQL语句在拼接过程中,加入自己的代码使得SQL语句的执行结果和原本开发者的意图不一样

下面我们举个简单的例子,原来要执行的SQL语句如下:

String sql="select * from user where username='"+name+"' and password='"+pwd+"'";

如果使用上面的用户名:asd和密码:asd' or '1'='1,那么得到的SQL语句为:

select * from user where username='asd' and password='asd' or '1'='1'

这句话加上OR语句后条件一直成立,所以可以访问到数据库的所有用户名和密码,自然就成功登录了。

6.2 PreparedStatement对象

解决这个问题的办法就是使用PreparedStatement对象,该对象是Statement对象的子类,该对象有以下几个特点:

  • 性能更高
  • 会将SQL语句预先编译
  • SQL语句中的参数会发生变化,过滤掉用户输入的关键字

我们首先修改一下代码,用PreparedStatement对象替换Statement对象,

package LoginDemo;

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

public class DoLoginpro {
    public static User findUser(String name,String pwd){
        Connection connection=null;
        PreparedStatement statement=null;
        ResultSet resultSet=null;
        User u=null;

        try {
            connection=DBUtils.getConnection();
            String sql="select * from user where username=? and password=?";//用问号表示变量
            statement=connection.prepareStatement(sql);//对sql语句进行预编译
            statement.setString(1,name);//给第一个问号赋值
            statement.setString(2,pwd);//给第二个问号赋值
            System.out.println(statement);
            resultSet=statement.executeQuery();//执行sql语句返回结果集

            if(resultSet.next()){
                u=new User();
                u.setUsername(resultSet.getString("username"));
                u.setPassword(resultSet.getString("password"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            DBUtils.closeAll(resultSet,statement,connection);
        }
        return u;
    }
}

然后用同样的数据测试一下可不可以通过数据库的检测,同时我们把sql语句输出:

我们可以看到PreparedStatement对象解决SQL注入问题的方法是将用户输入的单引号添加了反斜杠做转义字符

比如用户输入的:asd' or '1'='1经过PreparedStatement对象处理变成了字符串:asd\' or \'1\'=\'1。这样就可以解决SQL安全注入问题了。

猜你喜欢

转载自blog.csdn.net/weixin_39478524/article/details/114587281