JDBC编程,SQL注入与连接池

JDBC概念

JDBC(Java Data Base Conection)是java中提供的一套标准的应用编程接口,用来连接Java编程语言和数据库

JDBC常用组件:

  • DriverManger:数据库驱动程序的管理类,匹配java程序到数据库的驱动的协议(mysql,sqlerver...),识别出某个子协议与数据库服务器进行通信
  • Driver:处理与数据库服务器通信的,主要是DriverManger管理的对象,指具体的连接驱动(MYSQL驱动)
  • Connection:接口形式,接触数据库的所有的方法,以及数据库对象的上下文
  • Statement:创建用于这个接口的对象将所有的SQL提交的数据库(SQL操作都是通过该对象)
  • ResultSet:使用SQL语句执行查询后,返回的结果都是保存在该对象中
  • SqlExeption:这个类是数据库应用程序中的任何错误

注意:JDBC是第三方提供的库,要使用JDBC必须先引入jdbc相关jar包(mysql-connection-jave)

idea上直接编写(ArrayList等)等可以直接使用,这是JDK提供的工具类

 

使用maven来构建程序,主要作用用来统一管理第三方依赖jar,包括jar下载,更新...等操作使用maven可以更加的方便

mybatis,spring,springMVC,springboot框架都需要通过maven来管理

JDCB的使用

通过maven引入依赖

        <!--mysql依赖-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.39</version>
        </dependency>

核心信息

  • 驱动包路径
  • url:  jdbc:子协议(mysql)://ip+端口/数据库名
  • 账号
  • 密码

JDBC编程流程

  • 1、引入驱动包路径 Class.fromName
  • 2、连接数据库 DriverManager.getConnection(url,name,passwd)获取Connection对象
  • 3、获取Statement对象,通过Connection对象来获取
  • 4、通过Statement执行SQL
  • 5、查询操作会获取ResultSet结果集对象
  • 6、关闭资源
public class JDBCDemo {
    public static void main(String[] args) {
        //加载MYSQL驱动 com.mysql.jdbc.Driver
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        try {
        //连接MYSQL数据库
        /**
         * 连接数据库的信息:
         * url:jdbc:mysql://localhost:3306/ssms
         * username:用户名
         * password:密码
         */
           //获取Connection
            Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/ssms", "root", "123456");

            //获取Statement对象
            Statement statement = connection.createStatement();


            String sql = "select * from user ";
            //查询操作使用executeQuery
            ResultSet resultSet = statement.executeQuery(sql);


            //处理result结果集
            /**
             * resultSet对象返回的是一个结果集,可以包含多个对象
             * 多个对象中,每调用next()方法处理结果集中的一行数据
             *
             */
            while (resultSet.next()) {
                Integer id = resultSet.getInt("id");
                resultSet.getI
                String account = resultSet.getString("account");
                String name = resultSet.getString("name");
                System.out.println("Id:"+id+",name:"+name);
            }

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


        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

SQL注入问题

通过输入的参数和SQL进行拼接的查询数据库,为了绕开正常SQL逻辑而添加一些额外的SQL信息来来达到访问数据库的目的,以登陆为例:

 public static  String login(String name ,String passwd){
        getConnetion();
        try {
            Statement statement = connection.createStatement();
            String sql = "select * from user where name = "+name+" and password = "+passwd;
            // select * from user where name = XXX and password =XXX
            //select * from user where name = XXX or 1=1  and password =XXX
            ResultSet resultSet = statement.executeQuery(sql);
            if (resultSet.next()) {
                return "success";
            } else {
                return "fail";
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        close();
        return "fail";
    }

正常登陆通过name(2555)和passwd(111111)能够正常登陆

但是现在name= "2555 or 1=1"或者"2555 # XXX"(#后面变成注释了) 和passwd(错误信息)是可以完成登陆的,当前拼接的SQL信息为select * from user where name = XXX or 1=1  and password =XXX,改变SQL意义

String name = "2555 or 1=1";

String passwd = "11111";

String login = login(name, passwd);

 

如何解决SQL注入问题

使用PreparedStatement来解决SQL注入的问题,

将sql和参数分别传给数据库服务端,会先进行预编译,会先进行SQL语法的检查,在语法检测成功后才进行执行,如果语法检查不通过则无法执行

 public static  String login1(String name ,String passwd){
        getConnetion();
        try {
            //使用prepareStatement来实现 ,其中SQL中参数用"?"占位符
            String sql = "select * from user where name = ? and password = ?";
            PreparedStatement statement = connection.prepareStatement(sql);
            //往SQL中占位符给定数据  setString第一参数指的是占位符的位置,第一个位置为1
            statement.setString(1,name);
            statement.setString(2,passwd);

            ResultSet resultSet = statement.executeQuery(sql);
            if (resultSet.next()) {
                return "success";
            } else {
                return "fail";
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        close();
        return "fail";
    }

PreparedStatement和Statement区别点

 

  • SQL注入问题(安全性问题)

Statement是将拼接的SQL在服务端直接执行,无法避免的SQL注入问题,PreparedStatement将SQL和参数分别传递给服务单,先进行预编译,可以检查SQL语法,可以避免SQL注入问题

  • 效率不同

PreparedStatement先编译,在讲编译结果拿来执行,Statement是在服务端需要进行编译执行,PreparedStatement效率高于Statement

事务

JDBC同样支持事务,在JDBC下和事务相关的额操作都封装在Connection提供的相应操作

            /**
             * jdbc操作事务在Connection实例下提供的方法来进行事务相关操作
             */
            connection.commit();//提交事务
            connection.rollback();//事务回滚
            connection.setSavepoint();//保存点
            Savepoint point1 = connection.setSavepoint("point1");
            connection.rollback(point1);//回滚到指定的保存点

连接池

在JDBC的操作过程中,每次进行SQL操作都需要先进行连接操作,SQL执行完毕后需要关闭连接,在数据库的并发量比较高的情况下,频繁的进行数据库的连接和关闭是比较消耗系统性能。为了解决对资源的频繁的连接关闭问题,可以通过连接复用来解决,那么就引入了连接池的概念

连接池的原理

在创建连接池时,就先初始化指定数量的连接(initnum),将建立的连接放入到池中,当有新的SQL操作请求时,可以在池中获取一个空闲的连接来执行SQL操作,当SQL执行完成后将连接放入池中重复使用,最大连接数(maxNum),当池中的连接都在使用时,而且连接数没有到达最大的时候,就可以新创建一个连接来执行新的SQL请求操作,如果已经达到了最大的连接数且所有的连接都在使用时可以将新连接进行拒绝或者是通过缓存先存储等有空闲连接在此执行,有空闲时间阈值,当一个连接空闲的时间超过阈值,就自动进行销毁

连接池的优势

1、复用资源

2、对资源的进行统一的管理

3、提高了系统资源的利用率

 

连接池:c3p0,Druid,dbcp...连接池

引入依赖

配置连接池

使用连接池

 

C3p0连接池

引入依赖

  <!--c3p0连接池-->
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>

创建c3p0-config.xml配置文件

在resources资源路径下增加c3p0的配置

<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
    <!--配置连接池mysql-->
    <named-config name="mysql">
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/ssms</property>
        <property name="user">root</property>
        <property name="password">123456</property>
        <!-- 初始化连接数 -->
        <property name="initialPoolSize">10</property>
        <!--最大空闲时间,多少秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 -->
        <property name="maxIdleTime">30</property>
        <!--连接池中保留的最大连接数。Default: 15 -->
        <property name="maxPoolSize">100</property>
        <!-- 最小连接数 -->
        <property name="minPoolSize">10</property>
    </named-config>
    
</c3p0-config>

连接池的使用

 //设置数据源DataSource
        ComboPooledDataSource dataSource = new ComboPooledDataSource("mysql");

        try {
            //获取Connection
            Connection connection = dataSource.getConnection();

            //获取Statement对象
            Statement statement = connection.createStatement();

            String sql = "select * from user where id = 25";
            ResultSet resultSet = statement.executeQuery(sql);

            while (resultSet.next()) {
                Integer id1 = resultSet.getInt("id");
                String account = resultSet.getString("account");
                String name = resultSet.getString("name");
                System.out.println("Id:" + id1 + ",name:" + name);
            }


        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

 

 

 

上一篇:Java 中 JDBC 的使用详解


下一篇:视频:JDBCRDD源码及自定义JDBCRDD的分区策略