Java之JDBC

Posted 明王不动心

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java之JDBC相关的知识,希望对你有一定的参考价值。

JDBC的全称为:Java DataBase Connectivity(Java数据库连接)。
是一种执行SQL语句的Java API,可以为多种关系型数据库提供统一的访问。

 

1.JDBC的API

(1)JDBC入门

package com.imooc.jdbc.demo1;

import com.mysql.cj.jdbc.Driver;
import java.sql.*;

public class JDBCDemo1 {
    public void demo1() throws SQLException {  //这里需要接受异常
        //1.加载驱动
        DriverManager.registerDriver(new Driver());
        //2.获取连接
        Connection conn = DriverManager.getConnection("jdbc:mysql://18.0.51.10:3306/jdbctest","root","123456");
        //3.创建执行SQL语句的对象
        String sql = "select * from user";
        Statement stmt = conn.createStatement();
        //4.执行sql语句
        ResultSet resultSet = stmt.executeQuery(sql);
        while (resultSet.next()){
            int uid = resultSet.getInt("uid");
            String name = resultSet.getString("name");
            String mobile = resultSet.getString("mobile");
            String addr = resultSet.getString("addr");
            System.out.println(uid+"  "+name+"  "+mobile+"  "+addr);
        }
        //5.释放资源
        resultSet.close();
        stmt.close();
        conn.close();
    }
}

如果数据库中文变成???,需要注意编码方式:
"jdbc:mysql://18.0.251.10:3306/jdbctest?useUnicode=yes&characterEncoding=utf8"

 

(2)JDBC的API——DriverManager的使用

DriverManager属于驱动管理类。
主要有两个作用:
a.注册驱动
DriverManager.registerDriver(new Driver());
但是这种方式会导致驱动注册两次。
因为在这个驱动中有一个静态代码块会注册驱动。所以更好的做法是加载静态代码块。
实际中会使用如下方式注册:
Class.forName("com.mysql.cj.jdbc.Driver");

b.获取连接
Connection conn = DriverManager.getConnection("jdbc:mysql://148.70.251.10:3306/jdbctest","root","123456");
jdbc:协议
mysql:子协议

 

(3)JDBC的API——Connection的使用

Connection主要用来连接对象。
主要作用:
a.创建执行SQL语句的对象
  createStatement():创建一个Statement对象来将SQL语句发送到数据库。(执行SQL语句)
  prepareStatement(String sql): 创建一个 PreparedStatement 对象来将参数化的 SQL 语句发送到数据库。(预编译SQL语句)
  prepareCall(String sql):创建一个CallableStatement对象来调用数据库存储过程。(执行SQL中的存储过程)

b.进行事务的管理
  setAutoCommit():将此连接的自动提交模式设置为给定状态。
  commit():提交事务。
  rollback():回滚

 

(4)JDBC的API——Statement的使用

Statement主要用来执行SQL语句。
a.执行SQL语句
  execute(String sql):执行SQL,执行select返回true,否则返回false
  executeQuery(String sql):执行SQL中的select语句,返回一个结果集
  executeUpdate(String sql):执行SQL中的insert/update/delete语句,返回一个int类型的值
b.执行批处理操作
  addBatch(String sql):添加到批处理
  executeBatch():执行批处理
  clearBatch():清空批处理

 

(5)JDBC的API——ResultSet的使用

ResultSet,结果集,就是查询语句接口的封装。
next():将光标从当前位置向前移动,也就是下一个值。
针对不同的类型的数据可以使用getXXX()获取数据。通用获取数据的方法getObject()。

 

(6)JDBC的资源释放

jdbc程序运行完后,切记要释放程序在运行过程中,创建的那些与数据库进行交互的对象。
这些对象通常是ResultSet,Statement和Connection对象。

每个数据库都有最大连接数,如果没有释放,可能导致无法建立新的数据库连接。
特别是Connection对象,它是非常稀有的资源,用完必须马上释放,
如果COnnection不能及时、正确的关闭,极易导致系统宕机。
Connection的使用原则是尽量晚创建,尽量早的释放。

常规释放方式:

  resultSet.close();
  stmt.close();
  conn.close();

但是这种释放方式并不彻底,如果前面代码异常,那么可能无法得到释放。可以使用try-finally关键字:

package com.imooc.jdbc.demo1;

import com.mysql.cj.jdbc.Driver;

import java.sql.*;

public class JDBCDemo1 {
    public void demo1() {  //这里需要接受异常
        Connection conn = null;
        Statement stmt = null;
        ResultSet resultSet = null;
        try {
            //1.加载驱动
//          DriverManager.registerDriver(new Driver());
            Class.forName("com.mysql.cj.jdbc.Driver");
            //2.获取连接
            conn = DriverManager.getConnection("jdbc:mysql://148.70.251.10:3306/jdbctest", "root", "123456");
            //3.创建执行SQL语句的对象
            String sql = "select * from user";
            stmt = conn.createStatement();
            //4.执行sql语句
            resultSet = stmt.executeQuery(sql);
            while (resultSet.next()) {
                int uid = resultSet.getInt("uid");
                String name = resultSet.getString("name");
                String mobile = resultSet.getString("mobile");
                String addr = resultSet.getString("addr");
                System.out.println(uid + "  " + name + "  " + mobile + "  " + addr);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {  //不论结果如何都会释放连接
            //5.释放资源
            if (resultSet != null){  //需要对情况进行判断,如果可能没有创建连接,当然也无法释放连接
                try {
                    resultSet.close();
                }catch (SQLException e){
                    e.printStackTrace();
                }
            }
            if (stmt != null) {
                try {
                    stmt.close();
                } catch (SQLException e){
                    e.printStackTrace();
                }
                stmt = null;
            }
            if (conn != null){
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                conn = null;  //垃圾回收机制更早回收对象,提前回收
            }
        }
    }
}

 

 

2.JDBC的CRUD操作

所谓的CRUD就是数据库的增删改查。

(1)增删改

package com.imooc.jdbc.demo1;

import org.junit.Test;

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

public class JDBCDemo2 {
    @Test
    public void demo2(){
        Connection conn = null;
        Statement stmt = null;
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://148.70.251.10:3306/jdbctest?useUnicode=yes&characterEncoding=utf8", "root", "123456");
            ////String sql = "insert into user value(null,‘hao‘,‘18972068500‘,‘湖北安陆‘)";
            ////String sql = "update user set mobile=‘15302723629‘ where name=‘ming‘";
            //
            String sql = "delete from user where name=‘xing‘";
            stmt = conn.createStatement();
            int i = stmt.executeUpdate(sql);
            if (i > 0){
                System.out.println("删除成功");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (stmt != null) {
                try {
                    stmt.close();
                } catch (SQLException e){
                    e.printStackTrace();
                }
                stmt = null;
            }
            if (conn != null){
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                conn = null;
            }
        }
    }
}

 

(2)查

a.查询多条记录

package com.imooc.jdbc.demo1;

import org.junit.Test;

import java.sql.*;

public class JDBCDemo2 {
    @Test
    public void demo2(){
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://148.70.251.10:3306/jdbctest?useUnicode=yes&characterEncoding=utf8", "root", "123456");
            String sql = "select * from user";
            stmt = conn.createStatement();
            rs = stmt.executeQuery(sql);
            while (rs.next()){
                int uid = rs.getInt("uid");
                String name = rs.getString("name");
                String mobile = rs.getString("mobile");
                String addr = rs.getString("addr");
                System.out.println(uid+"  "+name+"  "+mobile+"  "+addr);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                rs = null;
            }
            if (stmt != null) {
                try {
                    stmt.close();
                } catch (SQLException e){
                    e.printStackTrace();
                }
                stmt = null;
            }
            if (conn != null){
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                conn = null;
            }
        }
    }
}

 

b.查询单条记录

if (rs.next()){
    int uid = rs.getInt("uid");
    String name = rs.getString("name");
    String mobile = rs.getString("mobile");
    String addr = rs.getString("addr");
    System.out.println(uid+"  "+name+"  "+mobile+"  "+addr);
}

 

 

3.JDBC工具类的抽取

为了简化JDBC的开发,可以将一些重复的代码进行提取。

(1)抽象工具类

package com.imooc.jdbc.utils;

import java.sql.*;

public class JDBCUtils {
    private static final String driverClass;
    private static final String ip;
    private static final String port;
    private static final String database;
    private static final String user;
    private static final String password;
    private static final String character;
    private static final String url;

    static {  //静态加载
        driverClass = "com.mysql.cj.jdbc.Driver";
        ip = "148.70.251.10";
        port = "3306";
        database = "jdbctest";
        user = "root";
        password = "123456";
        character = "useUnicode=yes&characterEncoding=utf8";
        url = "jdbc:mysql://" + ip + ":" + port + "/" + database + "?" + character;
    }
  //加载驱动器
    public static void loadDriver() throws ClassNotFoundException {
        Class.forName(driverClass);
    }
  //获取连接
    public static Connection getConnection() throws Exception{
        loadDriver();
        Connection conn = DriverManager.getConnection(url, user, password);
        return conn;
    }
  //释放连接
    public static void release(Statement stmt,Connection conn){
        if (stmt != null) {
            try {
                stmt.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
            stmt = null;
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
            conn = null;
        }
    }
    public static void release(ResultSet rs,Statement stmt,Connection conn){
        if (rs != null) {
            try {
                rs.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
            rs = null;
        }
        release(stmt,conn);
    }
}

 

(2)简单使用

package com.imooc.jdbc.test;

import com.imooc.jdbc.utils.JDBCUtils;
import org.omg.Messaging.SYNC_WITH_TRANSPORT;

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

public class test {
    public static void main(String[] args) {
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        try{
            conn = JDBCUtils.getConnection();  //获取连接
            stmt = conn.createStatement();
            String sql = "select * from user";
            rs = stmt.executeQuery(sql);
            while (rs.next()){
                int uid = rs.getInt("uid");
                String name = rs.getString("name");
                String mobile = rs.getString("mobile");
                String addr = rs.getString("addr");
                System.out.println(uid+"  "+name+"  "+mobile+"  "+addr);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.release(rs,stmt,conn);  //一次释放
        }
    }
}

 

(3)配置文件优化

如果我们想在一个配置文件中配置参数,可以将其剥离出来。
创建jdbc.properties,然后使用类加载器加载配置:

static {
    //加载属性文件并解析
    Properties props = new Properties();
    //通过类加载器获取
    InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
    try {
        props.load(is);
    } catch (IOException e) {
        e.printStackTrace();
    }
    driverClass = props.getProperty("driverClass");
    ip = props.getProperty("ip");
    port = props.getProperty("prot");
    database = props.getProperty("database");
    user = props.getProperty("user");
    password = props.getProperty("password");
    character = props.getProperty("character");
    url = "jdbc:mysql://" + ip + ":" + port + "/" + database + "?" + character;
}

 

 

4.SQL注入

SQL注入的实质是修改了SQL语句。输入了SQL语句的关键字,从而修改了逻辑。
select * from user where username = “xxx” and password = “xxx”;

如果我们在输入username的时候进行注入,比如:
username = “xxx ‘or ‘1=1”

结果就变成这样了:
select * from user where username = “xxx ‘or ‘1=1” and password = “xxx”;

无形之间,就修改SQL语句的逻辑。

 

js校验能够拦截一些初级注入,但是可以修改url来突破。
上述问题还是在于,SQL语句采用的字符串拼接方式。
在python中采用字符串拼接或者%替换都无法解决SQL注入的问题,一般解决方法有二:
  a.对传入的参数进行编码转义
  b.使用python的MySQLdb模块的execute方法。
    cursor.execute(query, params)
    第一个是参数化的sql语句,第二个是对应的实际的参数值。
    函数内部escape_string方法会对传入的参数值进行相应的处理(主要是转化为字符串)防止sql注入。

 

在Java中解决SQL注入漏洞可以使用PreparedStatement。
PreparedStatement是Statement的子接口,
它的实例对象可以通过调用Connection.preparedStatement(sql)方法获得,
相对于Statement对象而言:
  PreperedStatement可以避免SQL注入的问题。
  Statement会使数据库频繁编译SQL,可能造成数据库缓冲区溢出。
  PreparedStatement可对SQL进行预编译,从而提高数据库的执行效率。
  并且PreparedStatement对于sql中的参数,允许使用占位符的形式进行替换,简化SQL语句的编写。
String sql = "select * from user";
stmt = conn.createStatement(); //将其类型改为PreparedStatement即可
//执行sql语句
resultSet = stmt.executeQuery(sql);

采用预编译的方式,会将SQL语句的结构固定,你再传入关键字也不会改变SQL语句的结构。
String sql = “select * from user where username = ? and password = ?”
pstmt = conn.prepareStatement(sql)
pstmt.setString(1,username)
pstmt.setString(2,password)
pstmt.executeQuery()

以上是关于Java之JDBC的主要内容,如果未能解决你的问题,请参考以下文章

Java代码审计之SQL注入

关于mysql驱动版本报错解决,Cause: com.mysql.jdbc.exceptions.jdbc4Unknown system variable ‘query_cache_size(代码片段

[java两个数据库同步]Java代码之JDBC实现数据库之间定时的表格传输(由一个库读取到另一个库)实例

JAVA之AOP

JDBC之初识

Java语言基础之方法的设计