1.JDBC原生代码的弊端
通过对JDBC原生代码的探讨,了解底层的工作原理之后发现,底层代码的步骤是固定的,每次创建一条连接,发送一条sql语句后就关闭连接(JVM相关内存就会清空)。
这个步骤就好比在navicat中创建一个连接,然后新建查询,输入一条sql语句,执行完后就关闭连接(或删除连接)。
JDBC原生代码存在以下几个明显的缺点:
- 代码冗余:开发人员需要反复编写相同或相似的代码,去连接数据库,准备语句和执行查询,处理查询结果,增加了没必要的代码量
- 异常处理:编写原生代码的过程中会抛出非运行时异常,需要捕获处SQLException 并进行戳无处理或转换
- 可维护性差:SQL语句直接写到 Java 代码中,不利于维护测试
- 性能效率低:每次创建一条连接,只发送执行一条sql语句,会导致频繁地打开和关闭数据库连接,会影响程序的性能,在高并发环境下尤为明显
2.JDBCTemplate和DataSource
2.1 JDBCTemplate(JDBC模版)
概念:是Spring框架中JDBC模块的核心类,Spring使用工厂模式对JDBC原生底层代码(创建连接、发送执行SQL、结果处理、异常处理等每次都需要反复编写的代码)进行集成封装,提高代码的复用率和开发效率。
作用:简化JDBC,提供query(),update()等方法,负责执行具体的数据库操作。
2.2 DataSource
概念:为了提高开发效率,规范定义的接口,用于获取数据库连接。它通常由应用服务器或连接池实现,如 C3P0、Tomcat JDBC Connection Pool 等。
作用:管理数据库连接的生命周期
特点:管理Connection,初始时创建若干个Connection 连接 , 当我们需要连接数据库的时候DataSource会为我们提供 连接,当我们使用完后,它会把 连接 回收到数据库连接池中,等待我们的下次使用。DataSource可以视为将以下代码封装成一个接口:
2.3 两者的关系
JDBCTemplate类的构造函数中接收一个DataSource类型的参数,当JDBCTemplate需要执行数据操作时候,从DataSource获取连接,执行结束后释放连接,DataSource重新回收。
DataSource 负责连接的生命周期管理,而 JdbcTemplate 负责执行具体的数据库操作。
3.JDBCTemplate编程
3.1 导入jar包
把以下jar包复制到项目的lib文件夹下,然后添加为库,jar包能展开后,就可以了
3.2 增实例
3.2.1 创建数据库连接池
//使用DataSource实现类对象创建数据库连接池,传入三个参数,连接路径、账号、密码
String url = "jdbc:mysql://localhost:3306/day7.10" +
"?userUnicode=true&characterEncoding=utf-8&userSSL=false&serverTimezone=Asia/Shanghai";
DataSource dataSource = new DriverManagerDataSource(url,"root","200234");
3.2.2 创建JDBCTemplate对象
发送和执行sql语句需要基于数据库连接,需要找DataSource要数据库连接,在创建JDBCTemplate对象时候,在构造函数中传入DataSource实现类对象dataSource。
//创建JDBCTemplate
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
String sql = "insert into emp(ename,sal) values ('JDBC模版的测试数据1',6500)";
3.2.3 发送执行SQL
//发送执行sql,增删改返回行数,用int接收
int n = jdbcTemplate.update(sql);
3.2.4 处理结果
//处理结果
System.out.println(n==1?"新增成功":"新增失败");
3.3 查询实例
3.3.1 RowMapper
调用query()方法,传入sql语句和RowMapper实现类对象,通过匿名内部类的方式,重写mapRow()方法,将查询到的字段组装为数据库表对应的实体类对象
package zxp.test_716;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import zxp.day717.entity.Employer;
import javax.sql.DataSource;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
public class JDBC4_Template_query {
public static void main(String[] args) {
//使用DataSource实现类对象创建数据库连接池,传入三个参数,连接路径、账号、密码
String url = "jdbc:mysql://localhost:3306/day7.10" +
"?userUnicode=true&characterEncoding=utf-8&userSSL=false&serverTimezone=Asia/Shanghai";
DataSource dataSource = new DriverManagerDataSource(url,"root","200234");
//创建JDBCTemplate
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
String sql = "select * from emp";
//发送执行sql
//RowMapper:存放了结果集对象ResultSet,还存放了当前指针对应的行号,
// 使用匿名内部类(只处理一次结果集)
List<Employer> list = jdbcTemplate.query(sql,new RowMapper<Employer>(){
//多次调用mapRow方法,结果集中有多少行数据,mapRow方法就会被调用多少次
//先通过执行SQL获取结果集rs 将结果集封装进RowMapper,通过mapRow方法操作RowMapper中的结果集
@Override
public Employer mapRow(ResultSet rs, int i) throws SQLException {
//利用行号(第二个形参)获取对应行的数据
// 执行方法内容
// 将组装好的对象返回并添加进集合
Integer id = rs.getInt("eid");
String name = rs.getString("ename");
String sex = rs.getString("esex");
Date date = rs.getDate("hirdate");
Double sal = rs.getDouble("sal");
Integer deptId = rs.getInt("deptid");
//创建本行数据对应的对象
Employer emp = new Employer(id,name,sex,date,sal,deptId);
return emp;
}
});
}
}
表面上看代码并没有很简洁,但是底层封装了很多代码,除了这种方法,JDBCTemplate还提供了BeanPropertyRowMapper——RowMapper的实现类,重写了以上匿名内部类中重写的方法。
3.3.2 BeanPropertyRowMapper
通过调用query()方法,传入sql语句和RowMapper实现类BeanPropertyRowMapper的对象,有参构造中传入的参数是——数据库表对应的实体类的类对象。
通过类对象(包含了类的所有信息,注意和类的对象的区别)告诉实现类,创建哪个类的类对象
类对象通过 类名.class 获取
public class JDBC4_Template_query {
public static void main(String[] args) {
//使用DataSource实现类对象创建数据库连接池,传入三个参数,连接路径、账号、密码
String url = "jdbc:mysql://localhost:3306/day7.10" +
"?userUnicode=true&characterEncoding=utf-8&userSSL=false&serverTimezone=Asia/Shanghai";
DataSource dataSource = new DriverManagerDataSource(url,"root","200234");
//创建JDBCTemplate
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
String sql = "select * from emp";
List<Employer> list=jdbcTemplate.query(sql,new BeanPropertyRowMapper<>(Employer.class));
list.forEach(p-> System.out.println(p));
}
}
- 类名与对应表名相关
- 一个属性对应一个字段
- 属性类型必须与字段类型一致
- 属性名必须与字段名相关
(1)字段名由一部分组成:age-------age
(2)字段名由两部分组成,使用小驼峰 person_id-------personId
注:否则映射不上- 封装
- 提供有参无参
- 数据类型声明为引用类型
- 如果sql的查询语句中起别名了,实体类中的属性名保持一致,否则结果集会映射失败,非必要不起别名(必须起别名的话,可以创建第二个实体类,用来存放别名字段)