二.JDBC--进阶篇

七、JDBC扩展

7.1 实体类和ORM
  • 在使用JDBC操作数据库时,我们会发现数据都是零散的,明明在数据库中是一行完整的数据,到了Java中变成了一个一个的变量,不利于维护和管理。而我们Java是面向对象的,一个表对应的是一个类,一行数据就对应的是Java中的一个对象,一个列对应的是对象的属性,所以我们要把数据存储在一个载体里,这个载体就是实体类!

  • ORM(Object Relational Mapping)思想,对象到关系数据库的映射,作用是在编程中,把面向对象的概念跟数据库中表的概念对应起来,以面向对象的角度操作数据库中的数据,即一张表对应一个类,一行数据对应一个对象,一个列对应一个属性!

  • 当下JDBC中这种过程我们称其为手动ORM。后续我们也会学习ORM框架,比如MyBatis、JPA等。

package com.atguigu.pojo;
//类名和数据库名对应,但是表名一般缩写,类名要全写!
public class Employee {
    private Integer empId;//emp_id = empId 数据库中列名用下划线分隔,属性名用驼峰!
    private String empName;//emp_name = empName
    private Double empSalary;//emp_salary = empSalary
    private Integer empAge;//emp_age = empAge
​
    //省略get、set、无参、有参、toString方法。
}

封装代码:

    @Test
    public void querySingleRow() throws SQLException {
        //1.注册驱动
//        Class.forName("com.mysql.cj.jdbc.Driver");
​
        //2.获取数据库连接
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/atguigu", "root","atguigu");
​
        //3.创建PreparedStatement对象,并预编译SQL语句,使用?占位符
        PreparedStatement preparedStatement = connection.prepareStatement("select emp_id,emp_name,emp_salary,emp_age from t_emp where emp_id = ?");
​
        //4.为占位符赋值,索引从1开始,执行SQL语句,获取结果
        preparedStatement.setInt(1, 1);
        ResultSet resultSet = preparedStatement.executeQuery();
          //预先创建实体类变量
        Employee employee = null;
        //5.处理结果
        while (resultSet.next()) {
            int empId = resultSet.getInt("emp_id");
            String empName = resultSet.getString("emp_name");
            Double empSalary = Double.valueOf(resultSet.getString("emp_salary"));
            int empAge = resultSet.getInt("emp_age");
            //当结果集中有数据,再进行对象的创建
            employee = new Employee(empId,empName,empSalary,empAge);
        }
​
        System.out.println("employee = " + employee);
​
        //6.释放资源(先开后关原则)
        resultSet.close();
        preparedStatement.close();
        connection.close();
​
    }

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

  • 代码实现:

    •     @Test
         public void testReturnPK() throws SQLException {
             //1.注册驱动
      //        Class.forName("com.mysql.cj.jdbc.Driver");
      ​
              //2.获取数据库连接
              Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/atguigu", "root", "atguigu");
      ​
              //3.创建preparedStatement对象,传入需要主键回显参数Statement.RETURN_GENERATED_KEYS
              PreparedStatement preparedStatement = connection.prepareStatement("insert into t_emp (emp_name, emp_salary, emp_age)values  (?, ?,?)",Statement.RETURN_GENERATED_KEYS);
      ​
              //4.编写SQL语句并执行,获取结果
              Employee employee = new Employee(null,"rose",666.66,28);
              preparedStatement.setString(1,employee.getEmpName());
              preparedStatement.setDouble(2,employee.getEmpSalary());
              preparedStatement.setDouble(3,employee.getEmpAge());
              int result = preparedStatement.executeUpdate();
      ​
              //5.处理结果
              if(result>0){
                  System.out.println("添加成功");
              }else{
                  System.out.println("添加失败");
              }
      ​
              //6.获取生成的主键列值,返回的是resultSet,在结果集中获取主键列值
              ResultSet resultSet = preparedStatement.getGeneratedKeys();
              if (resultSet.next()){
                  int empId = resultSet.getInt(1);
                  employee.setEmpId(empId);
              }
              
              System.out.println(employee.toString());
             
              //7.释放资源(先开后关原则)
              resultSet.close();
              preparedStatement.close();
              connection.close();
      ​
          }

7.3 批量操作
  • 插入多条数据时,一条一条发送给数据库执行,效率低下!

  • 通过批量操作,可以提升多次操作效率!

  • 代码实现:

    •     @Test
          public void testBatch() throws Exception {
              //1.注册驱动
      //        Class.forName("com.mysql.cj.jdbc.Driver");
      ​
              //2.获取连接
              Connection connection = DriverManager.getConnection("jdbc:mysql:///atguigu?rewriteBatchedStatements=true", "root", "atguigu");
      ​
              //3.编写SQL语句
              /*
                  注意:1、必须在连接数据库的URL后面追加?rewriteBatchedStatements=true,允许批量操作
                      2、新增SQL必须用values。且语句最后不要追加;结束
                      3、调用addBatch()方法,将SQL语句进行批量添加操作
                      4、统一执行批量操作,调用executeBatch()
               */
              String sql = "insert into t_emp (emp_name,emp_salary,emp_age) values (?,?,?)";
      ​
              //4.创建预编译的PreparedStatement,传入SQL语句
              PreparedStatement preparedStatement = connection.prepareStatement(sql);
      ​
              //获取当前行代码执行的时间。毫秒值
              long start = System.currentTimeMillis();
              for(int i = 0;i<10000;i++){
                  //5.为占位符赋值
                  preparedStatement.setString(1, "marry"+i);
                  preparedStatement.setDouble(2, 100.0+i);
                  preparedStatement.setInt(3, 20+i);
      ​
                  preparedStatement.addBatch();
              }
      ​
              //执行批量操作
              preparedStatement.executeBatch();
      ​
              long end = System.currentTimeMillis();
      ​
              System.out.println("消耗时间:"+(end - start));
      ​
              preparedStatement.close();
              connection.close();
          }

八、连接池

8.1 现有问题
  • 每次操作数据库都要获取新连接,使用完毕后就close释放,频繁的创建和销毁造成资源浪费。

  • 连接的数量无法把控,对服务器来说压力巨大。

8.2 连接池

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

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

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

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

8.3 常见连接池

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

  • DBCP 是Apache提供的数据库连接池,速度相对C3P0较快,但自身存在一些BUG。

  • C3P0 是一个开源组织提供的一个数据库连接池,速度相对较慢,稳定性还可以。

  • Proxool 是sourceforge下的一个开源项目数据库连接池,有监控连接池状态的功能, 稳定性较c3p0差一点

  • Druid 是阿里提供的数据库连接池,是集DBCP 、C3P0 、Proxool 优点于一身的数据库连接池,性能、扩展性、易用性都更好,功能丰富

  • Hikari(ひかり[shi ga li]) 取自日语,是光的意思,是SpringBoot2.x之后内置的一款连接池,基于 BoneCP (已经放弃维护,推荐该连接池)做了不少的改进和优化,口号是快速、简单、可靠。

8.4 Druid连接池使用
  • 使用步骤:

    • 引入jar包。

    • 编码。

  • 代码实现:

    • 硬编码方式(了解):

      • @Test
            public void testHardCodeDruid() throws SQLException {
                /*
                    硬编码:将连接池的配置信息和Java代码耦合在一起。
                    1、创建DruidDataSource连接池对象。
                    2、设置连接池的配置信息【必须 | 非必须】
                    3、通过连接池获取连接对象
                    4、回收连接【不是释放连接,而是将连接归还给连接池,给其他线程进行复用】
                 */
        ​
                //1.创建DruidDataSource连接池对象。
                DruidDataSource druidDataSource = new DruidDataSource();
        ​
                //2.设置连接池的配置信息【必须 | 非必须】
                //2.1 必须设置的配置
                druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
                druidDataSource.setUrl("jdbc:mysql:///atguigu");
                druidDataSource.setUsername("root");
                druidDataSource.setPassword("atguigu");
        ​
                //2.2 非必须设置的配置
                druidDataSource.setInitialSize(10);
                druidDataSource.setMaxActive(20);
                
                //3.通过连接池获取连接对象
                Connection connection = druidDataSource.getConnection();
                System.out.println(connection);
        ​
                //基于connection进行CRUD
        ​
                //4.回收连接
                connection.close();
            }
    • 软编码方式(推荐):

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

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

        • @Test
              public void testResourcesDruid() throws Exception {
                  //1.创建Properties集合,用于存储外部配置文件的key和value值。
                  Properties properties = new Properties();
          ​
                  //2.读取外部配置文件,获取输入流,加载到Properties集合里。
                  InputStream inputStream = DruidTest.class.getClassLoader().getResourceAsStream("db.properties");
                  properties.load(inputStream);
          ​
                  //3.基于Properties集合构建DruidDataSource连接池
                  DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
          ​
                  //4.通过连接池获取连接对象
                  Connection connection = dataSource.getConnection();
                  System.out.println(connection);
          ​
                  //5.开发CRUD
          ​
                  //6.回收连接
                  connection.close();
              }

8.5 Druid其他配置【了解】
配置缺省说明
name配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来。 如果没有配置,将会生成一个名字,格式是:”DataSource-” + System.identityHashCode(this)
jdbcUrl连接数据库的url,不同数据库不一样。例如:mysql : jdbc:mysql://10.20.153.104:3306/druid2 oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto
username连接数据库的用户名
password连接数据库的密码。如果你不希望密码直接写在配置文件中,可以使用ConfigFilter。详细看这里:使用ConfigFilter · alibaba/druid Wiki · GitHub
driverClassName根据url自动识别 这一项可配可不配,如果不配置druid会根据url自动识别dbType,然后选择相应的driverClassName(建议配置下)
initialSize0初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时
maxActive8最大连接池数量
maxIdle8已经不再使用,配置了也没效果
minIdle最小连接池数量
maxWait获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
poolPreparedStatementsfalse是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
maxOpenPreparedStatements-1要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
validationQuery用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用。
testOnBorrowtrue申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
testOnReturnfalse归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
testWhileIdlefalse建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
timeBetweenEvictionRunsMillis有两个含义: 1)Destroy线程会检测连接的间隔时间2)testWhileIdle的判断依据,详细看testWhileIdle属性的说明
numTestsPerEvictionRun不再使用,一个DruidDataSource只支持一个EvictionRun
minEvictableIdleTimeMillis
connectionInitSqls物理连接初始化的时候执行的sql
exceptionSorter根据dbType自动识别 当数据库抛出一些不可恢复的异常时,抛弃连接
filters属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: 监控统计用的filter:stat日志用的filter:log4j防御sql注入的filter:wall
proxyFilters类型是List,如果同时配置了filters和proxyFilters,是组合关系,并非替换关系
8.6 HikariCP连接池使用
  • 使用步骤:

    • 引入jar包

    • 硬编码方式:

      • @Test
        public void testHardCodeHikari() throws SQLException {
            /*
             硬编码:将连接池的配置信息和Java代码耦合在一起。
             1、创建HikariDataSource连接池对象
             2、设置连接池的配置信息【必须 | 非必须】
             3、通过连接池获取连接对象
             4、回收连接
             */
            //1.创建HikariDataSource连接池对象
            HikariDataSource hikariDataSource = new HikariDataSource();
        ​
            //2.设置连接池的配置信息【必须 | 非必须】
            //2.1必须设置的配置
            hikariDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
            hikariDataSource.setJdbcUrl("jdbc:mysql:///atguigu");
            hikariDataSource.setUsername("root");
            hikariDataSource.setPassword("atguigu");
        ​
            //2.2 非必须设置的配置
            hikariDataSource.setMinimumIdle(10);
            hikariDataSource.setMaximumPoolSize(20);
        ​
            //3.通过连接池获取连接对象
            Connection connection = hikariDataSource.getConnection();
        ​
            System.out.println(connection);
        ​
            //回收连接
            connection.close();
        }
    • 软编码方式:

      • 在项目下创建resources/hikari.properties配置文件

        • driverClassName=com.mysql.cj.jdbc.Driver
          jdbcUrl=jdbc:mysql:///atguigu
          username=root
          password=atguigu
          minimumIdle=10
          maximumPoolSize=20
      • 编写代码:

        •     @Test
              public void testResourcesHikari()throws Exception{
                   //1.创建Properties集合,用于存储外部配置文件的key和value值。
                  Properties properties = new Properties();
                  
                  //2.读取外部配置文件,获取输入流,加载到Properties集合里。
                  InputStream inputStream = HikariTest.class.getClassLoader().getResourceAsStream("db.properties");
                  properties.load(inputStream);
                  
                  // 3.创建Hikari连接池配置对象,将Properties集合传进去
                  HikariConfig hikariConfig = new HikariConfig(properties);
                  
                  // 4. 基于Hikari配置对象,构建连接池
                  HikariDataSource hikariDataSource = new HikariDataSource(hikariConfig);
          ​
                  // 5. 获取连接
                  Connection connection = hikariDataSource.getConnection();
                  System.out.println("connection = " + connection);
                  
                  //6.回收连接
                  connection.close();
              }
          }
8.7 HikariCP其他配置【了解】
属性默认值说明
isAutoCommittrue自动提交从池中返回的连接
connectionTimeout30000等待来自池的连接的最大毫秒数
maxLifetime1800000池中连接最长生命周期如果不等于0且小于30秒则会被重置回30分钟
minimumIdle10池中维护的最小空闲连接数 minIdle<0或者minIdle>maxPoolSize,则被重置为maxPoolSize
maximumPoolSize10池中最大连接数,包括闲置和使用中的连接
metricRegistrynull连接池的用户定义名称,主要出现在日志记录和JMX管理控制台中以识别池和池配置
healthCheckRegistrynull报告当前健康信息
poolNameHikariPool-1连接池的用户定义名称,主要出现在日志记录和JMX管理控制台中以识别池和池配置
idleTimeout是允许连接在连接池中空闲的最长时间
  • 21
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值