JDBC-进阶篇

尚硅谷JDBC数据库连接技术2024版

进阶篇

7.JDBC拓展

7.1.实体类与ORM

  • 在使用JDBC操作数据库是,我们会发现数据都是零散的,明明数据库中是一行完整的数据,到了Java中变成了一个一个变量,不利于维护与管理,而我们Java是面向对象的,一个表对应的是一个类,一行数据就对应的是Java中的一个对象,一个列对应的是对象中的一个属性,所以我们要把数据存储到一个载体中,这个载体就是实体类。
  • ORM(object relational mapping)思想,对象到关系数据库的映射,作用是在编程中,把面向对象的概念跟数据库中表的概念对应起来,以面向对象的角度操作数据库中的数据,即一张表对应一个类,一行数据对应一个对象,一个列对应一个属性。
  • 当下JDBC中国这种过程我们 称之为手动ORM,后续我们也会学习ORM框架,比如MyBatis,JPA等。
package com.yan.advenced.pojo;

//类名就是t_后面的全写
public class Employee {
    private Integer id;
    private String name;
    private String salary;
    private Integer age;

    //构造方法与get,set方法
}

7.2.封装单个对象

package com.yan.advenced;

import com.yan.advenced.pojo.Employee;
import org.junit.Test;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class JDBCAdvanced {
    @Test
    public void testORM()throws Exception{
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/stguigu", "root", "123456");
        PreparedStatement preparedStatement = connection.prepareStatement("SELECT emp_id, emp_name,emp_salary, emp_age FROM t_emp where emp_id = ?");
        preparedStatement.setInt(1, 1);
        ResultSet resultSet = preparedStatement.executeQuery();
        Employee employee = null;
        if (resultSet.next()){
            employee = new Employee();
            int id = resultSet.getInt(1);
            String name = resultSet.getString(2);
            double salary = resultSet.getDouble(3);
            int age = resultSet.getInt(4);
            //为对象的属性赋值:
            employee.setId(id);
            employee.setName(name);
            employee.setSalary(salary);
            employee.setAge(age);
        }
        System.out.println(employee);
        resultSet.close();
        preparedStatement.close();
        connection.close();
    }
}

7.3.封装多个对象

@Test
    public void testORMList()throws Exception{
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/stguigu", "root", "123456");
        PreparedStatement preparedStatement = connection.prepareStatement("SELECT emp_id, emp_name,emp_salary, emp_age FROM t_emp");
        ResultSet resultSet = preparedStatement.executeQuery();
        Employee employee = null;
        List<Employee> employees = new ArrayList<>();
        while (resultSet.next()){
            employee = new Employee();

            int id = resultSet.getInt(1);
            String name = resultSet.getString(2);
            double salary = resultSet.getDouble(3);
            int age = resultSet.getInt(4);

            //为对象的属性赋值:
            employee.setId(id);
            employee.setName(name);
            employee.setSalary(salary);
            employee.setAge(age);

            employees.add(employee);
        }
        for (Employee e : employees) {
            System.out.println(e);
        }
        resultSet.close();
        preparedStatement.close();
        connection.close();
    }

7.4.主键回显

  • 在数据中执行新增操作时,主键列为自动增长,可以在表中直观的看到,但是在Java中,我们执行完新增后,只能得到受影响的行数,无法得知当前新增的主键值,在Java程序中获取数据库中插入新数据后的主键值,并赋值给java对象,此操作为主键回显。

  • 代码实现:

    @Test
        public void testReturnPK() throws Exception {
            Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/stguigu", "root", "123456");
    
            String sql = "INSERT INTO t_emp(emp_name,emp_salary,emp_age) VALUES(?,?,?)";
            PreparedStatement preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
    
            Employee employee = new Employee(null, "jack", 123.45, 29);
            preparedStatement.setString(1, employee.getName());
            preparedStatement.setDouble(2, employee.getSalary());
            preparedStatement.setInt(3, employee.getAge());
    
            int result = preparedStatement.executeUpdate();
            ResultSet resultSet = null;
    
            if (result > 0) {
                System.out.println("插入成功");
                //获取插入数据的id:
                resultSet = preparedStatement.getGeneratedKeys();
                if (resultSet.next()) {
                    int id = resultSet.getInt(1);
                    employee.setId(id);
                }
                System.out.println(employee);
            }
            else {
                System.out.println("插入失败");
            }
            
            if (resultSet != null) {
                resultSet.close();
            }
            preparedStatement.close();
            connection.close();
        }
    

7.5.批量操作

  • 插入多条数据时,一条一条发送给数据库执行,效率低下。
  • 通过批量操作,可以提升多次操作效率。

代码实现:

@Test
    public void testMoreInsert()throws Exception{
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/stguigu", "root", "123456");

        String sql = "INSERT INTO t_emp(emp_name, emp_salary, emp_age) VALUES(?,?,?)";

        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        //获取执行时间
        long start = System.currentTimeMillis();
        for (int i = 0; i < 10000; i++) {

            preparedStatement.setString(1, "marry" + i);
            preparedStatement.setDouble(2, 1000 + i);
            preparedStatement.setInt(3, 20 + i);

            preparedStatement.executeUpdate();
        }
        long end = System.currentTimeMillis();
        System.out.println("执行时间:" + (end - start));

        preparedStatement.close();
        connection.close();
    }

改进后

@Test
    public void testBatch()throws Exception{
        Connection connection = DriverManager.getConnection("jdbc:mysql:///stguigu?rewriteBatchedStatements=true", "root", "123456");

        /*
        注意一:必须在连接数据库的URL后面追加rewriteBatchedStatements=true
        注意二:新增sql是必须使用values,且语句后面不追加;
        注意三:调用addBatch方法时.
        注意四:调用executeBatch方法时.
        注意五:调用clearBatch方法时.
         */
        String sql = "INSERT INTO t_emp(emp_name, emp_salary, emp_age) VALUES(?,?,?)";

        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        //获取执行时间
        long start = System.currentTimeMillis();
        for (int i = 0; i < 10000; i++) {

            preparedStatement.setString(1, "marry" + i);
            preparedStatement.setDouble(2, 1000 + i);
            preparedStatement.setInt(3, 20 + i);

            preparedStatement.addBatch();
        }
        //执行批量操作
        preparedStatement.executeBatch();
        long end = System.currentTimeMillis();
        System.out.println("执行时间:" + (end - start));

        preparedStatement.close();
        connection.close();
    }

改进前运行时间约为改进后的100倍!

8.连接池

8.1.现有问题

  • 每一次操作数据库都要获取新的连接,使用完毕后就close释放,频繁的销毁和创建造成资源的浪费。
  • 连接的数量无法把控,对于服务器来说压力巨大。

8.2.连接池

连接池就是数据库连接对象的缓冲区,通过配置,由连接池负责创建连接,管理连接,释放连接等操作。

预先创建数据库放入连接池,用户在请求时,通过池直接获取连接,使用完毕后,将连接放回池中,避免了频繁的创建及销毁,同时解决了创建的效率。

当池中无连接可用,且未达到上限时,连接池会新建连接。

池中连接到达上限时,用户会请求等待,可以设置超时时间。

8.3.常见的连接池

JDBC的数据库连接池使用java.sql.DataSource接口进行规范,所有的第三方接口都实现此接口,自行添加具体实现!也就是说,所有连接池获取的和收回连接方法都一样,不同的只有性能和拓展功能。

  • DBCP是Apache提供的数据库连接池,速度相对与C3P0较快,但存在一些BUG。
  • C3P0是一个开源组织提供的数据库连接池,速度相对较慢,稳定性还可以。
  • Proxool是sourceforge下的一个开源项目数据库连接池,由监控连接池状态的功能,稳定性较C3P0差一点。
  • Druid是阿里提供的数据库连接池,是集DBCP,C3P0,Proxool于一体的数据库连接池,性能,拓展性,易用性都更好,功能丰富。
  • Hikari是SpringBoot2.x之后的内置的一款连接池,基于BoneCP(已经放弃维护,但推荐使用)做了不少的优化于改进,口号是快速简单可靠。

8.4.Druid连接池的使用

  • 使用步骤

    • 引入jar包
    • 编码
  • 代码实现:

    • 硬编码方式(了解)
    @Test
        public void testHardCode() throws Exception {
            /*
            硬编码:将数据是配置信息和Java代码耦合在一起
            1.创建DruidDataSource连接池对象
            2.设置连接池的配置信息
            3.通过连接池获取连接对象
            4.回收连接
             */
    
            //1.创建DruidDataSource连接池对象
            DruidDataSource dataSource = new DruidDataSource();
    
            //2.设置连接池的配置信息
            dataSource.setDriverClassName("com.mysql.jdbc.Driver");
            dataSource.setUrl("jdbc:mysql://localhost:3306/stguigu");
            dataSource.setUsername("root");
            dataSource.setPassword("123456");
            //非必须:
            dataSource.setInitialSize(10);
            dataSource.setMaxActive(20);
            //3.通过连接池获取连接对象
            Connection connection = dataSource.getConnection();
    
            //基于connection进行CRUD
    
            //4.回收连接
            connection.close();
        }
    
    • 软编码方式(推荐)

      • 在项目的目录下创建resources文件夹,标记该文件夹为资源目录,创建db.properties配置文件,将连接信息 定义在该文件中。

      •   # druid连接池需要的配置参数,key固定命名
          driverClassName=com.mysql.jdbc.Driver
          url=jdbc:mysql:///stguigu
          username=root
          password=123456
          initialSize=10
          maxActive=20
        
      • Java代码:

      •   @Test
              public void testResource() throws Exception {
                  //1.创建properties集合,用于存储外部配置文件
                  Properties properties = new Properties();
          
                  //2.读取配置文件,获取输入流
                  InputStream inputStream = DruidTest.class.getClassLoader().getResourceAsStream("db.properties");
                  properties.load(inputStream);
          
                  //3.基于Properties集合构建连接池
                  DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
          
                  //4.基于连接池获取连接对象
                  Connection connection = dataSource.getConnection();
                  System.out.println(connection);
                  //5.基于connection进行CRUD
          
                  //6.回收连接
                  connection.close();
              }
        

8.5.HikariCP连接池使用

  • 使用步骤

    • 引入jar包

    • 硬编码方式

      package com.yan.advenced.pool;
      
      import com.zaxxer.hikari.HikariDataSource;
      import org.junit.Test;
      
      import java.sql.Connection;
      
      public class HikariTest {
          @Test
          public void testHardCodeHikari() throws Exception{
              /*
              硬编码
              1.创建HikariDataSource连接池对象
              2.设置连接池的配置信息【必要|非必要】
              3.通过连接池获取连接对象
              4.回收连接
               */
      
              //1.创建HikariDataSource连接池对象
              HikariDataSource dataSource = new HikariDataSource();
      
              //2.设置连接池的配置信息【必要|非必要】
              dataSource.setDriverClassName("com.mysql.jdbc.Driver");
              dataSource.setJdbcUrl("jdbc:mysql:///stguigu");
              dataSource.setUsername("root");
              dataSource.setPassword("123456");
      
              dataSource.setMinimumIdle(10);
              dataSource.setMaximumPoolSize(20);
      
              //3.通过连接池获取连接对象
              Connection connection = dataSource.getConnection();
              System.out.println(connection);
      
              //4.回收连接
              connection.close();
          }
      }
      
      

posted @ 2024-05-09 18:12  YJQING  阅读(20)  评论(0)    收藏  举报