连接池和JdbcTemplate

连接池和JdbcTemplate

回顾

核心API的功能:

类名作用
DriverManager实现类注册驱动,创建建连接
Connection接口表示数据库的连接
Statement接口小货车,执行SQL语句的对象
ResultSet接口数据库返回的结果集

Connection接口中的方法:

Connection接口中的方法方法名
得到StatementcreateStatement

Statement接口中的方法:

Statement接口中的方法说明
int executeUpdate(String sql)更新INSERT UPDATE DELETE, DML
ResultSet executeQuery(String sql)查询SELECT

ResultSet接口的方法:

ResultSet接口中的方法描述
boolean next()将游标向下移动,看有下一行数据
数据类型 getXxx(参数)获取这条记录某个字段的数据

学习目标

  1. 能够通过PreparedStatement完成增、删、改、查
  2. 能够完成PreparedStatement改造登录案例
  3. 能够理解连接池解决现状问题的原理
  4. 能够使用C3P0连接池
  5. 能够使用DRUID连接池
  6. 能够编写连接池工具类
  7. 掌握JdbcTemplate实现增删改
  8. 掌握JdbcTemplate实现查询操作

PreparedSatement预编译对象

目标

能够理解什么是SQL注入

能够理解PreparedSatement的执行原理

讲解

SQL注入问题

在我们前一天JDBC实现登录案例中,当我们输入以下密码,我们发现我们账号和密码都不对竟然登录成功了

请输入用户名:
hehe
请输入密码:
a' or '1'='1

问题分析:

// 代码中的SQL语句
"SELECT * FROM user WHERE name='" + name + "' AND password='" + password + "';";
// 将用户输入的账号密码拼接后
"SELECT * FROM user WHERE name='hehe' AND password='a' or '1'='1';"

我们让用户输入的密码和SQL语句进行字符串拼接。用户输入的内容作为了SQL语句语法的一部分,改变了原有SQL真正的意义,以上问题称为SQL注入。

要解决SQL注入就不能让用户输入的密码和我们的SQL语句进行简单的字符串拼接。需要使用PreparedSatement类解决SQL注入。

PreparedSatement的执行原理

继承结构:

​ 我们写的SQL语句让数据库执行,数据库不是直接执行SQL语句字符串。和Java一样,数据库需要执行编译后的SQL语句(类似Java编译后的字节码文件)。

  1. Satement对象每执行一条SQL语句都会先将这条SQL语句发送给数据库编译,数据库再执行。
Statement stmt = conn.createStatement();
stmt.executeUpdate("INSERT INTO users VALUES (1, '张三', '123456');");
stmt.executeUpdate("INSERT INTO users VALUES (2, '李四', '666666');");

上面2条SQL语句我们可以看到大部分内容是相同的,只是数据略有不一样。数据库每次执行都编译一次。如果有1万条类似的SQL语句,数据库需要编译1万次,执行1万次,显然效率就低了。

  1. prepareStatement()会先将SQL语句发送给数据库预编译。PreparedStatement会引用着预编译后的结果。可以多次传入不同的参数给PreparedStatement对象并执行。相当于调用方法多次传入不同的参数。
String sql = "INSERT INTO users VALUES (?, ?, ?);";
// 会先将SQL语句发送给数据库预编译。PreparedStatement会引用着预编译后的结果。
PreparedStatement pstmt = conn.prepareStatement(sql);

// 设置参数
pstmt.setString(1, 1);
pstmt.setInt(2, "张三");
pstmt.setString(3, "123456");
pstmt.executeUpdate();

// 再次设置参数
pstmt.setString(1, 2);
pstmt.setInt(2, "李四");
pstmt.setString(3, "66666");
pstmt.executeUpdate();

上面预编译好一条SQL,2次传入了不同的参数并执行。如果有1万条类似的插入数据的语句。数据库只需要预编译一次,传入1万次不同的参数并执行。减少了SQL语句的编译次数,提高了执行效率。

示意图

PreparedSatement的好处
  1. prepareStatement()会先将SQL语句发送给数据库预编译。PreparedStatement会引用着预编译后的结果。可以多次传入不同的参数给PreparedStatement对象并执行。减少SQL编译次数,提高效率。
  2. 安全性更高,没有SQL注入的隐患。
  3. 提高了程序的可读性

小结

什么是SQL注入?
我们自己输入的内容被拼接成了SQL语句的一部分

PreparedSatement的好处?
1.会预编译,传入不同参数,提高效率
2.没有SQL注入的隐患,更加安全
3.提高SQL语句的可读性

PreparedSatement的API介绍

目标

能够掌握PreparedSatement相应的API

讲解

获取PreparedSatement

java.sql.Connection有获取PreparedSatement对象的方法

PreparedStatement prepareStatement(String sql) 
会先将SQL语句发送给数据库预编译。PreparedStatement对象会引用着预编译后的结果。
PreparedSatement的API介绍

java.sql.PreparedStatement中有设置SQL语句参数,和执行参数化的SQL语句的方法

  1. void setDouble(int parameterIndex, double x) 将指定参数设置为给定 Java double 值。
    
  2. void setFloat(int parameterIndex, float x) 将指定参数设置为给定 Java REAL 值。 
    
  3. void setInt(int parameterIndex, int x) 将指定参数设置为给定 Java int 值。
    
  4. void setLong(int parameterIndex, long x) 将指定参数设置为给定 Java long 值。 
    
  5. void setObject(int parameterIndex, Object x) 使用给定对象设置指定参数的值。  
    
  6. void setString(int parameterIndex, String x) 将指定参数设置为给定 Java String 值。 
    
  7. ResultSet executeQuery() 在此 PreparedStatement 对象中执行 SQL 查询,并返回该查询生成的ResultSet对象。 
    
  8. int executeUpdate() 在此 PreparedStatement 对象中执行 SQL 语句,该语句必须是一个 SQL 数据操作语言DML语句,比如 INSERT、UPDATE 或 DELETE 语句;或者是无返回内容的 SQL 语句,比如 DDL 语句。 
    
PreparedSatement使用步骤
  1. 编写SQL语句,未知内容使用?占位:"SELECT * FROM user WHERE name=? AND password=?;";

  2. 获得PreparedStatement对象

  3. 设置实际参数

  4. 执行参数化SQL语句

  5. 关闭资源

小结

PreparedSatement使用步骤?

  1. 编写SQL语句,未知内容使用?占位
  2. 获得PreparedStatement对象
  3. 设置实际参数
  4. 执行参数化SQL语句
  5. 关闭资源

PreparedSatement实现增删查改

目标

能够掌握PreparedSatement实现增删查改

讲解

创建表结构
CREATE TABLE employee (
  id INT PRIMARY KEY AUTO_INCREMENT,
  NAME VARCHAR(20),
  age INT,
  address VARCHAR(50)
);
添加数据
向Employee表添加3条记录
// 添加数据: 向Employee表添加3条记录
public static void addEmployee() throws Exception {
	Connection conn = JDBCUtils.getConnection();
	String sql = "INSERT INTO employee VALUES (NULL, ?, ?, ?);";
	// prepareStatement()会先将SQL语句发送给数据库预编译。
	PreparedStatement pstmt = conn.prepareStatement(sql);

	// 设置参数
	pstmt.setString(1, "刘德华");
	pstmt.setInt(2, 57);
	pstmt.setString(3, "香港");
	int i = pstmt.executeUpdate();
	System.out.println("影响的行数:" + i);

	// 再次设置参数
	pstmt.setString(1, "张学友");
	pstmt.setInt(2, 55);
	pstmt.setString(3, "澳门");
	i = pstmt.executeUpdate();
	System.out.println("影响的行数:" + i);

	// 再次设置参数
	pstmt.setString(1, "黎明");
	pstmt.setInt(2, 52);
	pstmt.setString(3, "香港");
	i = pstmt.executeUpdate();
	System.out.println("影响的行数:" + i);

	JDBCUtils.close(conn, pstmt);
}

效果:

PreparedSatement修改数据

将id为2的学生地址改成台湾

// 修改数据: 将id为2的学生地址改成台湾
public static void updateEmployee() throws Exception {
	Connection conn = JDBCUtils.getConnection();

	String sql = "UPDATE employee SET address=? WHERE id=?;";
	PreparedStatement pstmt = conn.prepareStatement(sql);
	pstmt.setString(1, "台湾");
	pstmt.setInt(2, 2);
	int i = pstmt.executeUpdate();
	System.out.println("影响的行数:" + i);

	JDBCUtils.close(conn, pstmt);
}

效果:

PreparedSatement删除数据

删除id为2的员工

// 删除数据: 删除id为2的员工
public static void deleteEmployee() throws Exception {
	Connection conn = JDBCUtils.getConnection();

	String sql = "DELETE FROM employee WHERE id=?;";
	PreparedStatement pstmt = conn.prepareStatement(sql);
	pstmt.setInt(1, 2);
	int i = pstmt.executeUpdate();
	System.out.println("影响的行数:" + i);

	JDBCUtils.close(conn, pstmt);
}

效果:

小结

PreparedSatement使用步骤?
1.获取连接
2.先写SQL语句带?的
3.获取PreparedStatement,预编译
4.设置参数
5.执行SQL
6.关闭资源

PreparedSatement实现增删查改使用哪个方法?
executeUpdate(); 执行增删改
exexuteQuery(); 执行查询

使用PreparedSatement改写登录案例

目标

能够使用PreparedSatement改写登录案例

  1. 输入正确的账号密码:
    1551606829574
  2. 输入错误的密码:
    1551606884763

讲解

PreparedSatement使用步骤
  1. 编写SQL语句,未知内容使用?占位
  2. 获得PreparedStatement对象
  3. 设置实际参数
  4. 执行参数化SQL语句
  5. 关闭资源
案例代码
public class Demo02 {
	public static void main(String[] args) throws Exception {
        // 让用户输入账号和密码
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入账号: ");
        String name = sc.nextLine();
        System.out.println("请输入密码: ");
        String password = sc.nextLine();
        
		// 获取连接
		Connection conn = JDBCUtils.getConnection();
		
		// 编写SQL语句,未知内容使用?占位
		String sql = "SELECT * FROM user WHERE name=? AND password=?;";
		
		// prepareStatement()会先将SQL语句发送给数据库预编译。
		PreparedStatement pstmt = conn.prepareStatement(sql);
		
		// 指定?的值
		// parameterIndex: 第几个?,从1开始算
		// x: 具体的值
		pstmt.setString(1, name);
		pstmt.setString(2, password); // 正确的密码
		
		ResultSet rs = pstmt.executeQuery();
		
		if (rs.next()) {
			String name = rs.getString("name");
			System.out.println("name:" + name);
		} else {
			System.out.println("没有找到数据...");
		}
		
		JDBCUtils.close(conn, pstmt, rs);
	}
}
案例效果
  1. 输入正确的账号密码:
    1551606829574
  2. 输入错误的密码:
    1551606884763

小结

PreparedSatement查询数据

目标

能够掌握PreparedSatement实现查询数据

1551613022301

讲解

1551613450029

查询id小于8的员工信息,并保存到员工类中

实现步骤
  1. 得到连接对象

  2. 得到Statement对象

  3. 编写SQL语句并执行,保存ResultSet

  4. 创建一个集合用于封装所有的记录

  5. 每次循环封装一个学生对象

  6. 把数据放到集合中

  7. 关闭连接

  8. 遍历集合,循环输出学生对象

代码
public class Employee {
	private int id;
	private String name;
	private int age;
	private String address;
	public Employee() {
	}
	
	public Employee(int id, String name, int age, String address) {
		this.id = id;
		this.name = name;
		this.age = age;
		this.address = address;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	@Override
	public String toString() {
		return "Employee2 [id=" + id + ", name=" + name + ", age=" + age + ", address=" + address + "]";
	}
}
// 查询数据: 查询id小于8的员工信息,并保存到员工类中
public static void queryEmployee() throws Exception {
	Connection conn = JDBCUtils.getConnection();
	String sql = "SELECT * FROM employee WHERE id<?;";
	PreparedStatement pstmt = conn.prepareStatement(sql);
	pstmt.setInt(1, 26);
	ResultSet rs = pstmt.executeQuery();

	// 创建集合存放多个Employee2对象
	ArrayList<Employee> list = new ArrayList<>();

	while (rs.next()) {
		// 移动到下一行有数据,取出这行数据
		int id = rs.getInt("id");
		String name = rs.getString("name");
		int age = rs.getInt("age");
		String address = rs.getString("address");

		// 创建Employee2对象
		Employee e = new Employee(id, name, age, address);
		// 将创建好的员工添加到集合中
		list.add(e);
	}

	// 输出对象
	for (Employee e : list) {
		System.out.println(e);
	}
	
	JDBCUtils.close(conn, pstmt, rs);
}

效果:

小结

表对应? 类

一条记录对应? 对象

字段值对应? 一个成员变量的值

PreparedSatement实现查询使用哪个方法?
executeQuery();

连接池介绍

目标

能够理解连接池的原理和好处

讲解

没有连接池的现状
  1. 之前JDBC访问数据库的步骤:
    创建数据库连接运行SQL语句关闭连接
    每次数据库访问执行这样重复的动作

  2. 每次创建数据库连接的问题

    • 获取数据库连接需要消耗比较多的资源,而每次操作都要重新获取新的连接对象,执行一次操作就把连接关闭,而数据库创建连接通常需要消耗相对较多的资源,创建时间也较长。这样数据库连接对象的使用率低。
    • 假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出

​ 我们现实生活中每日三餐。我们并不会吃一餐饭就将碗丢掉,而是吃完饭后将碗放到碗柜中,下一餐接着使用。目的是重复利用碗,我们的数据库连接也可以重复使用,可以减少数据库连接的创建次数。提高数据库连接对象的使用率。

连接池的概念: 连接池是创建和管理数据库连接的缓冲池技术。连接池就是一个容器,连接池中保存了一些数据库连接,这些连接是可以重复使用的。

连接池解决现状问题的原理
  1. 程序一开始就创建一定数量的连接,放在一个容器中,这个容器称为连接池(相当于碗柜/容器)。
  2. 使用的时候直接从连接池中取一个已经创建好的连接对象。
  3. 关闭的时候不是真正关闭连接,而是将连接对象再次放回到连接池中。
    1551614039056
连接池好处

连接池中保存了一些数据库连接,这些连接是可以重复使用的。节省数据库的资源消耗。

常用连接池的介绍

javax.sql.DataSource表示数据库连接池,是JDK中提供的一个接口,没有具体的实现,它的实现由连接池的数据库厂商去实现。我们只需要学习这个工具如何使用即可。

public interface DataSource {
	Connection getConnection();
}

常用的连接池实现组件有以下这些:

  1. 阿里巴巴-德鲁伊Druid连接池:Druid是阿里巴巴开源平台上的一个项目
  2. C3P0是一个开源的JDBC连接池,目前使用它的开源项目有Hibernate,Spring等。
  3. DBCP(DataBase Connection Pool)数据库连接池,是Apache上的一个Java连接池项目,也是Tomcat使用的连接池组件。

小结

连接池的好处?
连接池内部会保存好一些连接,这些连接可以反复使用,提高连接的使用率,降低数据库资源消耗
连接池的原理?
1.创建连接池时,连接池内部就会创建一些连接
2.当需要使用连接时,就直接从连接池里面取出连接
3.当连接使用完毕时,重新放回连接池

C3P0连接池

目标

能够使用C3P0连接池

1551619323240

讲解

C3P0连接池简介

C3P0地址:https://sourceforge.net/projects/c3p0/?source=navbar
C3P0是一个开源的连接池。Hibernate框架,默认推荐使用C3P0作为连接池实现。
C3P0的jar包:c3p0-0.9.1.2.jar
1550387401441

C3P0常用的配置参数解释
参数说明
initialPoolSize刚创建好连接池的时候连接数量
maxPoolSize连接池中最多可以放多少个连接
checkoutTimeout连接池中没有连接时最长等待时间
maxIdleTime连接池中的空闲连接多久没有使用就会回收。默认是0,0表示不回收
C3P0配置文件

​ 我们看到要使用C3P0连接池,需要设置一些参数。那么这些参数怎么设置最为方便呢?使用配置文件方式。

配置文件的要求:

  1. 文件名:c3p0-config.xml
  2. 放在源代码即src目录下

配置文件c3p0-config.xml

 <c3p0-config>
   <!-- 使用默认的配置读取连接池对象 -->
   <default-config>
     <!--  连接参数 -->
     <property name="driverClass">com.mysql.jdbc.Driver</property>
     <property name="jdbcUrl">jdbc:mysql://localhost:3306/day25</property>
     <property name="user">root</property>
     <property name="password">root</property>

     <!-- 连接池参数 -->
     <property name="initialPoolSize">5</property>
     <property name="maxPoolSize">10</property>
     <property name="checkoutTimeout">2000</property>
     <property name="maxIdleTime">1000</property>
   </default-config>

   <named-config name="itheimac3p0"> 
     <!--  连接参数 -->
     <property name="driverClass">com.mysql.jdbc.Driver</property>
     <property name="jdbcUrl">jdbc:mysql://localhost:3306/day25</property>
     <property name="user">root</property>
     <property name="password">root</property>

     <!-- 连接池参数 -->
     <property name="initialPoolSize">5</property>
     <property name="maxPoolSize">15</property>
     <property name="checkoutTimeout">2000</property>
     <property name="maxIdleTime">1000</property>
   </named-config>
 </c3p0-config>
API介绍

com.mchange.v2.c3p0.ComboPooledDataSource类表示C3P0的连接池对象,常用2种创建连接池的方式:

1.无参构造,使用默认配置

2.有参构造,使用命名配置

  1. public ComboPooledDataSource()
    无参构造使用默认配置(使用xml中default-config标签中对应的参数)
    
  2. public ComboPooledDataSource(String configName)
    有参构造使用命名配置(configName:xml中配置的名称,使用xml中named-config标签中对应的参数)
    
  3. public Connection getConnection() throws SQLException
    从连接池中取出一个连接
    
使用步骤
  1. 导入jar包c3p0-0.9.1.2.jar
  2. 编写c3p0-config.xml配置文件,配置对应参数
  3. 将配置文件放在src目录下
  4. 创建连接池对象ComboPooledDataSource,使用默认配置或命名配置
  5. 从连接池中获取连接对象
  6. 使用连接对象操作数据库
  7. 关闭资源
注意事项

C3P0配置文件名称必须为c3p0-config.xml
C3P0命名配置可以有多个

案例代码
  1. 准备数据

    CREATE TABLE student (
       id INT PRIMARY KEY AUTO_INCREMENT,
       NAME VARCHAR(20),
       age INT,
       score DOUBLE DEFAULT 0.0
    );
    
  2. 配置文件

     <c3p0-config>
       <!-- 使用默认的配置读取连接池对象 -->
       <default-config>
         <!--  连接参数 -->
         <property name="driverClass">com.mysql.jdbc.Driver</property>
         <property name="jdbcUrl">jdbc:mysql://localhost:3306/day25</property>
         <property name="user">root</property>
         <property name="password">root</property>
    
         <!-- 连接池参数 -->
         <property name="initialPoolSize">5</property>
         <property name="maxPoolSize">10</property>
         <property name="checkoutTimeout">2000</property>
         <property name="maxIdleTime">1000</property>
       </default-config>
    
       <named-config name="itheimac3p0"> 
         <!--  连接参数 -->
         <property name="driverClass">com.mysql.jdbc.Driver</property>
         <property name="jdbcUrl">jdbc:mysql://localhost:3306/day25</property>
         <property name="user">root</property>
         <property name="password">root</property>
    
         <!-- 连接池参数 -->
         <property name="initialPoolSize">5</property>
         <property name="maxPoolSize">15</property>
         <property name="checkoutTimeout">2000</property>
         <property name="maxIdleTime">1000</property>
       </named-config>
     </c3p0-config>
    
  3. java代码

     public class Demo01 {
    
         public static void main(String[] args) throws Exception {
             // 方式一: 使用默认配置(default-config)
             // new ComboPooledDataSource();
     //		 ComboPooledDataSource ds = new ComboPooledDataSource();
    
             // 方式二: 使用命名配置(named-config:配置名)
             // new ComboPooledDataSource("配置名");
             ComboPooledDataSource ds = new ComboPooledDataSource("otherc3p0");
    
     //		for (int i = 0; i < 10; i++) {
     //			Connection conn = ds.getConnection();
     //			System.out.println(conn);
     //		}
    
             // 从连接池中取出连接
             Connection conn = ds.getConnection();
    
             // 执行SQL语句
             String sql = "INSERT INTO student VALUES (NULL, ?, ?, ?);";
             PreparedStatement pstmt = conn.prepareStatement(sql);
             pstmt.setString(1, "张三");
             pstmt.setInt(2, 25);
             pstmt.setDouble(3, 99.5);
    
             int i = pstmt.executeUpdate();
             System.out.println("影响的行数: " + i);
             pstmt.close();
             conn.close(); // 将连接还回连接池中
         }
     }
    
案例效果
  1. 正常获取连接池中连接

  2. 获取连接池中连接超时

  3. 使用连接池中的连接往数据库添加数据

注意:配置文件名称必须为:c3p0-config.xml,将配置文件放在src目录下

使用配置文件方式好处

只需要单独修改配置文件,不用修改代码
多个配置的好处:

  1. 可以连接不同的数据库:db1,db2
  2. 可以使用不同的连接池参数:maxPoolSize
  3. 可以连接不同厂商的数据库:Oracle或MySQL

小结

CP30使用步骤?

1.导入jar包
2.复制配置文件到src下:c3p0-config.xml
3.修改配置信息
4.创建ComboPooledDataSource()连接池
5.使用连接池获取连接:getConnection();
6.后续操作
7.关闭资源

C3P0常用参数?

initialPoolSize刚创建好连接池时,连接池内包含多少个连接
maxPoolSize连接池最大可以存放多少连接
checkoutTimeout连接池没有连接可使用,最长的等待时间
maxIdleTime连接池中的连接多久没有被使用就会自动回收,默认0,不回收

Druid连接池

目标

能够掌握Druid连接池的使用

1551620535241

讲解

​ Druid是阿里巴巴开发的号称为监控而生的数据库连接池,Druid是目前最好的数据库连接池。在功能、性能、扩展性方面,都超过其他数据库连接池,同时加入了日志监控,可以很好的监控DB池连接和SQL的执行情况。Druid已经在阿里巴巴部署了超过600个应用,经过一年多生产环境大规模部署的严苛考验。Druid地址:https://github.com/alibaba/druid
DRUID连接池使用的jar包:druid-1.0.9.jar

1550388331344

Druid常用的配置参数
参数说明
jdbcUrl连接数据库的url:jdbc:mysql://localhost:3306/day17
username数据库的用户名
password数据库的密码
driverClassName驱动类名。根据url自动识别,这一项可配可不配,如果不配置druid会根据url自动识别dbType,然后选择相应的driverClassName(建议配置下)
initialSize刚创建好连接池的时候连接数量
maxActive连接池中最多可以放多少个连接
maxWait获取连接时最大等待时间,单位毫秒
Druid连接池基本使用

API介绍

com.alibaba.druid.pool.DruidDataSourceFactory类有创建连接池的方法

public static DataSource createDataSource(Properties properties)
创建一个连接池,连接池的参数使用properties中的数据

我们可以看到Druid连接池在创建的时候需要一个Properties对象来设置参数,所以我们使用properties文件来保存对应的参数。
Druid连接池的配置文件名称随便,放到src目录下面方便加载
druid.properties文件内容:

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/day17
username=root
password=root
initialSize=5
maxActive=10
maxWait=3000
使用步骤
  1. 导入druid的jar包

  2. 在src目录下创建一个properties文件,并设置对应参数

  3. 加载properties文件的内容到Properties对象中

  4. 创建Druid连接池,使用配置文件中的参数

  5. 从Druid连接池中取出连接

  6. 执行SQL语句

  7. 关闭资源

案例代码
  1. 在src目录下新建一个Druid配置文件,命名为:druid.properties,内容如下
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/day25
username=root
password=root
initialSize=5
maxActive=10
maxWait=3000

java代码

public class Demo04 {
	public static void main(String[] args) throws Exception {
		// 加载配置文件中的配置参数
		InputStream is = Demo04.class.getResourceAsStream("/druid.properties");
		Properties pp = new Properties();
		pp.load(is);
		
		// 创建连接池,使用配置文件中的参数
		DataSource ds = DruidDataSourceFactory.createDataSource(pp);
		
//		for (int i = 0; i < 10; i++) {
//			Connection conn = ds.getConnection();
//			System.out.println(conn);
//		}
		
		// 从连接池中取出连接
		Connection conn = ds.getConnection();
		
		// 执行SQL语句
		String sql = "INSERT INTO student VALUES (NULL, ?, ?, ?);";
		PreparedStatement pstmt = conn.prepareStatement(sql);
		pstmt.setString(1, "王五");
		pstmt.setInt(2, 35);
		pstmt.setDouble(3, 88.5);
		
		int i = pstmt.executeUpdate();
		System.out.println("影响的行数: " + i);
		
		// 执行查询
		sql = "SELECT * FROM student;";
		ResultSet rs = pstmt.executeQuery(sql);
		
		while (rs.next()) {
			int id = rs.getInt("id");
			String name = rs.getString("name");
			int age = rs.getInt("age");
			double score = rs.getDouble("score");
			System.out.println("id: " + id + " ,name: " + name + " ,age = " + age + " ,score = " + score);
		}
		
		pstmt.close();
		conn.close(); // 将连接还回连接池中
	}
}
案例效果
  1. 正常获取连接池中的连接
  2. 获取连接池中的连接超时
  3. 使用DRUID连接池中的连接操作数据库

小结

Druid使用步骤?

1.导入jar包:druid-1.0.9.jar
2.复制配置文件到src下:druid.properties,修改参数
3.创建Properties对象,加载druid.properties中的内容
4.创建连接池: DruidDataSourceFactory.createDataSource
5.获取连接
6.后续使用

Druid常用的配置参数

参数说明
initialSize初始化时建立连接的个数。
maxActive连接池最大连接的个数。
maxWait获取连接时最大等待时间,单位毫秒。

Druid连接池基本使用不管是C3P0连接池,还是DRUID连接池,配置大致都可以分为2种:1.连接数据库的参数2.连接池的参数,这2种配置大致参数作用都相同,只是参数名称可能不一样。

编写连接池工具类

目标

能够编写一个连接池的工具类,简化获取连接池中的连接对象

讲解

​ 我们每次操作数据库都需要创建连接池,获取连接,关闭资源,都是重复的代码。我们可以将创建连接池和获取连接池的代码放到一个工具类中,简化代码。

连接池工具类步骤
  1. 声明静态数据源成员变量
  2. 创建连接池对象
  3. 定义公有的得到数据源的方法
  4. 定义得到连接对象的方法
  5. 定义关闭资源的方法
案例代码

DataSourceUtils.java

public class DataSourceUtils {
	// 1.	声明静态数据源成员变量
	private static DataSource ds;

	// 2. 创建连接池对象
	static {
		// 加载配置文件中的数据
		InputStream is = DataSourceUtils.class.getResourceAsStream("/druid.properties");
		Properties pp = new Properties();
		try {
			pp.load(is);
			// 创建连接池,使用配置文件中的参数
			ds = DruidDataSourceFactory.createDataSource(pp);
		} catch (IOException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	// 3. 定义公有的得到数据源的方法
	public static DataSource getDataSource() {
		return ds;
	}

	// 4. 定义得到连接对象的方法
	public static Connection getConnection() throws SQLException {
		return ds.getConnection();
	}

	// 5.定义关闭资源的方法
	public static void close(Connection conn, Statement stmt, ResultSet rs) {
		if (rs != null) {
			try {
				rs.close();
			} catch (SQLException e) {}
		}

		if (stmt != null) {
			try {
				stmt.close();
			} catch (SQLException e) {}
		}

		if (conn != null) {
			try {
				conn.close();
			} catch (SQLException e) {}
		}
	}

	// 6.重载关闭方法
	public static void close(Connection conn, Statement stmt) {
		close(conn, stmt, null);
	}
}

测试类代码

public class Demo03 {
	public static void main(String[] args) throws Exception {
		// 拿到连接
		Connection conn = DataSourceUtils.getConnection();

		// 执行sql语句
		String sql = "INSERT INTO student VALUES (NULL, ?, ?, ?);";
		PreparedStatement pstmt = conn.prepareStatement(sql);
		pstmt.setString(1, "李四");
		pstmt.setInt(2, 30);
		pstmt.setDouble(3, 50);
		int i = pstmt.executeUpdate();
		System.out.println("影响的函数: " + i);

		// 关闭资源
		DataSourceUtils.close(conn, pstmt);
	}
}

使用连接池工具类后可以简化代码,我们重点是写SQL去执行。

小结

编写连接池工具类步骤

  1. 声明静态数据源成员变量
  2. 创建连接池对象
  3. 定义公有的得到数据源的方法
  4. 定义得到连接对象的方法
  5. 定义关闭资源的方法

JdbcTemplate基本使用

目标

能够掌握JdbcTemplate+Druid创建数据库

讲解

JdbcTemplate介绍

​ JDBC已经能够满足大部分用户最基本的需求,但是在使用JDBC时,必须自己来管理数据库资源如:获取PreparedStatement,设置SQL语句参数,关闭连接等步骤。

​ JdbcTemplate是Spring对JDBC的封装,目的是使JDBC更加易于使用。JdbcTemplate是Spring的一部分。JdbcTemplate处理了资源的建立和释放。他帮助我们避免一些常见的错误,比如忘了总要关闭连接。他运行核心的JDBC工作流,如Statement的建立和执行,而我们只需要提供SQL语句和提取结果。
Spring源码地址:https://github.com/spring-projects/spring-framework
在JdbcTemplate中执行SQL语句的方法大致分为3类:

  1. execute:可以执行所有SQL语句,一般用于执行DDL语句。
  2. update:用于执行INSERTUPDATEDELETE等DML语句。
  3. queryXxx:用于DQL数据查询语句。
JdbcTemplate配置连接池

org.springframework.jdbc.core.JdbcTemplate类方便执行SQL语句

  1. public JdbcTemplate(DataSource dataSource)
    创建JdbcTemplate对象,方便执行SQL语句
    
  2. public void execute(final String sql)
    execute可以执行所有SQL语句,因为没有返回值,一般用于执行DDL语句。
    
JdbcTemplate使用步骤
  1. 准备DruidDataSource连接池
  2. 导入依赖的jar包
    • spring-beans-4.1.2.RELEASE.jar
    • spring-core-4.1.2.RELEASE.jar
    • spring-jdbc-4.1.2.RELEASE.jar
    • spring-tx-4.1.2.RELEASE.jar
    • com.springsource.org.apache.commons.logging-1.1.1.jar
  3. 创建JdbcTemplate对象,传入Druid连接池
  4. 调用executeupdatequeryXxx等方法
案例代码
public class Demo04 {
	public static void main(String[] args) {
		// 创建表的SQL语句
		String sql = "CREATE TABLE product("
				+ "pid INT PRIMARY KEY AUTO_INCREMENT,"
				+ "pname VARCHAR(20),"
				+ "price DOUBLE"
				+ ");";
				
		JdbcTemplate jdbcTemplate = new JdbcTemplate(DataSourceUtils.getDataSource());
		jdbcTemplate.execute(sql);
	}
}
案例效果
  1. 代码效果
  2. 执行SQL后创建数据库效果

小结

JdbcTemplate使用步骤?

  1. 准备DruidDataSource连接池
  2. 导入依赖的jar包
    • spring-beans-4.1.2.RELEASE.jar
    • spring-core-4.1.2.RELEASE.jar
    • spring-jdbc-4.1.2.RELEASE.jar
    • spring-tx-4.1.2.RELEASE.jar
    • com.springsource.org.apache.commons.logging-1.1.1.jar
  3. 创建JdbcTemplate对象,传入Druid连接池
  4. 调用executeupdatequeryXxx等方法

JdbcTemplate实现增删改

目标

能够掌握JdbcTemplate实现增删改

讲解

API介绍

org.springframework.jdbc.core.JdbcTemplate类方便执行SQL语句

  1. public int update(final String sql)
    用于执行`INSERT`、`UPDATE`、`DELETE`等DML语句。
    
使用步骤

1.创建JdbcTemplate对象
2.编写SQL语句
3.使用JdbcTemplate对象的update方法进行增删改

案例代码
public class Demo05 {
	public static void main(String[] args) throws Exception {
//		test01();
//		test02();
//		test03();
	}
	
	// JDBCTemplate添加数据
	public static void test01() throws Exception {
		JdbcTemplate jdbcTemplate = new JdbcTemplate(DataSourceUtils.getDataSource());
		
		String sql = "INSERT INTO product VALUES (NULL, ?, ?);";
		
		jdbcTemplate.update(sql, "iPhone3GS", 3333);
		jdbcTemplate.update(sql, "iPhone4", 5000);
		jdbcTemplate.update(sql, "iPhone4S", 5001);
		jdbcTemplate.update(sql, "iPhone5", 5555);
		jdbcTemplate.update(sql, "iPhone5C", 3888);
		jdbcTemplate.update(sql, "iPhone5S", 5666);
		jdbcTemplate.update(sql, "iPhone6", 6666);
		jdbcTemplate.update(sql, "iPhone6S", 7000);
		jdbcTemplate.update(sql, "iPhone6SP", 7777);
		jdbcTemplate.update(sql, "iPhoneX", 8888);
	}
	
	// JDBCTemplate修改数据
	public static void test02() throws Exception {
		JdbcTemplate jdbcTemplate = new JdbcTemplate(DataSourceUtils.getDataSource());
		
		String sql = "UPDATE product SET pname=?, price=? WHERE pid=?;";
		
		int i = jdbcTemplate.update(sql, "XVIII", 18888, 10);
		System.out.println("影响的行数: " + i);
	}

	// JDBCTemplate删除数据
	public static void test03() throws Exception {
		JdbcTemplate jdbcTemplate = new JdbcTemplate(DataSourceUtils.getDataSource());
		String sql = "DELETE FROM product WHERE pid=?;";
		int i = jdbcTemplate.update(sql, 7);
		System.out.println("影响的行数: " + i);
	}
}
案例效果
  1. 增加数据效果
  2. 修改数据效果
  3. 删除数据效果

小结

JdbcTemplate的update方法用于执行DML语句。同时还可以在SQL语句中使用?占位,在update方法的Object... args可变参数中传入对应的参数。

JdbcTemplate查询-queryForInt返回一个int整数

目标

能够掌握JdbcTemplate中queryForInt方法的使用

讲解

org.springframework.jdbc.core.JdbcTemplate类方便执行SQL语句

API介绍
public int queryForInt(String sql)
执行查询语句,返回一个int类型的值。
使用步骤
  1. 创建JdbcTemplate对象
  2. 编写查询的SQL语句
  3. 使用JdbcTemplate对象的queryForInt方法
  4. 输出结果
案例代码
// queryForInt返回一个整数
public static void test01() throws Exception {
   // String sql = "SELECT COUNT(*) FROM product;";
   String sql = "SELECT pid FROM product WHERE price=18888;";
   JdbcTemplate jdbcTemplate = new JdbcTemplate(DataSourceUtils.getDataSource());
   int forInt = jdbcTemplate.queryForInt(sql);
   System.out.println(forInt);
}
案例效果

小结

queryForInt方法的作用?

JdbcTemplate查询-queryForLong返回一个long整数

目标

能够掌握JdbcTemplate中queryForLong方法的使用

讲解

org.springframework.jdbc.core.JdbcTemplate类方便执行SQL语句

API介绍
public long queryForLong(String sql)
执行查询语句,返回一个long类型的数据。
使用步骤
  1. 创建JdbcTemplate对象
  2. 编写查询的SQL语句
  3. 使用JdbcTemplate对象的queryForLong方法
  4. 输出结果
案例代码
// queryForLong  返回一个long类型整数
public static void test02() throws Exception {
   String sql = "SELECT COUNT(*) FROM product;";
   // String sql = "SELECT pid FROM product WHERE price=18888;";
   JdbcTemplate jdbcTemplate = new JdbcTemplate(DataSourceUtils.getDataSource());
   long forLong = jdbcTemplate.queryForLong(sql);
   System.out.println(forLong);
}
案例效果

小结

queryForLong方法的作用?

JdbcTemplate查询-queryForObject返回String

目标

能够掌握JdbcTemplate中queryForObject方法的使用

讲解

org.springframework.jdbc.core.JdbcTemplate类方便执行SQL语句

API介绍
public <T> T queryForObject(String sql, Class<T> requiredType)
执行查询语句,返回一个指定类型的数据。
使用步骤
  1. 创建JdbcTemplate对象
  2. 编写查询的SQL语句
  3. 使用JdbcTemplate对象的queryForObject方法,并传入需要返回的数据的类型
  4. 输出结果
案例代码
public static void test03() throws Exception {
   String sql = "SELECT pname FROM product WHERE price=7777;";
   JdbcTemplate jdbcTemplate = new JdbcTemplate(DataSourceUtils.getDataSource());
   String str = jdbcTemplate.queryForObject(sql, String.class);
   System.out.println(str);
}
案例效果

小结

queryForObject方法的作用?

JdbcTemplate查询-queryForMap返回一个Map集合

目标

能够掌握JdbcTemplate中queryForMap方法的使用

讲解

org.springframework.jdbc.core.JdbcTemplate类方便执行SQL语句

API介绍
public Map<String, Object> queryForMap(String sql)
执行查询语句,将一条记录放到一个Map中。
使用步骤
  1. 创建JdbcTemplate对象
  2. 编写查询的SQL语句
  3. 使用JdbcTemplate对象的queryForMap方法
  4. 处理结果
案例代码
public static void test04() throws Exception {
   String sql = "SELECT * FROM product WHERE pid=?;";
   JdbcTemplate jdbcTemplate = new JdbcTemplate(DataSourceUtils.getDataSource());
   Map<String, Object> map = jdbcTemplate.queryForMap(sql, 6);
   System.out.println(map);
}
案例效果

小结

queryForMap方法的作用?将返回的一条记录保存在Map集合中,key是字段名,value是字段值

JdbcTemplate查询-queryForList返回一个List集合

目标

能够掌握JdbcTemplate中queryForList方法的使用

讲解

org.springframework.jdbc.core.JdbcTemplate类方便执行SQL语句

API介绍
public List<Map<String, Object>> queryForList(String sql)
执行查询语句,返回一个List集合,List中存放的是Map类型的数据。
使用步骤
  1. 创建JdbcTemplate对象
  2. 编写查询的SQL语句
  3. 使用JdbcTemplate对象的queryForList方法
  4. 处理结果
案例代码
public static void test05() throws Exception {
   String sql = "SELECT * FROM product WHERE pid<?;";
   JdbcTemplate jdbcTemplate = new JdbcTemplate(DataSourceUtils.getDataSource());
   List<Map<String, Object>> list = jdbcTemplate.queryForList(sql, 8);
   for (Map<String, Object> map : list) {
      System.out.println(map);
   }
}
案例效果

小结

queryForList方法的作用?将返回的一条记录保存在Map集合中,多条记录对应多个Map,多个Map存储到List集合中

JdbcTemplate查询-RowMapper返回自定义对象

目标

能够掌握JdbcTemplate中RowMapper返回自定义对象

讲解

org.springframework.jdbc.core.JdbcTemplate类方便执行SQL语句

API介绍
public <T> List<T> query(String sql, RowMapper<T> rowMapper)
执行查询语句,返回一个List集合,List中存放的是RowMapper指定类型的数据。
使用步骤
  1. 定义Product类
  2. 创建JdbcTemplate对象
  3. 编写查询的SQL语句
  4. 使用JdbcTemplate对象的query方法,并传入RowMapper匿名内部类
  5. 在匿名内部类中将结果集中的一行记录转成一个Product对象
案例代码
// query使用rowMap做映射返回一个对象
public static void test06() throws Exception {
    JdbcTemplate jdbcTemplate = new JdbcTemplate(DataSourceUtils.getDataSource());

   // 查询数据的SQL语句
   String sql = "SELECT * FROM product;";

   List<Product> query = jdbcTemplate.query(sql, new RowMapper<Product>() {
      @Override
      public Product mapRow(ResultSet arg0, int arg1) throws SQLException {
         Product p = new Product();
         p.setPid(arg0.getInt("pid"));
         p.setPname(arg0.getString("pname"));
         p.setPrice(arg0.getDouble("price"));
         return p;
      }
   });

   for (Product product : query) {
      System.out.println(product);
   }
}
案例效果

小结

RowMapper的使用?

  1. 使用JdbcTemplate对象的query方法,并传入RowMapper匿名内部类
  2. 在匿名内部类中将结果集中的一行记录转成一个Product对象

JdbcTemplate查询-BeanPropertyRowMapper返回自定义对象

目标

能够掌握JdbcTemplate中BeanPropertyRowMapper返回自定义对象

讲解

org.springframework.jdbc.core.JdbcTemplate类方便执行SQL语句

API介绍
public <T> List<T> query(String sql, RowMapper<T> rowMapper)
执行查询语句,返回一个List集合,List中存放的是RowMapper指定类型的数据。
public class BeanPropertyRowMapper<T> implements RowMapper<T>
BeanPropertyRowMapper类实现了RowMapper接口
使用步骤
  1. 定义Product类
  2. 创建JdbcTemplate对象
  3. 编写查询的SQL语句
  4. 使用JdbcTemplate对象的query方法,并传入BeanPropertyRowMapper对象
案例代码
// query使用BeanPropertyRowMapper做映射返回对象
public static void test07() throws Exception {
	JdbcTemplate jdbcTemplate = new JdbcTemplate(DataSourceUtils.getDataSource());

	// 查询数据的SQL语句
	String sql = "SELECT * FROM product;";
	List<Product> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Product.class));

	for (Product product : list) {
		System.out.println(product);
	}
}
案例效果

小结

BeanPropertyRowMapper的使用?

List list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Product.class));

总结

JDBCTemplate的query方法用于执行SQL语句,简化JDBC的代码。同时还可以在SQL语句中使用占位,在query方法的Object... args可变参数中传入对应的参数。

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值