JDBC连接池&JDBCTemplate
- jdbc连接池
- 程序和数据库之间的连接是底层技术,每一次连接都比较耗时,而用完后又得关闭连接释放资源,这样反复连接和释放其实是很浪费时间的,因此引出一个连接池技术,来管理这些连接对象。连接对象被使用完后,会归还给连接池,等待分配给下一次使用,而不是销毁。
- jdbcTemplate
- jdbc操作中,很多的代码大量重复出现,其实我们写熟练以后,发现只需要关注sql怎么写就可以,所以对重复出现的代码进行封装来简化jdbc的操作。
一.数据库连接池
1. 概述
-
打个比方,过独木桥。
没有连接池时,一人一个独木桥,过河就拆,所以每来一个人过河就要搭建一个新的独木桥。
有了连接池后,就相当于建了一座大桥,可以同时让多个人通过,不需要一直建,一直拆。
-
画图理解。
2. 代码实现介绍
-
java.sql.DataSuorce 接口,就是表示连接池
- getConnection()方法 可以获取连接对象
-
数据源接口的实现由连接池厂商实现
- c3p0 古老的,常被hibernate框架使用
- druid 阿里的产品,效率很高
-
如果连接对象是由连接池得到的,那么它的Connection.close方法将不再表示关闭连接,而是归还连接对象到连接池。
3. c3p0连接池
3.1 基本使用
- 导jar包 – 两个连接池 一个数据库驱动 共3个
- 配置文件简介
<?xml version="1.0" encoding="utf-8" ?>
<c3p0-config>
<!-- 使用默认的配置读取连接池对象 -->
<default-config>
<!-- 连接参数 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/db1</property>
<property name="user">root</property>
<property name="password">root</property>
<!-- 连接池参数 -->
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">10</property>
<property name="checkoutTimeout">3000</property>
</default-config>
<named-config name="myc3p0">
<!-- 连接参数 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/db2</property>
<property name="user">root</property>
<property name="password">root</property>
<!-- 连接池参数 -->
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">20</property>
<property name="checkoutTimeout">5000</property>
</named-config>
</c3p0-config>
-
注意事项:
- c3p0-config.xml 文件名必须这么命名
- c3p0-config.xml 文件必须放在src目录下,不能放在子目录
-
可能会出错的地方
- 配置文件中书写错误
- 文件名或路径错误
- 数据库连接四大参数书写错误,或多加空格
- 忘记导入驱动jar包
public static void main(String[] args) throws SQLException { //创建连接池数据源对象 ComboPooledDataSource ds = new ComboPooledDataSource(); //获得连接对象 Connection con = ds.getConnection(); //输出连接对象的地址值表示连接正常 System.out.println(con); }
- 配置文件中书写错误
3.2 熟悉配置文件
-
测试最大连接数
//不给参数,默认使用default-config配置的数据源 ComboPooledDataSource ds = new ComboPooledDataSource(); //循环创建11个连接对象 for (int i=1;i<=11;i++){ Connection con = ds.getConnection(); System.out.println(i+":"+con); if(i==6){ con.close();//归还第6个对象给连接池 } }
-
测试超时时间
-
测试命名的数据源
ComboPooledDataSource ds = new ComboPooledDataSource("otherc3p0");
4. druid连接池
4.1 基本使用
-
导入jar包-- 1个
-
定义配置文件 druid.properties
-
代码演示
//1.导入jar包,定义配置文件 //2.加载配置文件 Properties pro = new Properties(); InputStream is = DruidDemo.class.getClassLoader().getResourceAsStream("druid.properties"); pro.load(is); //3.获取连接池对象 DataSource ds = DruidDataSourceFactory.createDataSource(pro); //4.获取连接对象 Connection con = ds.getConnection(); System.out.println(con);
4.2 工具类
-
代码演示
public class JDBCUtil { //1.定义一个数据源成员属性 private static DataSource ds; //2.在静态代码块中加载配置文件 static { try { Properties pro = new Properties(); InputStream is = JDBCUtil.class.getClassLoader().getResourceAsStream("druid.properties"); pro.load(is); //3.创建连接池对象 ds = DruidDataSourceFactory.createDataSource(pro); } catch (Exception e) { e.printStackTrace(); } } //获取连接对象的方法 public static Connection getConnection() throws SQLException { return ds.getConnection(); } //获取连接池对象的方法 public static DataSource getDataSource(){ return ds; } //释放资源的方法 public void close(Statement stmt, Connection con, ResultSet rs){ if(rs!=null){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if(stmt!=null){ try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if(con!=null){ try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
4.3 工具类测试
-
代码演示–向account表中添加一条数据
public static void main(String[] args) { Connection con=null; PreparedStatement pstmt=null; try { //1.获取连接对象 con = JDBCUtil.getConnection(); //2.定义sql String sql="insert into account values (null,?,?)"; //3.创建pstmt对象 pstmt = con.prepareStatement(sql); //4.给?赋值 pstmt.setString(1,"曹操"); pstmt.setDouble(2,3000); //5.执行sql int count = pstmt.executeUpdate(); System.out.println(count); } catch (SQLException e) { e.printStackTrace(); }finally { //6.释放资源 JDBCUtil.close(pstmt,con); } }
总结:
1.什么是连接池
1.就是一个容器
2.容器中装的是connection连接对象
3.这些连接对象创建后可以反复被使用,并不会销毁
4.它们是共享的
2.连接对象和连接池对象
connection 连接
DataSource 连接池 -- 数据源
获取;
DataSource ds=new DruidDataSource();
ds.getConnection();
-- 通过数据源获取数据,其实就是通过连接池获取连接
3.常用的连接池
c3p0 hibernate 传统的orm框架
druid 国产的 阿里
4.连接池的好处
1.节约资源 提高效率
二.jdbcTemplate
1. 简介
- Spring框架对jdbc的简单封装,提供了一个JdbcTemplate对象
- jdbcTemplate的若干方法
- update 执行 dml增删改
- queryForMap() 查询结果,将结果集封装为map集合
- queryForList() 查询结果,将结果集封装为list集合
- queryForObject() 查询结果集,将结果集封装成某个数据类型
- query() 查询结果集,将结果集封装为Javabean对象
2. 快速入门
-
代码演示–修改曹操的金额为5000
public static void main(String[] args) { //1.导入jar包 //2.创建jdbcTemplate对象 JdbcTemplate template = new JdbcTemplate(JDBCUtil.getDataSource()); //3.定义sql语句 String sql="update account set balance=5000 where id=?"; // 4.调用update方法,并给?赋值 int count = template.update(sql, 3); System.out.println(count); }
3. jdbcTemplate综合练习
- 修改id为1的数据
- 添加一条数据
- 删除一条数据
- 查询id为1的一条记录,封装成map
- 查询id为1的一条记录,封装成Account
- 查询所有记录,封装成list
- 查询所有记录,封装成泛型为Account的List
- 查询总记录数
public class DruidDemo3 {
private JdbcTemplate template= new JdbcTemplate(JDBCUtil.getDataSource());
@Test//修改
public void test1(){
int count = template.update("update account set balance=? where id=?",888,1);
System.out.println(count);
}
@Test//新增
public void test2(){
int count = template.update("insert account values (null,?,?)","哪吒",666);
System.out.println(count);
}
@Test//删除
public void test3(){
int count = template.update("delete from account where id=?",3);
System.out.println(count);
}
@Test//查询一条 封装map
public void test4(){
Map<String, Object> map = template.queryForMap("select * from account where id=?", 1);
System.out.println(map);
}
@Test//查询一条 封装javaBean
public void test5(){
Account account = template.queryForObject("select * from account where id=?", new BeanPropertyRowMapper<Account>(Account.class),2);
System.out.println(account);
}
@Test//查询所有,封装成List中泛型为Map
public void test6(){
List<Map<String, Object>> list = template.queryForList("select * from account ");
System.out.println(list);
}
@Test//查询所有,封装成List中泛型为JavaBean
public void test7(){
List<Account> list = template.query("select * from account ", new BeanPropertyRowMapper<Account>(Account.class));
System.out.println(list);
}
@Test//查询总记录数
public void test8(){
Integer a = template.queryForObject("select count(*) from account ", Integer.class);
System.out.println(a);
}
}
补充
1.部门表和员工表的查询并关联封装
@Test//每一个员工和他对应得部门信息
public void test2(){
List<Emp> emps = template.query("select * from emp ", new BeanPropertyRowMapper<Emp>(Emp.class));
//关联每个部门下 的多个员工信息
for (Emp emp : emps) {
emp.setDept(template.queryForObject("select * from dept where id=?",new BeanPropertyRowMapper<Dept>(Dept.class),emp.getDept_id()));
}
//输出
System.out.println(emps);
}
@Test//查询所有的部门下所有的员工
public void test1(){
List<Dept> depts = template.query("select * from dept ", new BeanPropertyRowMapper<Dept>(Dept.class));
//关联每个部门下 的多个员工信息
for (Dept dept : depts) {
dept.setEmps(template.query("select * from emp where dept_id=?",new BeanPropertyRowMapper<Emp>(Emp.class),dept.getId()));
}
//输出
System.out.println(depts);
}
2.三层架构
controller 控制层
service 业务层 --包含接口和实现类
dao 执行层 --包含接口和实现类
controller层 --调用--> service层 --调用--> dao层
注意:
接口和实现类的正确命名
3.template结合BeanPropertyRowMapper封装的模拟实现
@Test
public void testQuery1(){
List<Emp> emps = myQuery("SELECT * FROM EMP WHERE ID> ?", Emp.class, 3);
System.out.println(emps);
}
// 结合反射和原始的jdbc模拟查询和封装的实现
public <T> List<T> myQuery(String sql, Class<T> c,Object ...args){
try {
Class.forName("com.mysql.jdbc.Driver");
Connection con = DriverManager.getConnection("jdbc:mysql:///db3","root","root");
//3.获取执行sql的对象
PreparedStatement pstm = con.prepareStatement(sql);
//4.给占位符赋值
for (int i = 0; i < args.length; i++) {
pstm.setObject(i+1,args[i]);
}
//5.执行sql
ResultSet rs = pstm.executeQuery();
//6.处理结果集--拆开rs
//元数据--可以获取结果集中的列数,列名,列类型
ResultSetMetaData md = rs.getMetaData();
//获取总列数
int columnCount = md.getColumnCount();
ArrayList<T> ts = new ArrayList<>();
while (rs.next()){//外层循环遍历每一行
T t = c.newInstance();
for (int i = 1; i <= columnCount; i++) {//外层循环遍历每一行的每一个单元格
Object value = rs.getObject(i);
String columnName = md.getColumnName(i).toLowerCase();//获取每一个列的列名--和T中的属性名一致的
String columnName1 = md.getColumnLabel(i);//获取每一个列的列名--和T中的属性名一致的
String columnName2 = md.getColumnClassName(i);//获取每一个列的列名--和T中的属性名一致的
System.out.println(columnName+"--"+columnName1+"--"+columnName2);
Field f = c.getDeclaredField(columnName);
f.setAccessible(true);//开启私有属性的操作权限
f.set(t,value);
}
ts.add(t);
}
return ts;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}