目录
一、JDBC拓展
1.实体类和ORM
Ⅰ、ORM思想封装单个对象
Ⅱ、ORM思想封装集合
2.主键回显
3.批量操作
① 循环逐条数据进行添加
② 批量进行添加
二、连接池
1.现有问题
2.连接池
3.常见连接池
4.Druid连接池使用
使用步骤:
硬编码
软编码
5.HikariCP连接池使用
使用步骤
硬编码
软编码
坚持别人无法坚持的坚持,才能拥有别人无法拥有的拥有
—— 24.9.7
一、JDBC拓展
1.实体类和ORM
在使用JDBC操作数据库时,我们会发现数据都是零散的,明明在数据库中是一行完整的数据,到了Java中变成了一个一个的变量,不利于维护和管理。而我们Java是面向对象的,一个表对应的是一个类,一行数据就对应的是Java中的一个对象,一个列对应的是对象的属性,所以我们要把数据存储在一个载体里,这个载体就是实体类
ORM(Object Relational Mapping)思想,对象到关系数据库的映射,作用是在编程中,把面向对象的概念跟数据库中表的概念对应起来,以面向对象的角度操作数据库中的数据,即一张表对应一个类,一行数据对应一个对象,一个列对应一个属性
当下JDBC中这种过程我们称其为手动ORM。后续使用ORM框架:比如MyBatis、JPA等。
Ⅰ、ORM思想封装单个对象
将数据库中的数据R封装在Java中的对象O上,这个过程叫做映射M
package JDBC_Advance;
import JDBC_Advance.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 testORM1() throws Exception{
Connection con = DriverManager.getConnection("jdbc:mysql:///javawebjdbc","root","954926928lcl");
PreparedStatement ps = con.prepareStatement("select emp_id,emp_name,emp_salary,emp_age from t_emp where emp_id=?");
ps.setInt(1,1);
ResultSet rs = ps.executeQuery();
// 创建一个employee对象
Employee emp = null;
if(rs.next()){
emp = new Employee();
int empId = rs.getInt("emp_id");
String empName = rs.getString("emp_name");
double empSalary = rs.getDouble("emp_salary");
int empAge = rs.getInt("emp_age");
// 为对象属性赋值,这就是个ORM的体现过程,将数据库R中的数据封装在Java中的Object对象上,这个过程叫做映射 M
emp.setEmpId(empId);
emp.setEmpName(empName);
emp.setEmpSalary(empSalary);
emp.setEmAge(empAge);
}
System.out.println(emp);
rs.close();
ps.close();
con.close();
}
}
Ⅱ、ORM思想封装集合
① 创建对象——>② 将每一次对象存储在集合中——>③ 遍历集合
@Test
public void testORMList() throws Exception{
Connection con = DriverManager.getConnection("jdbc:mysql:///javawebjdbc","root","954926928lcl");
PreparedStatement ps = con.prepareStatement("select emp_id,emp_name,emp_salary,emp_age from t_emp");
ResultSet rs = ps.executeQuery();
Employee emp = null;
// 创建一个List泛型集合
List<Employee> employeelist = new ArrayList<>();
while(rs.next()) {
emp = new Employee();
int empId = rs.getInt("emp_id");
String empName = rs.getString("emp_name");
double empSalary = rs.getDouble("emp_salary");
int empAge = rs.getInt("emp_age");
emp.setEmpId(empId);
emp.setEmpName(empName);
emp.setEmpSalary(empSalary);
emp.setEmAge(empAge);
// 将每次循环创建的一行数据的对象存储在集合里
employeelist.add(emp);
}
// 5.遍历集合,处理结果
for (Employee employee : employeelist) {
System.out.println(employee);
}
// 6.资源的释放
rs.close();
ps.close();
con.close();
}
2.主键回显
在数据中,执行新增操作时,主键列为自动增长,可以在表中直观的看到,但是在Java程序中,我们执行完新增后,只能得到受影响行数,无法得知当前新增数据的主键值。在Java程序中获取数据库中插入新数据后的主键值,并赋值给Java对象,此操作为主键回显。
@Test
public void testReturnPK() throws Exception{
// 1.获取链接
Connection con = DriverManager.getConnection("jdbc:mysql:///javawebjdbc","root","954926928lcl");
// 2.预编译SQL语句
String sql = "insert into t_emp(emp_name,emp_salary,emp_age) values(?,?,?)";
// 3.对数据库声明主键回显,添加元素:RETURN_GENERATED_KEYS,主键回显,告知数据库在修改数据库后返回新增数据的主键列
PreparedStatement ps = con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
// 4.创建对象,将对象的属性值,赋值在?占位符上(ORM思想)
Employee emp = new Employee(null,"jack",123.45,28);
ps.setString(1,emp.getEmpName());
ps.setDouble(2,emp.getEmpSalary());
ps.setInt(3,emp.getEmAge());
// 5.执行SQL,并获取返回的结果
int res = ps.executeUpdate();
ResultSet rs2 = null;
// 6.处理结果
if(res > 0){
System.out.println("成功");
// 返回的主键值返回的是单行单列的结果,存储在rs2中获取当前新增数据的主键列,回显到Java中emp对象的empId属性上
// 返回的主键值返回的是单行单列的结果,存储在rs2中
rs2 = ps.getGeneratedKeys();
if(rs2 .next()){
int empId = rs2.getInt(1);
emp.setEmpId(empId);
}
// 输出员工对象(查询所有数据)
System.out.println(emp);
}else{
System.out.println("失败");
}
System.out.println(emp);
// 7.释放资源
if(rs2!=null){
rs2.close();
ps.close();
con.close();
}
}
3.批量操作
① 循环逐条数据进行添加
@Test
public void testMoreInsert() throws Exception{
// 1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2.获取链接
Connection con = DriverManager.getConnection("jdbc:mysql:///javawebjdbc","root","954926928lcl");
// 3.编写sql语句
String sql = "insert into t_emp(emp_name,emp_salary,emp_age) values(?,?,?)";
// 4.创建预编译的PreparedStatement,传入SQL语句
PreparedStatement ps = con.prepareStatement(sql);
// 5.获取当前系统执行用时
long start = System.currentTimeMillis();
// 获取当前系统时间(毫秒值)
for (int i = 0; i < 10000; i++) {
// 5.为占位符赋值
ps.setString(1,"jack"+i);
ps.setDouble(2,100.0+i);
ps.setInt(3,27+i);
ps.executeUpdate();
}
long end = System.currentTimeMillis();
System.out.println("消耗时间:"+(end-start));
con.close();
}
② 批量进行添加
1.批量操作必须是values,不能是value
2.必须在链接数据库的URL后面加?rewriteBatchedStatements=true,允许批量操作
3.调用addBatch()方法,将SQL语句进行批量添加的操作
4.统一执行批量操作,调用executeBatch()方法
@Test
public void testBatch() throws Exception{
// 1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2.获取链接
Connection con = DriverManager.getConnection("jdbc:mysql:///javawebjdbc?rewriteBatchedStatements=true","root","954926928lcl");
// 3.编写sql语句
// ① 批量操作必须是values,不能是value
// ② 必须在链接数据库的URL后面加?rewriteBatchedStatements=true,允许批量操作
// ③ 调用addBatch()方法,将SQL语句进行批量添加的操作
// ④ 统一执行批量操作,调用executeBatch()方法
String sql = "insert into t_emp(emp_name,emp_salary,emp_age) values(?,?,?)";
// 4.创建预编译的PreparedStatement,传入SQL语句
PreparedStatement ps = con.prepareStatement(sql);
// 5.获取当前系统执行用时
long start = System.currentTimeMillis();
// 获取当前系统时间(毫秒值)
for (int i = 0; i < 10000; i++) {
// 5.为占位符赋值
ps.setString(1,"jack"+i);
ps.setDouble(2,100.0+i);
ps.setInt(3,27+i);
// 若要批量操作,就不要进行executeUpdate()操作,将?看作一个整体,添加一个批处理方法addBatch
// ps.executeUpdate();
ps.addBatch();
}
// 6.执行批量操作
// 统一执行批量操作,调用executeBatch()方法
ps.executeBatch();
long end = System.currentTimeMillis();
System.out.println("消耗时间:"+(end-start));
con.close();
}
二、连接池
1.现有问题
① 每次操作数据库都要获取新连接,使用完毕后就close释放,频繁的创建和销毁造成资源浪费。
② 连接的数量无法把控,对服务器来说压力巨大。
2.连接池
① 连接池就是数据库连接对象的缓冲区,通过配置,由连接池负责创建连接、管理连接、释放连接等操作。
② 预先创建数据库连接放入连接池,用户在请求时,通过池直接获取连接,使用完毕后,将连接放回池中,避免了频繁的创建和销毁,同时解决了创建的效率
③ 当池中无连接可用,且未达到上限时,连接池会新建连接池中连接达到上限,用户请求会等待,可以设置超时时间。
3.常见连接池
JDBC的数据库连接池使用 javax.sql.DataSource接口进行规范,所有的第三方连接池都实现此接口,自行添加具体实现,也就是说,所有连接池获取连接的和回收连接方法都一样,不同的只有性能和扩展功能
① DBCP 是Apache提供的数据库连接池,速度相对C3P0较快,但自身存在一些BUG
② C3P0 是一个开源组织提供的一个数据库连接池,速度相对较慢,稳定性还可以。
③ Proxool 是sourceforge下的一个开源项目数据库连接池,有监控连接池状态的功能,稳定性较c3p0差一点
④ Druid 是阿里提供的数据库连接池,是集DBCP、C3P0、Proxool优点于一身的数据库连接池,性能、扩展性、易用性都更好,功能丰富。
⑤ Hikari(ひかり[shi ga li])取自日语,是光的意思,是SpringBoot2.x之后内置的一款连接池,基于 BoneCP(已经放弃维护,推荐该连接池)做了不少的改进和优化,口号是快速、简单、可靠。
4.Druid连接池使用
使用步骤:
① 引入jar包(第三方文件)
② 编码
硬编码
@Test
public void testHardCodeDruid() throws SQLException {
/*
硬编码:将连接池的配置信息与Java代码耦合在一起
1.创建一个DruidDataSource连接池对象
2.设置连接池的配置信息【必须项|非必须项】
3.通过连接池获取链接对象
4.回收链接【将连接归还给连接池,交给其他线程继续使用】
*/
// 1.创建一个DruidDataSource连接池对象
DruidDataSource ds = new DruidDataSource();
// 2.设置连接池的配置信息【必须项|非必须项】
// 2.1 必须设置的配置
ds.setDriverClassName("com.mysql.cj.jdbc.Driver");
ds.setUrl("jdbc:mysql:///javawebjdbc");
ds.setUsername("root");
ds.setPassword("954926928lcl");
// 2.2 非必须设置的配置
ds.setInitialSize(9); // 初始化大小,只要创建连接池,连接池中就会有9个链接对象
ds.setMaxActive(18); // 可以设置最大的连接池对象数目是多少
// 3.通过连接池获取链接对象
// 获取链接对象语句通用型
Connection conn = ds.getConnection();
System.out.println(conn);
// 4.基于链接对象conn进行增删改查操作
// 5.回收链接【将连接归还给连接池,交给其他线程继续使用】
conn.close();
}
软编码
在项目目录下创建resources文件夹,标识该文件夹为资源目录,创建db.properties配置文件,将连接信息定义在该文件中。
@Test
public void testResourcesDruid() throws Exception {
// 1.创建Properties集合,用于存储外部配置文件的key和value值
Properties props = new Properties();
// 2.读取外部配置文件,获取输入流,加载到Properties集合中
InputStream inputStream = DruidTest.class.getClassLoader().getResourceAsStream("db.properties");
props.load(inputStream);
// 3.基于Properties集合,构建DruidDataSource连接池
DataSource dataSource = DruidDataSourceFactory.createDataSource(props);
// 4.通过连接池获取链接对象
Connection connection = dataSource.getConnection();
System.out.println(connection);
// 5.开发CRUD
// 6.回收连接
connection.close();
}
软编码的方式可以从properties配置文件中直接更改,有利于我们对代码的更新和维护
5.HikariCP连接池使用
使用步骤
① 引入jar包(第三方文件)
② 编码
硬编码
@Test
public void testHardCodeHikari() throws Exception {
/*
硬编码:将连接池的配置信息与Java代码耦合在一起
1.创建一个HikariDataSource连接池对象
2.设置连接池的配置信息【必须项|非必须项】
3.通过连接池获取链接对象
4.回收链接【将连接归还给连接池,交给其他线程继续使用】
*/
// 1.创建一个HikariDataSource连接池对象
HikariDataSource hs = new HikariDataSource();
// 2.设置连接池的配置信息【必须项|非必须项】
// 2.1 必须设置的配置
hs.setDriverClassName("com.mysql.cj.jdbc.Driver");
hs.setJdbcUrl("jdbc:mysql:///javawebjdbc");
hs.setUsername("root");
hs.setPassword("954926928lcl");
// 2.2 非必须设置的配置
hs.setMaximumPoolSize(18);
hs.setMinimumIdle(9);
// 3.通过连接池获取链接对象
Connection con = hs.getConnection();
System.out.println(con);
// 4.回收连接
con.close();
}
软编码
@Test
public void testResourcesHikari() throws Exception {
// 1.创建Properties集合,用于存储外部配置文件的key和value值
Properties props = new Properties();
// 2.读取外部配置文件,获取输入流,加载到Properties集合里
InputStream inputStream = HIkariTest.class.getClassLoader().getResourceAsStream("hikari.properties");
props.load(inputStream);
// 只有第三步、第四步与Druid连接池不同,其他步骤差不多
// 3.创建一个HikariConfig连接池配置对象,将Properties集合传进去
HikariConfig hkcg = new HikariConfig(props);
// 4.基于HikariConfig连接池配置对象,构建HikariDataSource
HikariDataSource hkds = new HikariDataSource(hkcg);
// 5.获取链接
Connection con = hkds.getConnection();
System.out.println(con);
// 6.回收连接
con.close();
}