【Java之JDBC】

JDBC

数据库的访问过程

1.建立连接

​ 协议、ip、端口号、用户名、密码

2.客户端向MySQL服务器发送请求

​ 即SQL语句

3.服务器接收到请求,执行语句,得到一个结果

4.服务器将结果返回给客户端

5.客户端接收到结果后将结果展示出来

6.执行完毕后断开连接

JDBC的定义

JDBC:Java Database Connection

Java数据库连接。Java语言需要访问多个数据库,但流程是一样的,于是就制定了一套统一的接口方便开发者使用,具体的驱动程序由各个数据库的厂商来提供

JDBC所有的接口都在 java.sql 以及 javax.sql 这两个包下面。

JDBC程序的创建

1.新建项目

2.导入驱动程序包

  • 把驱动程序包下载下来https://mvnrepository.com/

    jar包是java虚拟机可以识别的压缩格式,里面放的class文件

  • 把文件放到项目的根目录下

  • 右键添加为library

  • 编写程序

public static void main(String[] args) throws SQLException{
    //1.注册驱动
    DriverManager.registerDriver(new Driver());
    
    //2.获取连接 返回的是Connection接口的实现类
    Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/36th_rev?useSSL=false","root","123456");
    //3.获取statement对象 这个对象是用来包装SQL语句为网络请求
    Statement statement = connection.createStatement();
    //4.通过statement对象发送SQL语句
    int affectedRows = statement.executeUpdate("");
    //5.解析结果集
    //6.关闭资源
    statement.close();
    connection.close();
}
public static void main(String[] args) throw SQLException{
    //1.注册驱动
    DriverManager.registerDriver(new Driver());
    //2.获取连接 返回的是Connection接口的实现类
    Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/36th_rev>useSSL=false","root","123456");
    //3.获取statement对象
    Statement statement = connection.createStatement();
    //4.通过statement对象发送语句
    ResultSet rs = statement.executeQuery("select * from student");
    //5.解析结果集
    while(resultSet.next()){
        int id = rs.getInt("id");
        String name = rs.getString("name");
        String aClass = rs.getString("class");
        int score = rs.getInt("score");
    }
    //6.关闭资源
    rs.close();
    statement.close();
    connection.close();
    
}

API

DriverManager

用于注册驱动和获取连接

注册驱动:

DriverManager.registerDriver(new com.mysql.jdbc.Driver());

获取连接:

Connection connection = DriverManager.getConnection(String url,String username,String password)

这里获取到的连接对象是

com.mysql.jdbc.JDBC4Connection这个类的对象,对象是根据我们上面注册的驱动来变化的,所以在设计工具类的时候就可以使用配置文件

Connection

这个是JDBC中提供的一个接口,这个接口的实现类代表一个数据库连接。在MySQL的操作中,实现类是com.mysql.jdbc.JDBC4Connection,通常我们使用连接来获取statement对象,用于控制事务

获取statement对象,返回的是StatementImpl,是Statement接口的实现类

Statement statement = connection.createStatement();

Statement

这个是一个JDBC中提供的一个接口,这个接口的实现类的具体作用是帮助我们把SQL语句包装成网络请求,发送给MySQL服务器

执行SQL语句

// 执行增删改的API,返回的是影响的行数
int affectedRows  = statement.executeUpdate(String sql);
    
// 执行查询的API,返回的是查询语句执行完之后的结果集
ResultSet rs = statement.executeQuery(String sql);

ResultSet

表示结果集,使用API来解析结果集

有一个类似于迭代器的游标,初始的时候指向第一个数的前一个位置,然后向下迭代,返回值是boolean类型,与迭代器不同,无法使用for each

// 向后移动游标,如果ret是true,说明移动了,如果ret是false,表示这个游标已经移到了结果集的尾部,这个游标默认指向第一行之前
Boolean ret  = resultSet.next();

// 移动到末尾
resultSet.afterLast();

// 往前移动
Boolean ret = resultSet.previous();

// 移动到第一行之前
resultSet.beforeFirst();

// 获取值 传入结果集的列名,根据字段的类型来选择对应的API,如果有别名,要使用别名
resultSet.getInt(String columnName);
resultSet.getString(String columnName);
resultSet.getDate(String columnName);
//循环调用
while(resultSet.next){
    resultSet.getInt(String columnName);
	resultSet.getString(String columnName);
	resultSet.getDate(String columnName);
}

释放资源

finally{
 //注意先后顺序,应该是resultSet、statement、connection
    try{
        resultSet.close();
    }finally{
        try{
            statement.close();
        }finally{
            connection.close();
        }
    }
}

好处:

  • 确保connection.close()能够被执行
  • 严格保证关闭的执行顺序

指令重排:volatile

java在执行程序的时候如果代码的顺序并不影响执行的结果的话,那么有可能导致执行的顺序错乱

int i = 0;
int b = 1;
i++;
b++;

JDBC的优化

使用工具类来简化JDBC的使用

package day4.utils;
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;

public class JDBCUtils { //JDBC工具类,用于封装方法
    static Connection connection;
    static String url;
    static String username;
    static String password;
    static String driverClassName;

    static {
        //静态代码块加载配置文件
        Properties properties = new Properties();
        try {
            properties.load(new FileInputStream("jdbc.properties"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        url = properties.getProperty("url");
        username = properties.getProperty("username");
        password = properties.getProperty("password");
        driverClassName = properties.getProperty("driverClassName");
        try {
            Class.forName(driverClassName); //注册驱动
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        try {
            connection = DriverManager.getConnection(url, username, password); //获取连接
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection() { //外部调用的获取连接的方法
        return connection;
    }

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

        } finally {
            try {
                if (statement != null) {
                    try {
                        statement.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            } finally {
                if (connection != null) {
                    try {
                        connection.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}
package day4.utils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class MyUtils { //用于增删改查的类
    static Statement statement = null;
    static Connection connection = JDBCUtils.getConnection();
    public static void createData(String createSql){ //创建数据的方法
        try {
            statement = connection.createStatement();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        try {
            statement.executeUpdate(createSql);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    public static ResultSet selectData(String sql){ //用于查找数据,返回一个结果集
        ResultSet rs = null;
        try {
            statement = connection.createStatement();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        try {
            rs = statement.executeQuery(sql);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return rs;
    }
    public static void closeResource(){ //用于关闭资源
        JDBCUtils.closeResource(null,statement,connection);
    }

    public static void closeResource(ResultSet resultSet){ //用于关闭资源的方法重载,提供给带有resultSet的使用
        JDBCUtils.closeResource(resultSet,statement,connection);
    }

}

数据库的注入问题

在用户登录的过程中,如果输入的SQL语句带有关键字的话,那么就有可能造成安全问题

login("","xxx' or '1=1");

如上,sql会判定or为关键字,那么1=1永远返回的是true,这样就会造成安全问题

解决办法:

  • 不让用户自己输入(登录的案例不太适用)
  • 审查用户的登录的时候传递过来的用户名和密码中是否有关键字
  • 适用SQL语句预编译的方式来做,可以解决数据库的注入问题,PrepareStatement就可以解决数据库的注入问题

PrepareStatement

statement的一个子接口,可以帮助我们防止注入的问题

//获取prepareStatement对象,需要传入一个SQL语句的模板,使用?来占位
//执行这一步的时候会把SQL语句发送到服务器,然后服务器进行预编译,并返回一个对象
PreparedStatement preparedStatement = connection.prepareStatement("select * from user where name = ? and password = ?")
//设置参数,下标对应的是?,从1开始
preparedStatement.setString(1,username);    
preparedStatement.setString(2,password);
//执行SQL语句,到这一步时需要再次与服务器进行通信,客户端将参数传给服务器后,服务器执行指令,返回一个结果集
ResultSet resultSet = preparedStatement.executeQuery();

批处理

批量执行SQL语句

循环

//循环来处理
public static void batchForeach() throws SQLException{
    //获取statement对象
    Statement statement = connection.createStatement;
    for(int i = 0;i<1000;i++){
        statement.executeUpdate("insert into user values(null,'带土','神威','莫毅几多')")
    }
    //关闭资源
    JDBCUtils.closeResource(null,statement,null)//正常来说连接都要关闭,但这里的代码是因为要使用3个不同的批处理对比,但共用同一个连接,如果断开的话会报错,因此等待执行完毕一起关闭
}

statement

public static void batchByStatement() throws SQLException{
    //获取连接
    Connection connection = JDBCUtils.getConnection();
    //获取Statement对象
    Statement statement = connection.createStatement();
    //添加到批处理
    for(int i = 0;i<100;i++){
        statement.addBatch("insert into user values(null,'naruto','螺旋丸','哒嘚吧哟')")
    }
    //执行批处理
    statement.executeBatch();
    //关闭资源
    JDBCUtils.closeResource(null,statement,null);
}

PrepareStatement

需要在url后面添加参数rewriteBatchedStatements=true才能使其生效

public static void batchBypreparedStatement() throws SQLException{
    //获取连接
    Connection connection = JDBCUtils.getConnection();
    //获取对象
    PreparedStatement preparedStatement = connection.prepareStatement("insert into user values(null,?,?,?)");
    for(int i = 0;i<100;i++){
        preparedStatement.setString(1,'sasigi');
        preparedStatement.setString(2,'千鸟');
        preparedStatement.setString(3,'naruto');
        //添加到批处理
        preparedStatement.addBatch();
    }
    //执行批处理
    preparedStatement.executeBatch();
    //关闭资源
    JDBCUtils.closeResource(null,preparedStatement,null);
       
}

服务器只需要预编译一次,然后每次我们输入不同的参数,服务器执行,这样效率大大的提高了

通信1000次的对比

通信次数 编译次数 执行时间
for循环 1000 1000 1213 ms
statement 1 1000 876 ms
prePrepareStatement 2 1 35 ms

prePrepareStatement的速度显然最快,但statement不限定SQL语句的格式,在使用的时候要根据实际情况来进行考虑

你可能感兴趣的:(#,Java核心技术精选,java,开发语言)