JDBC

JDBC 简介

JDBC 就是使用Java语言操作关系型数据库的一套API, 全称:( Java DataBase Connectivity ) Java 数据库连接

  • 官方(sun公司)定义的一套操作所有关系型数据库的规则,即接口
  • 各个数据库厂商去实现这套接口,提供数据库驱动jar包
  • 我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类
image-20210721185303106

驱动安装

  1. 下载 MySQL 驱动包,解压后得到 jar 库文件:https://downloads.mysql.com/archives/c-j/

  2. 打开 IDE,在对应项目中 configure build path 导入 jar 库文件。

JDBC快速入门

编写代码步骤

public class JDBCDemo {
    public static void main(String[] args) throws Exception {

        // 1.注册驱动(MySQL 5之后的驱动包,可以省略注册驱动的步骤)
         Class.forName("com.mysql.jdbc.Driver");

        // 2.获取连接
        String url = "jdbc:mysql://127.0.0.1:3306/db?useSSL=false";
        String username = "root";
        String password = "tt951008";
        Connection conn = DriverManager.getConnection(url, username, password);

        // 3.定义sql
        String sql = "update  account set money = 2000 where id=1";

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

        // 5.执行sql
        int count = statement.executeUpdate(sql); // 返回受影响的行数

        // 6.处理结果
        System.out.println(count);

        // 7.释放资源
        statement.close();
        conn.close();
    }
}

JDBC API详解

DriverManager

DriverManager(驱动管理类)作用:

  • 注册驱动
// registerDriver方法是用于注册驱动的,但是我们之前做的入门案例并不是这样写的。而是如下实现

Class.forName("com.mysql.jdbc.Driver");


我们查询MySQL提供的Driver类,看它是如何实现的,源码如下:
在该类中的静态代码块中已经执行了 DriverManager 对象的 registerDriver() 方法进行驱动的注册了,那么我们只需要加载 Driver 类,该静态代码块就会执行。而 Class.forName("com.mysql.jdbc.Driver"); 就可以加载 Driver 类。

  • 获取数据库连接
url : 连接路径

    语法:jdbc:mysql://ip地址(域名):端口号/数据库名称?参数键值对1&参数键值对2…

    示例:jdbc:mysql://127.0.0.1:3306/db1

        如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称?参数键值对

        配置 useSSL=false 参数,禁用安全连接方式,解决警告提示

user :用户名

poassword :密码

Connection

Connection(数据库连接对象)作用:

  • 获取执行 SQL 的对象

  • 管理事务

获取执行对象

  • 普通执行SQL对象
Statement createStatement()
  • 预编译SQL的执行SQL对象:防止SQL注入
PreparedStatement  prepareStatement(sql)
  • 执行存储过程的对象
CallableStatement prepareCall(sql)

事务管理

public class JDBCDemo3_Connection {

    public static void main(String[] args) throws Exception {
        //1. 注册驱动
        //Class.forName("com.mysql.jdbc.Driver");
        //2. 获取连接:如果连接的是本机mysql并且端口是默认的 3306 可以简化书写
        String url = "jdbc:mysql:///db1?useSSL=false";
        String username = "root";
        String password = "1234";
        Connection conn = DriverManager.getConnection(url, username, password);
        //3. 定义sql
        String sql1 = "update account set money = 3000 where id = 1";
        String sql2 = "update account set money = 3000 where id = 2";
        //4. 获取执行sql的对象 Statement
        Statement stmt = conn.createStatement();

        try {
            // ============开启事务==========
            conn.setAutoCommit(false); //true表示自动提交事务,false表示手动提交事务。而开启事务需要将该参数设为为false。
            //5. 执行sql
            int count1 = stmt.executeUpdate(sql1);//受影响的行数
            //6. 处理结果
            System.out.println(count1);
            int i = 3/0;
            //5. 执行sql
            int count2 = stmt.executeUpdate(sql2);//受影响的行数
            //6. 处理结果
            System.out.println(count2);

            // ============提交事务==========
            //程序运行到此处,说明没有出现任何问题,则需求提交事务
            conn.commit();
        } catch (Exception e) {
            // ============回滚事务==========
            //程序在出现异常时会执行到这个地方,此时就需要回滚事务
            conn.rollback();
            e.printStackTrace();
        }

        //7. 释放资源
        stmt.close();
        conn.close();
    }
}

Statement

Statement对象的作用就是用来执行SQL语句。而针对不同类型的SQL语句使用的方法也不一样。

  • 执行DDL、DML语句
executeUpdate(sql)
  • 执行DQL语句
executeQuery(sql)

ResultSet

ResultSet(结果集对象)封装了SQL查询语句的结果。

ResultSet对象提供了操作查询结果数据的方法,如下:

boolean  next()

    将光标从当前位置向前移动一行 
    判断当前行是否为有效行

方法返回值说明:

   true  : 有效行,当前行有数据
   false : 无效行,当前行没有数据

xxx  getXxx(参数):获取数据

   xxx : 数据类型;如: int getInt(参数) ;String getString(参数)
   参数
      int类型的参数:列的编号,从1开始
      String类型的参数: 列的名称 

PreparedStatement

PreparedStatement 好处:

    预编译SQL,性能更高

    防止SQL注入:将敏感字符进行转义
  • 获取 PreparedStatement 对象
// SQL语句中的参数值,使用?占位符替代
String sql = "select * from user where username = ? and password = ?";

// 通过Connection对象获取,并传入对应的sql语句
PreparedStatement pstmt = conn.prepareStatement(sql);

// 设置?的值
pstmt.setString(1,name);
pstmt.setString(2,pwd);
  • 设置参数值
上面的sql语句中参数使用 ? 进行占位,在之前之前肯定要设置这些 ?  的值。

PreparedStatement对象:setXxx(参数1,参数2):给 ? 赋值
    Xxx:数据类型 ; 如 setInt (参数1,参数2)
    参数:
        参数1: ?的位置编号,从1 开始
        参数2: ?的值

执行SQL语句
    executeUpdate();  执行DDL语句和DML语句
    executeQuery();  执行DQL语句
    注意:调用这两个方法时不需要传递SQL语句,因为获取SQL语句执行对象时已经对SQL语句进行预编译了

PreparedStatement原理

接下来我们通过查询日志来看一下原理:

  • 开启预编译功能
在代码中编写url时需要加上以下参数。而我们之前根本就没有开启预编译功能,只是解决了SQL注入漏洞。
useServerPrepStmts = true
  • 配置MySQL执行日志(重启mysql服务后生效)
在mysql配置文件(my.ini)中添加如下配置
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
  • java测试代码如下:
@Test
public void testPreparedStatement2() throws  Exception {

    //2. 获取连接:如果连接的是本机mysql并且端口是默认的 3306 可以简化书写
    // useServerPrepStmts=true 参数开启预编译功能
    String url = "jdbc:mysql:///db1?useSSL=false&useServerPrepStmts=true";
    String username = "root";
    String password = "1234";
    Connection conn = DriverManager.getConnection(url, username, password);

    // 接收用户输入 用户名和密码
    String name = "zhangsan";
    String pwd = "' or '1' = '1";

    // 定义sql
    String sql = "select * from tb_user where username = ? and password = ?";

    // 获取pstmt对象
    PreparedStatement pstmt = conn.prepareStatement(sql);

    Thread.sleep(10000);
    // 设置?的值
    pstmt.setString(1,name);
    pstmt.setString(2,pwd);
    ResultSet rs = null;
    // 执行sql
    rs = pstmt.executeQuery();

    // 设置?的值
    pstmt.setString(1,"aaa");
    pstmt.setString(2,"bbb");
    // 执行sql
    rs = pstmt.executeQuery();

    // 判断登录是否成功
    if(rs.next()){
        System.out.println("登录成功~");
    }else{
        System.out.println("登录失败~");
    }

    //7. 释放资源
    rs.close();
    pstmt.close();
    conn.close();
}
  • 执行SQL语句,查看 D:\mysql.log 日志如下:

    上图中第三行中的 Prepare 是对SQL语句进行预编译。第四行和第五行是执行了两次SQL语句,而第二次执行前并没有对SQL进行预编译。

Java代码操作数据库流程如图所示:

将sql语句发送到MySQL服务器端

MySQL服务端会对sql语句进行如下操作

    检查SQL语句

    检查SQL语句的语法是否正确。

    编译SQL语句。将SQL语句编译成可执行的函数。

    检查SQL和编译SQL花费的时间比执行SQL的时间还要长。如果我们只是重新设置参数,那么检查SQL语句和编译SQL语句将不需要重复执行。这样就提高了性能。

    执行SQL语句

数据库连接池

数据库连接池简介

数据库连接池是个容器,负责分配、管理数据库连接(Connection)

它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;

释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏

好处:
    资源重用
    提升系统响应速度
    避免数据库连接遗漏

数据库连接池实现

标准接口: =DataSource
官方(SUN) 提供的数据库连接池标准接口,由第三方组织实现此接口。该接口提供了获取连接的功能:

那么以后就不需要通过 DriverManager 对象获取 Connection对象,而是通过连接池(DataSource)获取 Connection 对象。

Connection getConnection()

常见的数据库连接池,DBCP、C3P0、Druid,我们现在使用更多的是Druid,它的性能比其他两个会好一些。

Druid(德鲁伊)
  Druid连接池是阿里巴巴开源的数据库连接池项目
  功能强大,性能优秀,是Java语言最好的数据库连接池之一

Driud使用

1.导入jar包 druid-1.1.12.jar
2.定义配置文件
3.加载配置文件
4.获取数据库连接池对象
5.获取连接

使用druid的代码如下:

public class DruidDemo {
    @Test
    public void test() throws Exception {
        // 1.导入jar包
        // 2.定义配置文件
        // 3. 加载配置文件
        Properties prop = new Properties();
        prop.load(new FileReader("src/druid.properties"));

        // 4. 获取连接池对象
        DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);


        // 5.获取数据库连接 Connection
        Connection connection = dataSource.getConnection();

        System.out.println(connection);

        // System.out.println(System.getProperty("user.dir")); // 获取当前文件所在路径位置

    }
}

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将强制回收该连接,单位秒。
logAbandoned 当druid强制回收连接后,是否将stack trace 记录到日志中 true
testWhileIdle 当程序请求连接,池在分配连接时,是否先检查该连接是否有效。(高效) true
validationQuery 检查池中的连接是否仍可用的 SQL 语句,drui会连接到数据库执行该SQL, 如果
正常返回,则表示连接可用,否则表示连接不可用
testOnBorrow 程序 申请 连接时,进行连接有效性检查(低效,影响性能) false
testOnReturn 程序 返还 连接时,进行连接有效性检查(低效,影响性能) false
poolPreparedStatements 缓存通过以下两个方法发起的SQL: true
public PreparedStatement prepareStatement(String sql)
public PreparedStatement prepareStatement(String sql,
int resultSetType, int resultSetConcurrency)
maxPoolPrepareStatementPerConnectionSize 每个连接最多缓存多少个SQL 20
filters 这里配置的是插件,常用的插件有: stat,wall,slf4j
监控统计: filter:stat
日志监控: filter:log4j 或者 slf4j
防御SQL注入: filter:wall
connectProperties 连接属性。比如设置一些连接池统计方面的配置。
druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
比如设置一些数据库连接属性:
posted @ 2022-11-21 09:56  晚点心动。  阅读(115)  评论(0)    收藏  举报