一、JDBC扩展
1.1.实体类和ORM
(1)在使用JDBC操作数据库时,我们会发现数据都是零散的,明明在数据库中是一行完整的数据,到了Java中变成了一个一个的变量,不利于维护和管理。而我们Java是面向对象的,一个表对应的是一个类,一行数据就对应的是Java中的一个对象,一个列对应的是对象的属性,所以我们要把数据存储在一个载体里,这个载体就是实体类
(2)ORM(Object Relational Mapping)思想,对象到关系数据库的映射,作用是在编程中,把面向对象的概念跟数据库中表的概念对应起来,以面向对象的角度操作数据库中的数据,即一张表对应一个类,一行数据对应一个对象,一个列对应一个属性;
(3)当下JDBC中这种过程我们称其为手动ORM。后续我们也会学习ORM框架,比如MyBatis、JPA等
//类名就是数据表t_后面的单词全写
public class Employee{
private Integer empId;
private String empName;
private Double empSalary;
private Integer empAge;
//空参和全参的构造方法
public Employee(){};
public Employee(Integer empId,String empName,Double empSalary,Integer empAge) {
this.empId = empId;
this.empName = empName;
this.empSalary = empSalary;
this.empAge = empAge;
}
//每个属性的get和set方法以及toString()方法
}
public class JDBCAdvanced{
public void testORM(){
Connection connection DriverManager.getConnection("jdbc:mysql:///atguigu","root","abc123");
PreparedStatement preparedStatement = connection.prepareStatement("SELECT emp_id,emp_name,emp_salary FROM t_emp WHERE emp_id = ?");
prepareStatement.setInt(1,5);
ResultSet resultSet = preparedStatement.executeQuery();
Employee employee = null;
while(resultSet.next()){
int emp_id = resultSet.getInt("emp_id ");
String emp_name = resultSet.getString("emp_name");
double emp_salary = resultSet.getDouble("emp_salary");
employee = new Employee(emp_id,emp_name,emp_salary);
System.out.println(employee);
}
resultSet.close();
preparedStatement.close();
connection.close();
}
}
public void testORMList(){
Connection connection DriverManager.getConnection("jdbc:mysql:///atguigu","root","abc123");
PreparedStatement preparedStatement = connection.prepareStatement("SELECT * FROM t_emp");
prepareStatement.setInt(1,5);
ResultSet resultSet = preparedStatement.executeQuery();
Employee employee = null;
List<Employee> el = new ArrayList<>();
while(resultSet.next()){
int emp_id = resultSet.getInt("emp_id ");
String emp_name = resultSet.getString("emp_name");
double emp_salary = resultSet.getDouble("emp_salary");
employee = new Employee(emp_id,emp_name,emp_salary);
el.add(employee);
System.out.println(employee);
}
resultSet.close();
preparedStatement.close();
connection.close();
}
}
1.2.主键回显
在数据中,执行新增操作时,主键列为自动增长,可以在表中直观的看到,但是在Java程序中,我们执行完新增后,只能得到受影响行数,无法得知当前新增数据的主键值。在Java程序中获取数据库中插入新数据后的主键值,并赋值给Java对象,此操作为主键回显。
//类名就是数据表t_后面的单词全写
public class Employee{
private Integer empId;
private String empName;
private Double empSalary;
private Integer empAge;
//空参和全参的构造方法
public Employee(){};
public Employee(Integer empId,String empName,Double empSalary,Integer empAge) {
this.empId = empId;
this.empName = empName;
this.empSalary = empSalary;
this.empAge = empAge;
}
//每个属性的get和set方法以及toString()方法
}
public class JDBCAdvanced{
public void testReturnPK(){
Connection connection DriverManager.getConnection("jdbc:mysql:///atguigu","root","abc123");
PreparedStatement preparedStatement = connection.prepareStatement("INSERT INTO t_emp(emp_name,emp_salary,emp_age) VALUES (?,?,?)",Statement.RETURN_GENERATED_KEYS);
ResultSet resultSet = preparedStatement.executeQuery();
Employee employee = new Employee(null,"Jack",29);
preparedStatement.setString(1,employee.getEmpName());
preparedStatement.setDouble(2,employee.getEmpSalary());
preparedStatement.setInt(3, employee.getEmpAge());
int result =preparedStatement.executeUpdate();
if(result>0){
ResultSet resultSet = preparedStatement.getGeneratedKeys();
while(resultSet.next()){
int emp_id = resultSet.getInt(1);
employee.setEmpId(emp_id);
}
resultSet.close();
}
}
preparedStatement.close();
connection.close();
}
}
1.3.批量操作
(1)必须在连接数据库的URL后面追加?rewriteBatchedStatements=true,允许批量操作
(2)新增SQL必须用values,且语句最后不要追加;结束
(3)调用addBatch()方法,将SQL语句进行批量添加操作
(4)统一执行批量操作,调用executeBatch()
//类名就是数据表t_后面的单词全写
public class Employee{
private Integer empId;
private String empName;
private Double empSalary;
private Integer empAge;
//空参和全参的构造方法
public Employee(){};
public Employee(Integer empId,String empName,Double empSalary,Integer empAge) {
this.empId = empId;
this.empName = empName;
this.empSalary = empSalary;
this.empAge = empAge;
}
//每个属性的get和set方法以及toString()方法
}
public class JDBCAdvanced{
public void testMoreInsert(){
Connection connection = DriverManager.getConnection("jdbc:mysql:///atguigu?rewriteBatchedStatements=true","root","atguigu");
String sql = "insert into t_emp (emp_name,emp_salary,emp_age) values (?,?,?)";
PreparedStatement prepareedStatement = connection.prepareStatement(sql);
for(int i = 0;i<10000;i++){
preparedStatement.setString(1,"marry"+i);
preparedStatement.setDouble(2,100.0+i);
preparedStatement.setInt(3,20+i):
preparedStatement.addBatch():
}
preparedStatement.executeBatch();
preparedStatement.close();
connection.close();
}
}
二、连接池
2.1现有问题:
(1)每次操作数据库都要获取新连接,使用完毕后就close释放,频繁的创建和销毁造成资源浪费;
(2)连接的数量无法把控,对服务器来说压力巨大。
2.2.连接池
连接池就是数据库连接对象的缓冲区,通过配置由连接池负责创建连接、管理连接、释放连接等操作。预先创建数据库连接放入连接池,用户在请求时通过池直接获取连接,使用完毕后,将连接放回池中,避免了频繁的创建和销毁,同时解决了创建的效率。当池中无连接可用,且未达到上限时,连接池会新建连接。池中连接达到上限,用户请求会等待,可以设置超时时间。
2.3.常见连接池:
JDBC的数据库连接池使用javax.sql.DataSource接口进行规范,所有的第三方连接池都实现此接口,自行添加具体实现。也就是说,所有连接池获取连接的和回收连接方法都一样,不同的只有性能和扩展功能。
(1)DBCP是Apache提供的数据库连接池,速度相对C3P0较快,但自身存在一些BUG;
(2)C3P0 是一个开源组织提供的一个数据库连接池,速度相对较慢,稳定性还可以;
(3)Proxool是sourceforge下的一个开源项目数据库连接池,有监控连接池状态的功能,稳定性较c3po差一点;
(4)Druid是阿里提供的数据库连接池,是集DBCP、C3P0、Proxool优点于一身的数据库连接池,性能、扩展性、易用性都更好,功能丰富;
(5)Hikari是SpringBoot2.x之后内置的一款连接池,基于 BoneCP(已经放弃维护,推荐该连接池)做了不少的改进和优化,口号是快速、简单、可靠;
2.4.Druid连接池使用:
(1)硬编码:
import com.alibaba.druid.pool.DruidDataSource;
public void druidHard()throws SQLException {
//1.连接池对象
DruidDataSource dataSource =new DruidDataSource();
//2.设置四个必须参数
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUsername("root");
dataSource.setPassword("atguigu");
dataSource.setUrl("jdbc:mysql:///atguigu");
//非必须
dataSource.setInitialsize(5);//初始化数
dataSource.setMaxActive(10);//最大数量
//3.通过连接池获取连接
Connection connection=dataSource.getConnection();
// JDBC的步骤,正常curd
//4.回收连接,此处不是释放,而是将连接放回池中
connection.close();
}
(2)软编码:
在项目目录下创建resources文件夹,标识该文件夹为资源目录,创建db.properties配置文件,将连接信息定义在该文件中。
# druid连接池需要的配置参数,key固定命名
driyerClassName=com.mysql.cj.jdbc.Driver
username=root
password=atguigu
url=jdbc:mysql:///atguigu
initialSize=10
maxActive=20
public void druidSoft()throws Exception{
//创建Properties集合,存储文件中的key=value
Properties properties =new Properties();
//借助类加载器获取文件的字节数入流
InputStream ips=DruidTest.class.getClassLoader().getResourceAsStream("db.properties");
//将流中的数据存储到集合中
properties.load(ips);
//读取Properties集合中的数据创建连接池
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
Connection connection=dataSource.getConnection();
connection.close();
}
2.5.HikariCP连接池使用:
(1)硬编码方式:
import com.zaxxer.hikari.HikariDataSource;
public void testHardCodeHikari()throws SQLException {
//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();
}
(2)软编码方式:
在项目目录下创建resources文件夹,标识该文件夹为资源目录,创建hikari.properties配置文件,将连接信息定义在该文件中。
driverClassName=com.mysql.cj.jdbc.Driver
jdbcUrl=jdbc:mysql:///atguigu
username=root
password=atguigu
minimumIdle=10
maximumPoolSize=20
public void testResourcesHikari()throws Exception{
//1.创建Properties集合,用于存储外部配置文件的key和value值
Properties properties=new Properties();
//2.读取外部配置文件,获取输入流,加载到Properties集合里
Inputstream inputstream = HikariTest.class.getclassLoader().getResourceAsstream("hikari.properties");
properties.load(inputStream);
//3.创建HikariConfig连接池配置对象,将Properties集合传进去.
HikariConfig hikariconfig = new Hikariconfig(properties);
//4.基于HikariConfi连接池配置对象构建HikariDataSource
HikariDataSource hikariDataSource = new HikariDataSource(hikariconfig);
//5.获取连接
Connection connection=hikariDataSource.getConnection();
System.out.println(connection);
//6.回收连接
connection.close();
}