JDBC


驱动

下载驱动

本章所用到的jar包
单击直接下载。

我们要通过Java代码来操作数据库,而在代码与数据库这两者之间我们需要一个中间的连接来实现,而这个连接就是JDBC。

我们要使用JDBC需要先下载相应的驱动

JDBC驱动

我这里是用mysql-connector-java-5.1.47

添加驱动到lib包

jdbc执行过程代码

  1. 加载驱动
  2. 通过url、用户名、密码获取数据库连接
  3. 通过数据库连接返回执行sql语句的对象Statement
  4. 用Statement对象执行sql语句
  5. 如果是查询语句有返回结果的,用ResultSet的对象接收
  6. 最后,关闭resultSet、statement、connection连接

例子:

-- 先建立个有数据表的数据库,并插入数据
CREATE DATABASE jdbcStudy CHARACTER SET utf8 COLLATE utf8_general_ci;

USE jdbcStudy;

CREATE TABLE `users`(
id INT PRIMARY KEY,
NAME VARCHAR(40),
PASSWORD VARCHAR(40),
email VARCHAR(60),
birthday DATE
);

INSERT INTO `users`(id,NAME,PASSWORD,email,birthday)
VALUES(1,'zhansan','123456','zs@sina.com','1980-12-04'),
(2,'lisi','123456','lisi@sina.com','1981-12-04'),
(3,'wangwu','123456','wangwu@sina.com','1979-12-04')

// 编写代码查询数据库的数据
package JDBC;

import java.sql.*;

public class TestJDBC {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        //1.加载驱动
        Class.forName("com.mysql.jdbc.Driver"); //固定写法

        //2.用户信息和url
        String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&&useSSL=false";
        String name = "root";
        String password = "123456";

        //3.连接成功,返回数据库对象,connection代表数据库
        Connection connection = DriverManager.getConnection(url, name, password);

        //4.执行SQL的对象statement
        Statement statement = connection.createStatement();

        //5.执行SQL的对象,去执行SQL 可能存在结果,查看返回结果
        String sql = "SELECT * FROM users";
        ResultSet resultSet = statement.executeQuery(sql);
        while (resultSet.next()) {
            System.out.println("name=" + resultSet.getObject("NAME"));
        }

        //6.释放连接
        resultSet.close();
        statement.close();
        connection.close();
    }
}

工具类

由于JDBC连接有些是固定的,所以我们可以将他们提取出来,这样这样就不用重复编写。这就形成了工具类。

参数文件db.properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&&useSSL=false
username=root
password=123456

Jdbc工具类

package JDBC;

import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

public class JdbcUtils {
    private static String driver=null;
    private static String url=null;
    private static String username=null;
    private static String password=null;

    static{
        try {
            //1.获取数据文件的数据
            InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("JDBC/db.properties");
            Properties properties = new Properties();
            properties.load(in);

            driver=properties.getProperty("driver");
            url=properties.getProperty("url");
            username=properties.getProperty("username");
            password=properties.getProperty("password");

            //2.加载驱动,驱动只需要加载一次
            Class.forName(driver);
        } catch (ClassNotFoundException | IOException e) {
            e.printStackTrace();
        }
    }
    //获取连接方法
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url, username, password);
    }
    //释放连接资源方法
    public static void release(Connection conn, Statement st, ResultSet rs){
        if(rs!=null){
            try {
                rs.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if(st!=null){
            try {
                st.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if(conn!=null){
            try {
                conn.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
}

测试工具类

package JDBC;

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

public class TestSelect {
    public static void main(String[] args) {
        Connection connection=null;
        Statement statement=null;
        ResultSet rs=null;
        try {
            connection= JdbcUtils.getConnection();
            statement = connection.createStatement();
            String sql="SELECT * FROM users";
            //返回结果集
            rs=statement.executeQuery(sql);
            while(rs.next()){
                System.out.println(rs.getString("NAME"));
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            JdbcUtils.release(connection,statement,rs);
        }
    }
}

注入

SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。

如:

我们要通过用户名和密码来查询数据,但我们在设置查询代码时是直接将传来的用户名和密码直接连接到查询语句的字符串里。


String sql="SELECT * FROM `users` WHERE `NAME`= '"+username+"' AND `PASSWORD` = '"+password+"'";

如上面这样,那么用户可以在输入用户名、密码时输入一定的字符来获取信息,如下

//用户名输入'or' 1=1
//密码输入'or' 1=1
//这样我们的sql语句就变成
SELECT * FROM `users` WHERE `NAME`= ''or' 1=1' AND `PASSWORD` = ''or' 1=1';
//这将会把数据都输出出来

测试

package JDBC;

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

public class SQLInject {
    public static void main(String[] args) {
        Connection connection=null;
        Statement statement=null;
        ResultSet rs=null;
        try {
            connection= JdbcUtils.getConnection();
            statement = connection.createStatement();
            String username ="'or' 1=1";
            String password ="'or' 1=1";
            //SELECT * FROM `users` WHERE `NAME` = 'zhansan' AND `PASSWORD` = '123456';
            String sql="SELECT * FROM `users` WHERE `NAME`= '"+username+"' AND `PASSWORD` = '"+password+"'";
            //返回结果集
            System.out.println(sql);
            rs=statement.executeQuery(sql);

            while(rs.next()){
                System.out.println(rs.getString("NAME"));
                System.out.println(rs.getString("PASSWORD"));
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            JdbcUtils.release(connection,statement,rs);
        }
    }
}

防止注入方法

我们要防止注入可以使用PreparedStatement对象,?表示占位符。

PreparedStatement 防止SQL注入的本质是把传递进来的参数当做字符。假设其中存在转义字符,如 会被直接转义。

实现方式

package JDBC;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Date;

public class TestInsert {
    public static void main(String[] args) {
        Connection conn=null;
        PreparedStatement pst=null;
        try {
            conn = JdbcUtils.getConnection();
            //区别:使用?占位符代替参数
            String sql="insert into users values(?,?,?,?,?)";
            //预编译SQL,先写SQL然后不执行
            pst=conn.prepareStatement(sql);
            //手动给参数赋值
            pst.setInt(1,4);
            pst.setString(2,"amo");
            pst.setString(3,"123");
            pst.setString(4,"1");
            pst.setDate(5,new java.sql.Date(new Date().getTime()));

            //执行
            int i = pst.executeUpdate();
            if(i>0)
                System.out.println("insert success");

        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            JdbcUtils.release(conn,pst,null);
        }
    }
}

jdbc事务

jdbc事务是通过编写java代码和利用jdbc驱动来实现mysql事务的方式。

创建account表并插入数据

CREATE TABLE `account`
(
id INT PRIMARY KEY,
NAME VARCHAR(40),
money VARCHAR(40)
);

INSERT INTO `account`(`id`,`name`,`money`) VALUES (1,'A','500'),(2,'B','1000');

编写事务代码

package JDBC;

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

public class TestTransaction {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement st = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils.getConnection();
            //关闭自动提交功能,开始事务
            conn.setAutoCommit(false);

            String sql1 = "UPDATE account set money=money-100 WHERE name='A'";
            st = conn.prepareStatement(sql1);
            st.executeUpdate();

            //会导致操作失败,事务回滚。
            int x=1/0;

            String sql2 = "UPDATE account set money=money+100 WHERE name='B'";
            st = conn.prepareStatement(sql2);
            st.executeUpdate();

            //业务完毕提交事务
            conn.commit();
            System.out.println("Success");

        } catch (SQLException throwables) {
            //如果失败则回滚
            try {
                conn.rollback();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            throwables.printStackTrace();
        } finally {
            JdbcUtils.release(conn,st,rs);
        }
    }
}

连接池

由于我们在每次操作数据库时要开数据库连接,用完就关闭连接,这样频繁开关,会十分的占用资源影响性能。所以我们要使用连接池来解决这个问题。

连接池

数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。

简单的说就是先预先开一些连接放在连接池里,要用到的时候就拿出来用,不用的时候就放回连接池。而这些连接一旦超过最大空闲时间就会被关闭。这样一来,在频繁进行数据库操作时就不用老是关闭连接,而在不频繁进行数据库操作时,一些多余的连接就会被关闭。

DBCP连接池实现

先导入jar包

commons-dbcp-1.4

commons-pool-1.6

先编写参数文件

#连接设置
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&&useSSL=false
username=root
password=123456

#连接池设置
#!-- 初始化连接 --
initialSize=10

#最大连接数量
maxActive=50

#!-- 最大空闲连接 --
maxIdle=20

#!-- 最小空闲连接 --
minIdle=5

#!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 --
maxWait=60000
#JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:【属性名=property;】
#注意:user 与 password 两个属性会被明确地传递,因此这里不需要包含他们。
connectionProperties=useUnicode=true;characterEncoding=UTF8

#指定由连接池所创建的连接的自动提交(auto-commit)状态。
defaultAutoCommit=true

#driver default 指定由连接池所创建的连接的只读(read-only)状态。
#如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix)
defaultReadOnly=

#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_UNCOMMITTED

连接工具类

package JDBC;

import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;

import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

public class JdbcUtils_DBCP {
    private static DataSource dataSource=null;
    static {
        try {
            InputStream in = JdbcUtils_DBCP.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
            Properties properties=new Properties();
            properties.load(in);
            //创建数据源 工厂模式->创建
            dataSource= BasicDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //获取连接
    public static Connection getConnection() throws SQLException{
        return dataSource.getConnection();//从数据源获取连接
    }

    //释放连接资源
    public static void release(Connection conn, Statement st, ResultSet rs){
        if(rs!=null){
            try {
                rs.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if(st!=null){
            try {
                st.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if(conn!=null){
            try {
                conn.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
}

测试

package JDBC;

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

public class TestDBCP {
    public static void main(String[] args) {
        Connection connection = null;
        Statement statement = null;
        ResultSet rs = null;
        String username="lisi";
        try {
            connection = JdbcUtils_DBCP.getConnection();
            statement = connection.createStatement();
            String sql = "SELECT * FROM users";

            //返回结果集
            rs = statement.executeQuery(sql);
            while (rs.next()) {
                System.out.print(rs.getString("NAME"));
                System.out.println(rs.getString("PASSWORD"));
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            JdbcUtils_DBCP.release(connection, statement, rs);
        }

    }
}

注:以上内容参考自狂神说Java