优秀的编程知识分享平台

网站首页 > 技术文章 正文

JDBC和连接池(jdbc连接池满了怎么排查)

nanyue 2024-09-06 20:25:49 技术文章 4 ℃

JDBC是什么?

Java DataBase Connectivity(Java语言连接数据库)

JDBC的本质是什么?

  1. JDBC是Sun公司提供的一套专门用于Java语言和数据库进行链接的API(Application Programma Interface应用程序编程接口)
  2. java.sql.*;(这个软件包下有很多接口)
  3. 接口都有调用者和实现者。面向接口调用、面向接口写实现类,这都属于面向接口编程。

为什么使用JDBC?

Sun公司为了避免Java程序员,每一种数据库软件都学习一套全新的方法,通过JDBC接口将方法名定义好, 让各个数据库厂商根据此接口中的方法名写各自的实现类(就是一个jar文件, 称为数据库的驱动) ,这样Java程序员只需要掌握JDBC接口中方法的调用,即可访问任何数据库软件.


JDBC用途

  1. 与数据库建立连接
  2. 发送 SQL 语句
  3. 处理结果

JDBC接口的本质


为什么要面向接口编程?

//解耦合:降低程序的耦合度,提高程序的扩展力。
//多态机制就是非常典型的:面向抽象编程。(不要面向具体编程)
//		建议:
    Animal a = new Cat();
    Animal a = new Dog();
    // 方法
    public void feed(Animal a){ // 面向父类型编程。
    }
//不建议:
    Dog d = new Dog();
    Cat c = new Cat();

JDBC编程六步

  • 第一步:注册驱动(作用:告诉Java,即将连接的是哪个品牌的数据库)IDEA忽略
  • 第二步:获取连接(表示JVM的进程和数据库进程之间的通道打开了,这属于进程之间的通信,重量级的,使用完之后一定要关闭通道。)
  • 第三步:获取数据库操作对象(专门执行sql语句的对象)
  • 第四步:执行SQL语句(DQL DML…)
  • 第五步:处理查询结果集(只有当第四步执行的是select语句的时候,才有这第五步处理查询结果集。)
  • 第六步:释放资源(使用完资源之后一定要关闭资源。Java和数据库属于进程之间的通信,开启之后一定要关闭。)
try {
  // 1、注册驱动
  Class.forName(driver);
  // 2、获取连接
  conn = DriverManager.getConnection(url,user,password);
  // 3、获取数据库操作对象
  stmt = conn.createStatement();
  // 4、执行sql语句
  String sql = "update dept set dname = '销售部',loc = '天津' where deptno = 20";
  int connt = stmt.executeUpdate(sql); 
  System.out.println(count == 1? "修改成功":"修改失败");
  // 5、获取查询结果集
} catch(Exception e){
  e.printStackTrace();
} finally {
  // 6、释放资源
  if(conn != null) {
    try {
      conn.close();
    } catch(SQLException e){
      e.printStackTrace();
    }
  }
  if(stmt != null) {
    try {
      stmt.close();
    } catch(SQLException e){
      e.printStackTrace();
    }
	}
}

IDEA 使用JDBC操作数据库

  • 1).pom.xml文件安装数据库依赖
<!-- 连接MySQL数据库的依赖 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.15</version>
</dependency>
  • 2).JDBC代码
  1. execute("sql"); 此方法可以执行任意SQL语句, 但建议执行DDL(数据库相关和表相关的SQL语句)
  2. int row = executeUpdate("sql"); 此方法用来执行增删改相关的SQL语句, 返回值表示生效的行数
  3. ResultSet rs = executeQuery("sql"); 此方法用来执行查询相关的SQL语句,返回值ResultSet叫做结果集对象,查询到的数据都装在此对象中
try{
  //1.获取链接
  Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/emp?
         charsetEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false","root","root");
   //2.获取数据库操作对象
  Statement s = coon.createStatement();
  //3执行sql
   s.executeUpdate("insert into hero (name,money) values ('"+name+"',"+money+")");
  //4.关闭资源
 conn.close();
 } catch (SQLException throwables) {
   throwables.printStackTrace();
 }

ResultSet里的数据一行一行排列,每行有多个字段,且有一个记录指针,指针所指的数据行叫做当前数据行,我们只能来操作当前的数据行。我们如果想要取得某一条记录,就要使用ResultSet的next()方法 ,如果我们想要得到ResultSet里的所有记录,就应该使用while循环。

JDBC中使用事务

(1)在JDBC中,事务操作缺省是自动提交。

  1. 一条对数据库的DML(insert、update、delete)代表一项事务操作
  2. 操作成功后,系统将自动调用commit()提交,否则自动调用rollback()回滚

(2)在JDBC中,事务操作方法都位于接口java.sql.Connection中

  1. 可以通过调用setAutoCommit(false)来禁止自动提交。
  2. 之后就可以把多个数据库操作的表达式作为一个事务,在操作完成后调用commit()来进行整体提交,
  3. 倘若其中一个表达式操作失败,都不会执行到commit(),并且将产生响应的异常;此时就可以在异常捕获时调用rollback()进行回滚,回复至数据初始状态

(3)事务开始的边界则不是那么明显了,它会开始于组成当前事务的所有statement中的第一个被执行的时候。

(4)事务结束的边界是commit或者rollback方法的调用

Connection connect = null;
String sql = "update ACCOUNT set balance=balance-100 where id=2";
String sql2 = "update ACCOUNT set balance=balance+100 where id=1";
PreparedStatement preparedStatement = null;
try {
  //1.得到连接
  connect = JDBCUtils.connect();
  connect.setAutoCommit(false);   //关闭自动提交
  //2.执行语句
  preparedStatement = connect.prepareStatement(sql);
  preparedStatement.executeUpdate();
  
  preparedStatement = connect.prepareStatement(sql2);
  preparedStatement.executeUpdate();

  //这里提交事务
  connect.commit();
  
} catch (Exception e) {
  //这里可以进行回滚,即撤销执行的sql语句
  //默认回滚到事务开始状态
  System.out.println("执行发生异常,撤销执行的sql");
  try {
    connect.rollback();
  } catch (SQLException throwables) {
    throwables.printStackTrace();
  }
  e.printStackTrace();
}finally {
  //调用工具类关闭资源
  JDBCUtils.close(null,preparedStatement,connect);
}

数据库连接池DBCP DataBaseConnectionPool数据库连接池)

作用: 将连接重用,从而提高执行效率

为什么采用连接池?

数据库连接的建立及关闭是耗费系统资源的操作,在大型应用中对系统的性能影响尤为明显。为了能重复利用数据库连接对象,缩短请求的响应时间和提高服务器的性能,支持更多的客户,应采用连接池技术。

如何使用?

  • 1)pom.xml配置依赖(阿里的为例)
<!-- 数据库连接池 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.21</version>
</dependency>
  • 2)代码
//创建链接对象
DruidDataSource ds = new DruidDataSource();
//设置链接信息
ds.setUrl("jdbc:mysql://localhost:3306/emp?charsetEncoding=utf8&serverTimezone=Asia/Shanghai");
ds.setUsername("root");
ds.setPassword("root");
//设置初始链接数
ds.setInitialSize(3);
//设置最大链接数
ds.setMaxActive(5);
//获取链接
Connection conn = ds.getConnection();

演示SQL注入现象

  1. 当前程序出现的问题
  2. 用户名:fdsa
  3. 密码:fdsa ’ or ‘1’='1
  4. 登录成功 这种现象被称为SQL注入(安全隐患),(黑客经常使用)
// fdsa ’ or ‘1’='1
String  sql = "select * from user where Name = '"+loginName+"' and Pwd = '"+loginPwd+"'";
 //以上正好完成了SQL语句的拼接,以下代码的含义是,发送SQL语句给DBMS,DBMS进行SQL编译
 //正好将用户提供的“非法信息”编译进去,导致原SQL语句的含义被扭曲了。   
resultSet = statement.executeQuery(sql);

用户输入的信息中含有SQL语句的关键字,并且这些关键字参与SQL语句的编译过程【参与编译了这是最关键的原因】

解决SQL注入问题(sql中存在变量则必须防止sql注入)

  1. 只要用户提供的信息不参与SQL语句的编译过程,问题就解决了
  2. 即使用户提供的信息中含有SQL语句的关键字,但是没有参与编译,不起作用
  3. 要想用户信息不参与SQL语句的编译,那么必须使用java.sql.preparedStatement
  4. PreparedStatement接口继承了java.sql.Statement
  5. PreparedStatement是属于预编译的数据库操作对象。
  6. PreparedStatement的原理 是,预先对SQL语句的框架进行编译,然后再给SQL语句传“值”。

【注】编译SQL语句的时间点从之前执行时,提前到了创建对象时, 好处是此时编译用户输入的内容还不在SQL语句里面,只是将原有SQL语句进行编译, 此时可以将原有SQL语句的逻辑部分锁死

//SQL语句的框子。其中一个?,表示一个占位符,一个?将来接收一个“值”,
//【注意】,占位符不能使用单引号括起来。
String  sql = "select * from user where Name = ? and Pwd = ?";
//程序执行到此处,会发送SQL语句的框子给DBMS,然后DBMS进行SQL语句的预先编译。
ps = connection.prepareStatement(sql);
//给占位符?传值(第1个问号下标是1,第2个问号下标是2,JDBC中所有下标从1开始)
ps.setString(1,loginName);
ps.setString(2,loginPwd);
//4.执行SQL
resultSet = ps.executeQuery();

Demo综合案例(登录)

//DB工具类
public class DBUtils {
    private static DruidDataSource ds;
    static {
        //创建连接池对象
        ds = new DruidDataSource();
        //设置数据库连接信息 url 用户名 密码
        ds.setUrl("jdbc:mysql://localhost:3306/emp?characterEncoding=utf8
                  &serverTimezone=Asia/Shanghai&useSSL=false");
        ds.setUsername("root");
        ds.setPassword("root");
        //设置初始连接数量
        ds.setInitialSize(3);
        //设置最大连接数量
        ds.setMaxActive(5);
    }
    public static Connection getConn() throws SQLException {
        //从连接池中获取连接   异常抛出
        Connection conn = ds.getConnection();
        System.out.println("连接对象:"+conn);
        return conn;
    }
}
//登录实现
public static void main(String[] args) {
  Scanner sc = new Scanner(System.in);
  System.out.println("请输入用户名:");
  String username = sc.nextLine();
  System.out.println("请输入密码");
  String password =sc.nextLine();
  //连接,使用工具类
  try(Connection coon = DBUtils.getConn()) {
    Statement s = coon.createStatement();
    //有sql注入风险
 //  ResultSet rs =s.executeQuery("select username,password from user where username ='"+username+"'");
   
    //使用PreparedStatement 防止sql注入
    String sql = "select username,password from user where username =?";
    PreparedStatement ps = coon.prepareStatement(sql);
    ps.setString(1,username);
    ResultSet rs = ps.executeQuery();
    //没有查到,代表账号不存在
    if(!rs.next()){
      System.out.println("用户名不存在!");
      return;
    }
    //判断密码是否正确
    if(password.equals(rs.getString("password"))){
      System.out.println("恭喜你,登录成功!");
      return;
    }
    System.out.println("密码错误,登录失败");
  } catch (SQLException throwables) {
    throwables.printStackTrace();
  }
}

JDBC API总结

  • 1、Connection接口

作用:代表数据库连接

  • 2、DriverManager类

作用:管理一组 JDBC 驱动程序的基本服务

应用程序不再需要使用 Class.forName() 显式地加载 JDBC 驱动程序。在调用 getConnection 方法时,DriverManager 会试着从初始化时加载的那些驱动程序以及使用与当前 applet 或应用程序相同的类加载器显式加载的那些驱动程序中查找合适的驱动程序

  • 3、Statement接口

作用:用于将 SQL 语句发送到数据库中,或理解为执行sql语句

  1. Statement:用于执行不带参数的简单SQL语句;
  2. PreparedStatement 从 Statement 继承:用于执行带或不带参数的预编译SQL语句;
  3. CallableStatement 从PreparedStatement 继承:用于执行数据库存储过程的调用。
  • 4、PreparedStatement接口

关系:public interface PreparedStatement extends Statement

  1. PreparedStatment安全性高,可以避免SQL注入
  2. PreparedStatment简单不繁琐,不用进行字符串拼接
  3. PreparedStatment性能高,用在执行多个相同数据库DML操作时

5、ResultSet接口

  1. ResultSet对象是executeQuery()方法的返回值,它被称为结果集,它代表符合SQL语句条件的所有行,并且它通过一套getXXX方法(这些get方法可以访问当前行中的不同列)提供了对这些行中数据的访问。
  2. ResultSet里的数据一行一行排列,每行有多个字段,且有一个记录指针,指针所指的数据行叫做当前数据行,我们只能来操作当前的数据行。我们如果想要取得某一条记录,就要使用ResultSet的next()方法 ,如果我们想要得到ResultSet里的所有记录,就应该使用while循环。
  3. ResultSet对象自动维护指向当前数据行的游标。每调用一次next()方法,游标向下移动一行。
  4. 初始状态下记录指针指向第一条记录的前面,通过next()方法指向第一条记录。循环完毕后指向最后一条记录的后面。

学习记录,如有侵权请联系删除。

最近发表
标签列表