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(); } }
-

浙公网安备 33010602011771号