[Java] Resumen de JDBC

JDBC

JDBC es un conjunto de API que utilizan el lenguaje Java para operar bases de datos relacionales. El nombre completo es (Java DataBase Connectivity) conexión de base de datos Java.

inserte la descripción de la imagen aquí

Esencia JDBC:

  • Un conjunto de reglas definidas por el oficial (compañía sol) para operar todas las bases de datos relacionales, a saber, la interfaz
  • Varios proveedores de bases de datos implementan esta interfaz y proporcionan paquetes jar controlados por bases de datos.
  • Podemos usar esta programación de interfaz (JDBC), el código real para ejecutar es la clase de implementación en el paquete jar del controlador

Idea agrega paquete de controladores MySQL

1. Primero descargue el paquete mysqljar apropiado del repositorio de Maven

2. Cree una carpeta lib y agregue el paquete jar del controlador MySQL 1

inserte la descripción de la imagen aquí

3. Establezca la carpeta lib en la biblioteca

inserte la descripción de la imagen aquí

uso de JDBC

使用步骤

  1. Crea un proyecto e importa el paquete jar
  2. Registrar el conductorClass.forName("com.mysql.jdbc.Driver");
  3. conectarseConnection conn = DriverManager.getConnection(url,username,password);
  4. Definir sentencias SQLString sql ="update...";
  5. Obtener el objeto SQL de ejecuciónStatement stmt =conn.createStatement();
  6. ejecutar SQLstmt.executeUpdate(sql);
  7. Procesar el resultado devuelto
  8. liberar recursos

\

inserte la descripción de la imagen aquí

使用示例

 import java.sql.Connection;
 import java.sql.Driver;
 import java.sql.DriverManager;
 import java.sql.Statement;
 ​
 /**
  * JDBC快速入门
  */
 public class JDBC_Test {
     public static void main(String[] args) throws Exception {
         //注册驱动
         Class.forName("com.mysql.jdbc.Driver");//固定的类名
 ​
         //获取连接
         String url = "jdbc:mysql://127.0.0.1:3306/db1";//jdbc:mysql://+ IP + Port /数据库名
         String username = "root";
         String password = "123";
         Connection conn = DriverManager.getConnection(url, username, password);
 ​
         //定义sql语句
         String sql = "update account set money =20300 where id =1";
 ​
         //获取执行sql的对象 statement
         Statement stmt = conn.createStatement();
 ​
         //执行sql
         int count = stmt.executeUpdate(sql);//返回受影响的行数
 ​
         //受处理结果
         System.out.println(count);
 ​
         //释放资源
         stmt.close();
         conn.close();
     }
 }
复制代码

base de datos de prueba

 create database db1;
 create table account(
     `id` int primary key,
     `name` varchar(4) not null,
     `money` int not null
 );
 ​
 Insert into account values (1,"ning",10000);
复制代码

API común de JDBC

Drivermanager (clase de gestión de controladores)

efecto:

1. Registre el controlador

 Class.forName("com.mysql.jdbc.Driver");
复制代码

com.mysql.jdbc.Driver源码

 public class Driver extends NonRegisteringDriver implements java.sql.Driver {
     public Driver() throws SQLException {
     }
 ​
     static {
         try {
             DriverManager.registerDriver(new Driver());
         } catch (SQLException var1) {
             throw new RuntimeException("Can't register driver!");
         }
     }
 }
复制代码

El paquete de controladores después de MySQL5 puede omitir el paso de registrar el controlador, lo que cargará automáticamente la clase de controlador en el archivo META->INF/services/java.sql.Driver en el paquete jar.

inserte la descripción de la imagen aquí

2. Obtener conexión a la base de datos

 static Connection getConnection(String url, String user, String password)
复制代码

Parámetros :

inserte la descripción de la imagen aquí Demostración detallada :

 String url = "jdbc:mysql:///db1";//省略127.0.0.1:3306
复制代码

Cuando la versión llegue a la 5.7 o superior, MySQL le sugerirá que use una conexión SSL más segura, que requiere una configuración compleja y reduce el rendimiento en un 20 %, por lo que generalmente no usamos esta configuración y establecemos parámetros para desactivar su sugerencia de advertencia.

 String url = "jdbc:mysql://127.0.0.1:3306/db1?useSSL=false";
复制代码

Conexión (objeto de conexión de base de datos)

efecto:

1. Obtener el objeto que ejecuta el SQL

Hay tres métodos: (principalmente use los dos primeros, el tercero rara vez se usa)

  • Ejecutar objeto SQL ordinario
 Statement createStatement()
复制代码
  • Ejecutar objeto SQL para SQL precompilado
 PreparedStatement prepareStatement(String sql)
复制代码
  • El objeto que ejecuta el procedimiento almacenado.
 CallableStatement prepareCall(String sql)
复制代码

2. Administrar asuntos

inserte la descripción de la imagen aquí

import java.sql.*;

public class JDBC_Test {
    public static void main(String[] args) throws Exception {
        //获取连接
        String url = "jdbc:mysql://127.0.0.1:3306/db1";//jdbc:mysql://+ IP + Port /数据库名
        String username = "root";
        String password = "123";
        Connection conn = DriverManager.getConnection(url, username, password);

        //定义sql语句
        String sql1 = "update account set money =money+500 where id =1";
        String sql2 = "update account set money =money-500 where id =2";
        Statement stmt = null;

        //获取执行sql的对象 statement
        stmt = conn.createStatement();

        try {
            //开启事务
            conn.setAutoCommit(false);//将自动提交事务关闭

            //执行sql
            int count1 = stmt.executeUpdate(sql1);//返回受影响的行数
            System.out.println(count1);

            //出现意外
//            System.out.println(3 / 0);

            int count2 = stmt.executeUpdate(sql2);//返回受影响的行数
            System.out.println(count2);

            //提交事务
            conn.commit();

        } catch (SQLException throwables) {
            //回滚事务
            conn.rollback();
            throwables.printStackTrace();
        }

        //释放资源
        stmt.close();
        conn.close();
    }
}
复制代码

declaración de la base de datos

CREATE DATABASE IF NOT EXISTS db1;
USE db1;
CREATE TABLE account ( `id` INT, `name` VARCHAR ( 10 ), `money` INT, PRIMARY KEY ( id ) );

INSERT INTO account VALUES (1,"阿画",20);
INSERT INTO account VALUES (2,"苗苗",5000);
复制代码

Sentencia (ejecutar sentencia SQL)

1. Ejecutar sentencias DML y DDL

int executeUpdate(sql)
复制代码

Valor de retorno: el número de filas afectadas por la instrucción DML (después de ejecutar la instrucción DDL, puede devolver 0 si la ejecución es exitosa)

实际使用:不返回影响的行数,而是返回执行是否成功,对于一些DDL语句(比如定义数据库),不出现异常就算成功。

String sql = "update account set money =20300 where id =1";
Statement stmt = conn.createStatement();
int count = stmt.executeUpdate(sql);
if (count > 0) {
    System.out.println("执行成功");
} else {
    System.out.println("执行失败");
}
复制代码

2.执行DQL语句

ResultSet executeQuery(sql)
复制代码

返回值: ResultSet 结果集对象

ResultSet(结果集对象)

封装DQL语句的查询结果

其封装了二维表,内部存有游标,游标默认指向数据的上一行(即表头行)

获取查询结果:

boolen next()
复制代码

将光标从当前位置向下移动一行 ,判断当前行是否为有效行(即有数据的行) 返回值:

  • true: 有效行,当前行有数据
  • false:无效行,当前行没有数据
XXx getXxx(参数):获取数据
复制代码

Xxx:数据类型;如: int getInt(参数) ; String getString(参数) 参数:

  • int:列的编号,从1开始
  • String:列的名称

使用步骤

String sql = "select * from account ";
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
while (rs.next()) {
    int id = rs.getInt(1);//ID
    String name = rs.getString(2);//name
    int money = rs.getInt(3);//money
    /*和上面的输出结果一样
    int id = rs.getInt(“id”);//ID
    String name = rs.getString("name");//name
    int money = rs.getInt("money");//money
    */

    System.out.println(id);
    System.out.println(name);
    System.out.println(money);
}
rs.close();
复制代码

使用案例:将ResultSet封装进ArrayList中

定义需要封装的类

class customers {
    String CustomerID;
    String CompanyName;
    String Address;
    String City;

    public String getCustomerID() {
        return CustomerID;
    }

    public void setCustomerID(String customerID) {
        CustomerID = customerID;
    }

    public String getCompanyName() {
        return CompanyName;
    }

    public void setCompanyName(String companyName) {
        CompanyName = companyName;
    }


    public String getAddress() {
        return Address;
    }

    public void setAddress(String address) {
        Address = address;
    }

    public String getCity() {
        return City;
    }

    public void setCity(String city) {
        City = city;
    }

    @Override
    public String toString() {
        return "customers{" +
                "CustomerID='" + CustomerID + '\'' +
                ", CompanyName='" + CompanyName + '\'' +
                ", Address='" + Address + '\'' +
                ", City='" + City + '\'' +
                '}';
    }
}
复制代码

将查询到的结果装入集合容器中

 Collection<customers> rsl = new ArrayList<>();
 ​
 String sql = "select * from customers ";
 Statement stmt = conn.createStatement();
 ResultSet rs = stmt.executeQuery(sql);
 while (rs.next()) {
     customers customers = new customers();
     customers.setAddress(rs.getString("Address"));
     customers.setCity(rs.getString("city"));
     customers.setCustomerID(rs.getString("CustomerID"));
     customers.setCompanyName(rs.getString("CompanyName"));
     rsl.add(customers);
 }
 rs.close();
 rsl.forEach(new Consumer<customers>() {
     @Override
     public void accept(customers customers) {
         System.out.println(customers);
     }
 });
复制代码

PreparedStatement(预编译SQL语句)

继承自Statement,作用是预编译SQL语句并执行,预防SQL注入问题。

什么是SQL注入

SQL注入是对用户输入数据的合法性没有判断或过滤不严,通过操作输入来修改事先定义好的SQL语句,用以达到执行代码对服务器进行攻击的方法。

 @Test
 public void testLogin_Inject() throws Exception {
     //获取连接
     String url = "jdbc:mysql:///db1?serverTimezone=UTC";//jdbc:mysql://+ IP + Port /数据库名
     String username = "root";
     String password = "123";
     Connection conn = DriverManager.getConnection(url, username, password);
 ​
     String name = "dadsadadad";
     String pwd = "' or '1' = '1";
     String sql = "select * from tb_user where username='" + name + "'and password ='" + pwd + "'";
     System.out.println(sql);
     Statement stmt = conn.createStatement();
     ResultSet rs = stmt.executeQuery(sql);
 ​
     if (rs.next()) {
         System.out.println("login success");
     } else {
         System.out.println("login fail");
     }
     //释放资源
     stmt.close();
     conn.close();
 }
复制代码

inserte la descripción de la imagen aquí

数据库语句

 CREATE TABLE `tb_user` (
   `username` varchar(20) DEFAULT NULL,
   `password` varchar(20) DEFAULT NULL
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
复制代码

1.防止SQL注入

1.获取PreparedStatement对象

new Connection().prepareStatement(String sql)

造成SQL注入的原因还是在拼接字符串时被钻了篡改语义的空子,所以PreparedStatement干脆不拼接字符串,直接使用?占位符代替参数值。

 //SQL语句中的参数值,这里使用?占位符替代
 String sql = "select * from user where username = ? and password = ?";
 //通过Connection对象获取获取,并传入对应的SQL语句
 PreparedStatement pstmt = conn.prepareStatement(sql);
复制代码

2.设置参数值

PreparedStatement对象 : setXxx(参数1,参数2):给?赋值

使用?占位符代替参数拼接后,我们使用setXxx来设置参数:

Xxx为数据类型,如setInt(参数1,参数2) 参数1为 ?的位置编号(即第几个?),参数2为?的值

3.执行SQL

不需要再传递sql,直接使用PreparedStatement对象调用,

pstmt.executeUpdate();

pstmt.executeQuery();

 @Test
 public void testLogin_Inject() throws Exception {
     //获取连接
     String url = "jdbc:mysql:///db1?serverTimezone=UTC";//jdbc:mysql://+ IP + Port /数据库名
     String username = "root";
     String password = "' or '1' ='1";//这里使用SQL注入也会失败
     Connection conn = DriverManager.getConnection(url, username, password);
 ​
     String name = "zhangsan";
     String pwd = "1234";
     String sql = "select * from tb_user where username=? and password = ?";
     PreparedStatement pstmt = conn.prepareStatement(sql);
 ​
     //设置?的值
     pstmt.setString(1, name);
     pstmt.setString(2, pwd);
 ​
     ResultSet rs = pstmt.executeQuery();
     if (rs.next()) {
         System.out.println("登录成功");
     } else {
         System.out.println("登录失败");
     }
     //释放资源
     rs.close();
     pstmt.close();
     conn.close();
 }
复制代码

预防机制原理:

在设置?的值时会对其转义,原本的单引号'会被转义为',即原本的SQL注入就变成了'' or '1' ='1',无法起到原先的作用。

2.预编译SQL

PreparedStatement能预编译SQL,提高性能。

inserte la descripción de la imagen aquí

  • 当我们在Java中写完SQL语句后,Java代码会将SQL传给MySQL进行检查SQL语法和编译SQL,这两步的执行时间比执行SQL可能更久。
  • 对于Statement对象来说,要执行SQL语句需要每次将其导入Statement对象,如上图,MySQL会分别对两句SQL进行检查和编译;
  • 而对于PreparedStatement对象来说,MySQL只会对SQL语句检查、编译一次,如果sql模板一样,则只需要进行一次检查、编译。
  1. PreparedStatement预编译功能开启:在url后加useServerPrepStmts=true

     String url = "jdbc:mysql:///db1?serverTimezone=UTC&useServerPrepStmts=true";
    复制代码
  2. 配置MySQL执行日志(重启mysq|服务后生效)

     log-output=FILE
     general-log=1
     general_log_file="D:\mysql.log" #日志的位置
     slow-query-log=1
     slow_query_log_file="D:\mysql_slow.log"
     long_query_time=2
    复制代码

使用之前的SQL注入测试

inserte la descripción de la imagen aquí

数据库连接池

inserte la descripción de la imagen aquí

  • 数据库连接池是个容器,负责分配、管理数据库连接(Connection)
  • 它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;
  • 释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏

数据库连接池的实现

  • 官方(SUN)提供的数据库连接池标准接口DataSource,由第三方组织实现此接口,其功能是获取连接 Connection getConnection()
  • 常见的数据库连接池:DBCP、C3PO、Druid
  • 其中Druid(德鲁伊)连接池是阿里巴巴开源的数据库连接池项目,功能强大,性能优秀,是Java语言最好的数据库连接池之一。

Druid连接池

  1. 导入jar包 druid-1.1.12.jar
  2. 定义配置文件
  3. 加载配置文件
  4. 获取数据库连接池对象
  5. 获取连接
 //加载配置文件
 Properties prop = new Properties();
 prop.load(new FileInputStream("C:\Users\Aaa\Desktop\Agust\src\druid.properties"));
 //获取连接池对象
 DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
 //获取数据库连接
 Connection conn = dataSource.getConnection();
 System.out.println(conn);
复制代码

配置文件

 #druid.properties
 driverClassName=com.mysql.cj.jdbc.Driver
 url=jdbc:mysql:///db1?useSSL=false&serverTimezone=Asia/Shanghai
 username=root
 password=123
 #初始化连接数量
 initialSize=5
 #最大连接数
 maxActive=10
 #最大等待时间
 maxWait=3000
复制代码

Druid配置详解

属性 说明 建议值
url 数据库的jdbc连接地址。一般为连接oracle/mysql。示例如下:
mysql : jdbc:mysql://ip:port/dbname?option1&option2&…
oracle : jdbc:oracle:thin:@ip:port:oracle_sid
username 登录数据库的用户名
password 登录数据库的用户密码
initialSize 启动程序时,在连接池中初始化多少个连接 10-50已足够
maxActive 连接池中最多支持多少个活动会话
maxWait 程序向连接池中请求连接时,超过maxWait的值后,认为本次请求失败,即连接池 100
没有可用连接,单位毫秒,设置-1时表示无限等待
minEvictableIdleTimeMillis 池中某个连接的空闲时长达到 N 毫秒后, 连接池在下次检查空闲连接时,将 见说明部分
回收该连接,要小于防火墙超时设置
net.netfilter.nf_conntrack_tcp_timeout_established的设置
timeBetweenEvictionRunsMillis 检查空闲连接的频率,单位毫秒, 非正整数时表示不进行检查
keepAlive 程序没有close连接且空闲时长超过 minEvictableIdleTimeMillis,则会执 true
行validationQuery指定的SQL,以保证该程序连接不会池kill掉,其范围不超
过minIdle指定的连接个数。
minIdle 回收空闲连接时,将保证至少有minIdle个连接. 与initialSize相同
removeAbandoned 要求程序从池中get到连接后, N 秒后必须close,否则druid 会强制回收该 false,当发现程序有未
连接,不管该连接中是活动还是空闲, 以防止进程不会进行close而霸占连接。 正常close连接时设置为true
removeAbandonedTimeout 设置druid 强制回收连接的时限,当程序从池中get到连接开始算起,超过此 应大于业务运行最长时间
值后,druid将强制回收该连接,单位秒。
registroAbandonado Cuando el druida recicla a la fuerza la conexión, ya sea para registrar el seguimiento de la pila en el registro verdadero
prueba mientras está inactivo Cuando un programa solicita una conexión, cuando el grupo asigna una conexión, primero verifica si la conexión es válida. (eficiente) verdadero
consulta de validación instrucción SQL para verificar si la conexión en el grupo aún está disponible, drui se conectará a la base de datos para ejecutar el SQL, si
Si regresa normalmente, significa que la conexión está disponible, de lo contrario, significa que la conexión no está disponible
testOnBorrow Cuando el programa solicita una conexión, se realiza una verificación de validez de la conexión (ineficiente, que afecta el rendimiento) falso
testOnReturn Cuando el programa devuelve la conexión, se realiza la verificación de validez de la conexión (ineficiente, que afecta el rendimiento) falso
la mitad de declaraciones preparadas Cache SQL iniciado por los siguientes dos métodos: verdadero
declaración preparada pública preparar declaración (String sql)
public PreparedStatement prepareStatement(String sql,
int resultSetType, int resultSetConcurrency)
maxPoolPrepareStatementPerConnectionSize Cantidad máxima de SQL para almacenar en caché por conexión 20
filtros Los complementos se configuran aquí. Los complementos comúnmente utilizados son: estadística, pared, slf4j
Supervisión de estadísticas: filter:stat
Supervisión de registro: filtro: log4j o slf4j
Defensa contra la inyección de SQL: filter:wall
conectarPropiedades Propiedades de conexión. Por ejemplo, establecer alguna configuración de estadísticas del grupo de conexiones.
druida.stat.mergeSql=verdadero;druida.stat.slowSqlMillis=5000
Por ejemplo, establezca algunas propiedades de conexión de la base de datos:

Resumen de preguntas frecuentes

El problema ocurre al conectarjava.sql.SQLException: No timezone mapping entry for 'UTC?useSSL=false'

Solución: añádelo al parámetro url?serverTimezone=UTC

 String url = "jdbc:mysql:///db1?serverTimezone=UTC";
复制代码

Supongo que te gusta

Origin juejin.im/post/7085248046157529125
Recomendado
Clasificación