目录
3.5、PreparedStatement:是Statement的子类,可以执行sql的对象,功能更强大。
1、JDBC基本概念
JDBC(Java DataBase Connectivity):Java数据库连接,Java语言操作数据库。
JDBC的本质:其实是官方(Sun公司)定义的一套操作所有关系型数据库的规则,即接口。各个数据库厂商去实现这套接口,提供数据库驱动jar包。我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。
2、快速入门
步骤(使用Statement类情形):
- 导入驱动jar包:下载地址 MySQL :: Download MySQL Connector/J (Archived Versions)
驱动jar包即解压后的 mysql-connector-java-5.1.37-bin.jar 文件。 导入驱动jar包分两步:① 复制 mysql-connector-java-5.1.37-bin.jar 到项目的libs目录下(新建一个libs目录);② 右键libs文件夹,点击 Add as library 。 - 注册驱动:
Class.forName("com.mysql.jdbc.Driver");
- 获取数据库的连接对象Connection:
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydatabase?characterEncoding=utf-8", "root", "123456");
- 定义sql:
String sql = "update student set age = 0 where id = 1";
- 获取执行sql语句的对象Statement:
Statement stmt = conn.createStatement();
- 执行sql,接收返回的结果:
int count = stmt.executeUpdate(sql);
- 处理结果:
System.out.println(count);
- 释放资源
代码演示:
package JDBCPackage;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
/**
* JDBC快速入门
*/
public class JdbcDemo1 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//1、导入驱动jar包 -> libs文件夹
//2、注册驱动
Class.forName("com.mysql.jdbc.Driver");
//3、获取数据库的连接对象
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/mydatabase?characterEncoding=utf-8", "root", "123456");
//4、定义sql语句
String sql = "update student set age = 0 where id = 1";
//5、获取执行sql的对象
Statement stmt = conn.createStatement();
//6、执行sql
int count = stmt.executeUpdate(sql);
//7、处理结果
System.out.println(count);
//8、释放资源
stmt.close();
conn.close();
}
}
3、详解各个对象
3.1、DriverManager:驱动管理对象。
3.1.1、DriverManager的功能:
1、注册驱动:告诉程序该使用哪一种数据库驱动jar包。
-注册与给定的驱动程序DirverManager
static void registerDriver(Driver driver)
写代码时使用:Class.forName("com.mysql.jdbc.Driver");
通过查看源码发现:在com.mysql.jdbc.Driver类中存在静态代码块:
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
注意:mysql5之后的驱动的jar包可以省略注册驱动的步骤。2、
2、获取数据库连接对象:
-获取数据库连接对象
static Connection getConnection(String url, String user, String password)
参数:
url:指定连接的路径。
mysql的语法写url:jdbc:mysql://ip地址(域名):端口号/数据库名称。
如果连接的是本机mysql服务器,并且mysql服务器默认端口是3306,则url可以简写为jdbc:mysql:///数据库名称
user:用户名
password:密码
范例:
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/mydatabase?characterEncoding=utf-8", "root", "123456");
3.2、Connection:数据库连接对象。
3.2.1、Connection的功能
1、获取执行sql的对象:
-获取执行sql的对象。两种方式:
Statement createStatement()
PreparedStatement preparedStatement(String sql)
2、管理事务:
-开启事务。调用该方法,设置参数为false,即为开启事务。
void setAutoCommit(boolean autoCommit)
-提交事务。
void commit()
-回滚事务
void rollback()
3.3、Statement:执行sql的对象。
3.3.1、Statement的功能
1、执行sql:
-执行任意的sql(了解即可)
boolean execute(String sql)
-执行DML(insert、update、delete)语句、DDL(create、alter、drop)语句
int executeUpdate(String sql)
返回值:影响的行数,可以通过这个影响的行数来判断DML语句是否执行成功。
-执行DQL(select)语句
ResultSet executeQuery(String sql)
代码演示:
package JDBCPackage;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
/**
* account表添加一条记录 insert语句
*/
public class JDBCDemo2 {
public static void main(String[] args) {
Statement stmt = null;
Connection conn = null;
try {
//1、注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2、定义sql
String sql = "insert into account values (null,'王五',3000)";
//3、获取Connection对象
conn = DriverManager.getConnection("jdbc:mysql:///mydatabase?characterEncoding=utf-8",
"root", "123456");
//4、获取执行sql的对象
stmt = conn.createStatement();
int count = stmt.executeUpdate(sql); //返回影响的行数
//5、处理结果
System.out.println(count);
if(count > 0) {
System.out.println("添加成功!");
}else {
System.out.println("添加失败!");
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}finally {
//避免空指针异常
if(stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
3.4、ResultSet:结果集对象
3.4.1、ResultSet的功能
ResultSet为结果集对象,用来封装查询的结果。
-游标向下移动一行,判断当前行是否是最后一行(是否有数据)
-如果是最后一行,则返回false;如果不是,则返回true
boolean next():
-获取数据。
getXxx(参数)
Xxx代表数据类型,如int getInt(), String getString(), ...
参数:两种重载:
int:代表列的编号。 如:getString(1);为获取该行第1列的值
String:代表列名称。 如:getString("name");
注意使用步骤:
- 游标向下移动一行;
- 判断是否有数据;
- 获取数据。
代码演示:
package JDBCPackage;
import java.sql.*;
public class JdbcDemo7 {
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
//1、注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2、获取连接对象
conn = DriverManager.getConnection("jdbc:mysql:///mydatabase?characterEncoding=utf-8",
"root", "123456");
//3、定义sql
String sql = "select * from account";
//4、获取执行sql对象
stmt = conn.createStatement();
//5、执行sql,取得ResultSet
rs = stmt.executeQuery(sql);
//6、处理结果
//6.1、让游标移动一行
while(rs.next()){
//6.2、获取数据
int id = rs.getInt(1);
String name = rs.getString("name");
int money = rs.getInt(3);
System.out.println(id + "---" + name + "---" + money);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}finally {
//7、释放资源
if(rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
3.4.2、抽取JDBC工具类:JDBCUtils
目的:简化书写。
分析:
- 抽取一个方法,用于注册驱动;
- 抽取一个方法,用于获取连接对象:需求:不想传递参数,还得保证工具类的通用性;解决方案:配置文件。
- 抽取与各方法,用于释放资源;
代码演示:
package util;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.sql.*;
import java.util.Properties;
/**
* JDBC工具类
*/
public class JDBCUtils {
private static String url;
private static String user;
private static String password;
private static String driver;
//文件的读取,只需要读取一次文件即可拿到这些值。使用静态代码块
static {
try {
//读取资源文件,获取值
//1、创建Properties集合类
Properties pro = new Properties();
//2、加载文件
//获取src路径下文件的方式 --> ClassLoader 类加载器
ClassLoader classLoader = JDBCUtils.class.getClassLoader();
URL res = classLoader.getResource("jdbc.properties");
String path = res.getPath();
pro.load(new FileReader(path));
//3、获取属性,赋值
url = pro.getProperty("url");
user = pro.getProperty("user");
password = pro.getProperty("password");
driver = pro.getProperty("driver");
//4、注册驱动
Class.forName(driver);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 获取连接对象
* @return 连接对象
*/
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url,user,password);
}
/**
* 释放资源
* @param stmt
* @param conn
*/
public static void close(Statement stmt, Connection conn) {
if(stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
* 释放资源
* @param rs
* @param stmt
* @param conn
*/
public static void close(ResultSet rs, Statement stmt, Connection conn) {
if(rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
package domain;
/**
* 1、定义Employee类
* 2、定义方法:public List<Employee> findAll() {}
* 3、实现方法:select * employee
*/
//封装employee表的JavaBean
public class Employee {
private int id; //员工id
private String eName; //员工名字
private int age; //员工年龄
private int dep_id; //所在部门编号
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String geteName() {
return eName;
}
public void seteName(String eName) {
this.eName = eName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getDep_id() {
return dep_id;
}
public void setDep_id(int dep_id) {
this.dep_id = dep_id;
}
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", eName='" + eName + '\'' +
", age=" + age +
", dep_id=" + dep_id +
'}';
}
}
package JDBCPackage;
import domain.Employee;
import util.JDBCUtils;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
/** 练习:定义一个方法,查询employee表的数据将其封装为对象,
* 然后装在集合中,返回
*/
public class JDBCDemo8 {
public static void main(String[] args) {
List<Employee> list = new JDBCDemo8().findAll();
System.out.println(list);
System.out.println(list.size());
}
/**
* 演示JDBC工具类
* @return
*/
public List<Employee> findAll() {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
List<Employee> list = null;
try {
//1、注册驱动,获取连接对象
conn = JDBCUtils.getConnection();
//3、定义sql
String sql = "select * from employee";
//4、获取执行sql的对象
stmt = conn.createStatement();
//5、执行sql
rs = stmt.executeQuery(sql);
Employee emp = null;
list = new ArrayList<Employee>();
//6、遍历结果集,封装对象,装在集合
while(rs.next()) {
//获取数据
int id = rs.getInt("id");
String eName = rs.getString("name");
int age = rs.getInt("age");
int dep_id = rs.getInt("dep_id");
//创建Employee对象
emp = new Employee();
emp.setId(id);
emp.seteName(eName);
emp.setAge(age);
emp.setDep_id(dep_id);
//装在集合中
list.add(emp);
}
}catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCUtils.close(rs,stmt,conn);
}
return list;
}
}
结果:
[Employee{id=1, eName='张三', age=20, dep_id=1}, Employee{id=2, eName='李四', age=21, dep_id=1}, Employee{id=3, eName='王五', age=20, dep_id=1}, Employee{id=4, eName='老王', age=20, dep_id=2}, Employee{id=5, eName='大王', age=22, dep_id=2}, Employee{id=6, eName='小王', age=18, dep_id=2}]
6
3.4.3、JDBC练习——登录案例
需求:
- 通过键盘录入用户名和密码;
- 判断用户是否登录成功。数据库表 user
代码演示:
package JDBCPackage;
import util.JDBCUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;
/**
* 练习:需求:
* 1、通过键盘录入用户名和密码
* 2、判断用户是否登录成功
*/
public class JDBCDemo9 {
public static void main(String[] args) {
//键盘录入,接收用户名和密码
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = sc.nextLine();
System.out.println("请输入密码:");
String password = sc.nextLine();
//调用方法
boolean flag = new JDBCDemo9().login(username, password);
//判断结果,输出不同语句
if(flag) {
//登录成功
System.out.println("登录成功!");
}else {
System.out.println("用户名或密码错误!");
}
}
public boolean login(String username, String password) {
if (username == null || password == null) {
return false;
}
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
//连接数据库判断是否成功
try {
//1、获取连接
conn = JDBCUtils.getConnection();
//2、定义sql
String sql = "select * from user where username = '" + username + "' " +
"and password = '" + password + "'";
//3、获取执行sql的对象
stmt = conn.createStatement();
//4、执行查询
rs = stmt.executeQuery(sql);
//5、判断
return rs.next();
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.close(rs, stmt, conn);
}
return false;
}
}
结果:
请输入用户名:
zhangsan
请输入密码:
123
登录成功!
3.5、PreparedStatement:是Statement的子类,可以执行sql的对象,功能更强大。
3.5.1、SQL注入问题
SQL注入:在拼接sql时,有一些sql的特殊关键字参与字符串的拼接,会造成安全性问题。
例如:
- 输入用户:任意;输入密码:a' or 'a' = 'a
- 得到sql语句:select * from user where username = 'sfsasds' and password = 'a' or 'a' = 'a'
- 因为where条件判断恒为true,因此可以返回查询结果。
解决sql注入问题:使用PreparedStatement对象来解决。
预编译的SQL:参数使用 ?作为占位符。如:PreparedStatement pstmt = con.prepareStatement("UPDATE EMPLOYEES SET SALARY = ? WHERE ID = ?");
3.5.2、PreparedStatement使用步骤
- 导入驱动jar包:mysql-connector-java-5.1.37-bin.jar
- 注册驱动:Class.forName("com.mysql.jdbc.Driver");
- 获取数据库的连接对象Connection:
- 定义sql:注意:sql的参数使用?作为占位符。如:select * from user where username = ? and password = ?
- 获取执行sql语句的对象PreparedStatement:
Connection.prepareStatement(String sql) 例如: PreparedStatement pstmt = conn.prepareStatement(String sql);
- 给?赋值:
PreparedStatement中的方法 -给?赋值 setXxx(参数1,参数2) 参数1: ?的位置编号(从1开始) 参数2: ?的值
- 执行sql,接收返回的结果,不需要传递sql语句:
- 处理结果:
- 释放资源
注意:后期都会使用PreparedStatement对象来完成增删改查的所有操作,因为:
- 可以防止SQL注入;
- 效率更高。
代码演示:
package JDBCPackage;
import util.JDBCUtils;
import java.sql.*;
import java.util.Scanner;
/**
* 练习:需求:
* 1、通过键盘录入用户名和密码
* 2、判断用户是否登录成功
* 登录方法:使用PreparedStatement来实现
*/
public class JDBCDemo10 {
public static void main(String[] args) {
//键盘录入,接收用户名和密码
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = sc.nextLine();
System.out.println("请输入密码:");
String password = sc.nextLine();
//调用方法
boolean flag = new JDBCDemo10().login(username, password);
//判断结果,输出不同语句
if(flag) {
//登录成功
System.out.println("登录成功!");
}else {
System.out.println("用户名或密码错误!");
}
}
public boolean login(String username, String password) {
if (username == null || password == null) {
return false;
}
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
//连接数据库判断是否成功
try {
//1、获取连接
conn = JDBCUtils.getConnection();
//2、定义sql
String sql = "select * from user where username = ? and password = ?";
//3、获取执行sql的对象
pstmt = conn.prepareStatement(sql);
//给?赋值
pstmt.setString(1,username);
pstmt.setString(2,password);
//4、执行查询,不需要传递sql
rs = pstmt.executeQuery();
//5、判断
return rs.next();
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.close(rs, pstmt, conn);
}
return false;
}
}
结果:
请输入用户名:
dsfs
请输入密码:
a' or 'a' = a'
用户名或密码错误!
4、JDBC控制事务
事务:一个包含多个步骤的业务操作,如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。
操作:
- 开启事务:
-调用该方法,设置参数为false,即开启事务 setAutoCommit(boolean autoCommit)
- 提交事务:
-提交事务 commit()
- 回滚事务:
-回滚事务 rollback()
代码演示:
package JDBCPackage;
import util.JDBCUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* 事务操作
*/
public class JDBCDemo11 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pstmt1 = null;
PreparedStatement pstmt2 = null;
try {
//1、获取连接
conn = JDBCUtils.getConnection();
//开启事务,设置在执行sql之前
conn.setAutoCommit(false);
//2、定义sql
//Jack -500
String sql1 = "update account set money = money - ? where name = ?";
//Mike +500
String sql2 = "update account set money = money + ? where name = ?";
//3、获取执行sql对象
pstmt1 = conn.prepareStatement(sql1);
pstmt2 = conn.prepareStatement(sql2);
//4、设置参数
pstmt1.setInt(1, 500);
pstmt1.setString(2, "Jack");
pstmt2.setInt(1, 500);
pstmt2.setString(2, "Mike");
//执行sql语句
pstmt1.executeUpdate();
pstmt2.executeUpdate();
//提交事务,设置在所有sql都执行完
conn.commit();
} catch (Exception e) {
try {
if(conn != null) {
//回滚事务,设置在catch中,先判断conn是否为null
conn.rollback();
}
} catch (SQLException ex) {
ex.printStackTrace();
}
e.printStackTrace();
} finally {
JDBCUtils.close(pstmt1, conn);
JDBCUtils.close(pstmt2, null);
}
}
}
5、数据库连接池(DataSource)
5.1、数据库连接池的概念
数据库连接池其实就是一个容器(集合),用来存放数据库的连接对象。当系统初始化好之后,容器会被创建,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完之后会将连接对象归还给容器。
5.2、数据库连接池的好处
- 节约了系统资源;
- 用户访问高效。
5.3、数据库连接池的实现
1、标准接口:DataSource,在javax.sql包下。
DataSource中的方法
-获取连接
Connection getConnection()
-归还连接
void close()
如果连接对象Connection是从连接池中获取的,那么调用这个方法,则不会再关闭连接了,而是会归还连接。
2、一般我们不去实现它,由数据库厂商实现:
- C3P0:数据库连接池
- Druid:数据库连接池实现技术,由阿里巴巴提供。
5.4、数据库连接池 C3P0 的使用
5.4.1、使用步骤
- 导入两个jar包:c3p0-0.9.5.2.jar 和 mchange-commons-java-0.2.12.jar 放入项目所在目录下的libs文件夹并设置add as library。注意:使用数据库连接池不要忘记导入驱动jar包。
- 定义配置文件:① 名称:c3p0.properties 或者 c3p0-config.xml ② 路径:直接将文件放在src目录下即可。不妨使用.xml形式的配置文件,然后打开文件修改相关参数。
- 创建核心对象:数据库连接池对象ComboPooledDataSource
-获取DataSource DataSource ds = new ComboPooledDataSource();
- 获取连接:
-获取连接 Connection getConnection()
- 进行操作
- 归还连接:
-归还连接到连接池中 void close()
代码演示:
c3p0-config.xml中内容:
<c3p0-config>
<!-- 使用默认的配置读取连接池对象 -->
<default-config>
<!-- 连接参数 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/mydatabase</property>
<property name="user">root</property>
<property name="password">123456</property>
<!-- 连接池参数 -->
<!-- 初始化申请的连接数量 -->
<property name="initialPoolSize">5</property>
<!-- 最大的连接数量 -->
<property name="maxPoolSize">10</property>
<!-- 超时时间 -->
<property name="checkoutTimeout">3000</property>
</default-config>
<named-config name="otherc3p0">
<!-- 连接参数 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/mydatabase?characterEncoding=utf-8</property>
<property name="user">root</property>
<property name="password">123456</property>
<!-- 连接池参数 -->
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">10</property>
<property name="checkoutTimeout">1000</property>
</named-config>
</c3p0-config>
package datasource.c3p0;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
/**
* c3p0演示
*/
public class C3P0Demo2 {
public static void main(String[] args) throws SQLException {
// //1、获取DataSource,使用默认配置
// DataSource ds = new ComboPooledDataSource();
// //2、获取连接
// for (int i = 0; i < 11; i++) {
// Connection conn = ds.getConnection();
// System.out.println(i+":"+conn);
//
// if(i==5){
// conn.close();//归还连接到连接池中
// }
// }
testNamedConfig();
}
//使用其它配置,<named-config name="otherc3p0">
public static void testNamedConfig() throws SQLException {
//获取DataSource,使用指定的配置名
ComboPooledDataSource ds = new ComboPooledDataSource("otherc3p0");
//2、获取连接
for (int i = 0; i < 10; i++) {
Connection conn = ds.getConnection();
System.out.println(i+":"+conn);
}
}
}
结果:
1月 26, 2022 12:31:20 下午 com.mchange.v2.log.MLog
信息: MLog clients using java 1.4+ standard logging.
1月 26, 2022 12:31:20 下午 com.mchange.v2.c3p0.C3P0Registry
信息: Initializing c3p0-0.9.5.2 [built 08-December-2015 22:06:04 -0800; debug? true; trace: 10]
1月 26, 2022 12:31:20 下午 com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource
信息: Initializing c3p0 pool... com.mchange.v2.c3p0.ComboPooledDataSource [ acquireIncrement -> 3, acquireRetryAttempts -> 30, acquireRetryDelay -> 1000, autoCommitOnClose -> false, automaticTestTable -> null, breakAfterAcquireFailure -> false, checkoutTimeout -> 1000, connectionCustomizerClassName -> null, connectionTesterClassName -> com.mchange.v2.c3p0.impl.DefaultConnectionTester, contextClassLoaderSource -> caller, dataSourceName -> otherc3p0, debugUnreturnedConnectionStackTraces -> false, description -> null, driverClass -> com.mysql.jdbc.Driver, extensions -> {}, factoryClassLocation -> null, forceIgnoreUnresolvedTransactions -> false, forceSynchronousCheckins -> false, forceUseNamedDriverClass -> false, identityToken -> 1hge0yvam158uicss15wgp|5dfcfece, idleConnectionTestPeriod -> 0, initialPoolSize -> 5, jdbcUrl -> jdbc:mysql://localhost:3306/mydatabase?characterEncoding=utf-8, maxAdministrativeTaskTime -> 0, maxConnectionAge -> 0, maxIdleTime -> 0, maxIdleTimeExcessConnections -> 0, maxPoolSize -> 10, maxStatements -> 0, maxStatementsPerConnection -> 0, minPoolSize -> 3, numHelperThreads -> 3, preferredTestQuery -> null, privilegeSpawnedThreads -> false, properties -> {password=******, user=******}, propertyCycle -> 0, statementCacheNumDeferredCloseThreads -> 0, testConnectionOnCheckin -> false, testConnectionOnCheckout -> false, unreturnedConnectionTimeout -> 0, userOverrides -> {}, usesTraditionalReflectiveProxies -> false ]
0:com.mchange.v2.c3p0.impl.NewProxyConnection@5ccddd20 [wrapping: com.mysql.jdbc.JDBC4Connection@1ed1993a]
1:com.mchange.v2.c3p0.impl.NewProxyConnection@794cb805 [wrapping: com.mysql.jdbc.JDBC4Connection@4b5a5ed1]
2:com.mchange.v2.c3p0.impl.NewProxyConnection@3cc2931c [wrapping: com.mysql.jdbc.JDBC4Connection@20d28811]
3:com.mchange.v2.c3p0.impl.NewProxyConnection@60d8c9b7 [wrapping: com.mysql.jdbc.JDBC4Connection@48aaecc3]
4:com.mchange.v2.c3p0.impl.NewProxyConnection@7adda9cc [wrapping: com.mysql.jdbc.JDBC4Connection@5cee5251]
5:com.mchange.v2.c3p0.impl.NewProxyConnection@5c909414 [wrapping: com.mysql.jdbc.JDBC4Connection@4b14c583]
6:com.mchange.v2.c3p0.impl.NewProxyConnection@4ddced80 [wrapping: com.mysql.jdbc.JDBC4Connection@1534f01b]
7:com.mchange.v2.c3p0.impl.NewProxyConnection@2ea227af [wrapping: com.mysql.jdbc.JDBC4Connection@4386f16]
8:com.mchange.v2.c3p0.impl.NewProxyConnection@4690b489 [wrapping: com.mysql.jdbc.JDBC4Connection@79b06cab]
9:com.mchange.v2.c3p0.impl.NewProxyConnection@7f552bd3 [wrapping: com.mysql.jdbc.JDBC4Connection@3c22fc4c]
5.5、数据库连接池 Druid 的使用
5.5.1、使用步骤
- 导入jar包:druid-1.0.9.jar 放入项目所在目录下的libs文件夹并设置add as library。注意:使用数据库连接池不要忘记导入驱动jar包。
- 定义配置文件:特点:① 是properties形式的;② 可以叫任意名称,可以放在任意目录下。可以放在src目录下。
druid.properties中内容: driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://127.0.0.1:3306/mydatabase username=root password=123456 initialSize=5 maxActive=10 maxWait=3000
- 加载配置文件:Properties方式
- 获取数据库连接池连接对象:通过工厂类来获取:DruidDataSourceFactory
-获取DataSource DataSource ds = new ComboPooledDataSource();
- 获取连接:
-获取连接 Connection getConnection()
- 进行操作
- 归还连接:
-归还连接到连接池中 void close()
代码演示:
package datasource.druid;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;
/**
* Druid演示
*/
public class DruidDemo1 {
public static void main(String[] args) throws Exception {
//1、导入jar包,复制到libs目录下,设置add as library
//2、定义配置文件,将druid.properties放到src目录下
//3、加载配置文件
Properties pro = new Properties();
InputStream is = DruidDemo1.class.getClassLoader().getResourceAsStream("druid.properties");
pro.load(is);
//4、获取连接池对象
DataSource ds = DruidDataSourceFactory.createDataSource(pro);
//5、获取连接
Connection conn = ds.getConnection();
System.out.println(conn);
}
}
结果:
1月 26, 2022 12:49:02 下午 com.alibaba.druid.pool.DruidDataSource info
信息: {dataSource-1} inited
com.mysql.jdbc.JDBC4Connection@5d740a0f
5.5.2、定义工具类
1、定义一个类 JDBCDruidUtils;
2、提供静态代码块加载配置文件,初始化连接池对象;
3、提供方法:
① 获取连接的方法:通过数据库连接池获取连接;
② 释放资源的方法;
③ 获取连接池的方法
代码演示:
package util;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
/**
* Druid连接池的工具类
*/
public class JDBCDruidUtils {
//1、定义成员变量
private static DataSource ds;
//初始化连接池
static {
try {
//1、加载配置文件
Properties pro = new Properties();
pro.load(JDBCDruidUtils.class.getClassLoader().getResourceAsStream("druid.properties"));
//2、获取DataSource
ds = DruidDataSourceFactory.createDataSource(pro);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取连接池
*/
public static DataSource getDataSource() {
return ds;
}
/**
* 获取连接
*/
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
/**
* 释放资源
*/
public static void close(Statement stmt,Connection conn) {
// if(stmt != null) {
// try {
// stmt.close();
// } catch (SQLException e) {
// e.printStackTrace();
// }
// }
// if(conn != null) {
// try {
// conn.close();
// } catch (SQLException e) {
// e.printStackTrace();
// }
// }
close(null,stmt,conn);
}
/**
* 释放资源
*/
public static void close(ResultSet rs, Statement stmt, Connection conn) {
if(rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
package datasource.druid;
import util.JDBCDruidUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* 使用JDBCDruidUtils工具类
*/
public class DruidDemo2 {
public static void main(String[] args) {
/*
完成添加操作,给account表添加一条记录
*/
Connection conn = null;
PreparedStatement pstmt = null;
try {
//1、获取连接
conn = JDBCDruidUtils.getConnection();
//2、定义sql
String sql = "insert into account values (null,?,?)";
//3、获取PreparedStatement对象
pstmt = conn.prepareStatement(sql);
//4、给?赋值
pstmt.setString(1,"张三");
pstmt.setInt(2,3000);
//5、执行sql
int count = pstmt.executeUpdate();
System.out.println(count);
} catch (SQLException e) {
e.printStackTrace();
}finally {
//6、释放资源
JDBCDruidUtils.close(pstmt,conn);
}
}
}
6、Spring JDBC:JDBC Template
Spring框架提供的对JDBC的简单封装。提供了一个JDBCTemplate对象简化JDBC的开发。
6.2、JDBC Template的使用步骤
1、导入5个jar包:
commons-logging-1.2.jar
spring-beans-5.0.0.RELEASE.jar
spring-core-5.0.0.RELEASE.jar
spring-jdbc-5.0.0.RELEASE.jar
spring-tx-5.0.0.RELEASE.jar
2、创建JDBC Template对象。依赖于数据源DataSource。
JdbcTemplate template = new JdbcTemplate(ds)
3、调用JdbcTemplate的方法来完成CRUD(增删改查)的操作:
-执行DML语句。增删改 语句
update()
-查询结果将结果集封装为map集合
queryForMap()
将列名作为key,值作为value,将这条记录封装为一个Map集合。
注意:这个方法查询的结果集长度只能是1。
-查询结果将结果集封装为list集合
queryForList()
将每一条记录封装为一个Map集合,再将Map集合装在到List集合中。
-查询结果,将结果集封装为JavaBean对象
query()
该方法的参数:RowMapper 。一般我们使用BeanProperyRowMapper实现类,可以完成数据到JavaBean的自动封装。
格式:new BeanPropertyRowMapper<类型>(类型.class)
-查询结果,将结果封装为对象(基本数据类型封装类对象)
queryForObject()
一般用于聚合函数的查询
6.3、JDBC快速入门
package JdbcTemplate;
import org.springframework.jdbc.core.JdbcTemplate;
import util.JDBCDruidUtils;
/**
* JdbcTemplate的入门
*/
public class JdbcTemplateDemo1 {
public static void main(String[] args) {
//1、导入jar包
//2、创建JDBCTemplate对象
JdbcTemplate template = new JdbcTemplate(JDBCDruidUtils.getDataSource());
//3、调用方法
String sql = "update account set money = ? where id = ?";
int count = template.update(sql, 1234,3);
System.out.println(count);
}
}
6.4、练习
需求:
- 修改1号数据的salary为10000;
- 添加一条记录;
- 删除刚才添加的记录;
- 查询id为1的记录,将其封装为Map集合:将列名作为key,值作为value,将这条记录封装为一个Map集合。注意:这个方法查询的结果集长度只能是1。
- 查询所有记录;将其封装为List集合:将每一条记录封装为一个Map集合,再将Map集合装在到List集合中。
- 查询所有记录,将其封装为Emp对象的List集合;
- 查询总记录数。
代码演示:
package domain;
import java.util.Date;
public class Emp {
//定义成封装类
private Integer id;
private String ename;
private Integer job_id;
private Integer mgr;
private Date joindate;
private Double salary;
private Double bonus;
private Integer dept_id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
public int getJob_id() {
return job_id;
}
public void setJob_id(int job_id) {
this.job_id = job_id;
}
public int getMgr() {
return mgr;
}
public void setMgr(int mgr) {
this.mgr = mgr;
}
public Date getJoindate() {
return joindate;
}
public void setJoindate(Date joindate) {
this.joindate = joindate;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public double getBonus() {
return bonus;
}
public void setBonus(double bonus) {
this.bonus = bonus;
}
public int getDept_id() {
return dept_id;
}
public void setDept_id(int dept_id) {
this.dept_id = dept_id;
}
@Override
public String toString() {
return "Emp{" +
"id=" + id +
", ename='" + ename + '\'' +
", job_id=" + job_id +
", mgr=" + mgr +
", joindate=" + joindate +
", salary=" + salary +
", bonus=" + bonus +
", dept_id=" + dept_id +
'}';
}
}
------------------------------------------------------------------
package JdbcTemplate;
import domain.Emp;
import org.junit.Test;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import util.JDBCDruidUtils;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* 练习
*/
public class JdbcTemplateDemo2 {
//0、获取JdbcTemplate对象
private JdbcTemplate template = new JdbcTemplate(JDBCDruidUtils.getDataSource());
//Junit单元测试。可以让方法独立执行
/**
* 1、修改1号数据的salary为10000
*/
@Test
public void test1() {
//1、定义sql
String sql = "update emp set salary = ? where id = ?";
//2、执行sql
int count = template.update(sql,10000,1); //返回受影响的行数
System.out.println(count);
}
/**
* 2、添加一条记录
*/
@Test
public void test2() {
//1、定义sql
String sql = "insert into emp(id,ename,dept_id) values(?,?,?)";
//2、执行sql
int count = template.update(sql,1015,"张三",10); //返回受影响的行数
System.out.println(count);
}
/**
* 3、删除刚才添加的记录
*/
@Test
public void test3() {
//1、定义sql
String sql = "delete from emp where id = ?";
//2、执行sql
int count = template.update(sql,1015); //返回受影响的行数
System.out.println(count);
}
/**
* 4、查询id为1001的记录,将其封装为Map集合
* 注意:这个方法查询的结果集只能是1
*/
@Test
public void test4() {
String sql = "select * from emp where id = ?";
Map<String, Object> map = template.queryForMap(sql, 1001);
System.out.println(map);
}
/**
* 5、查询所有记录,将其封装为List集合
* 将每一条记录封装为一个Map集合,再将Map集合装在到List集合中。
*/
@Test
public void test5() {
String sql = "select * from emp";
List<Map<String, Object>> list = template.queryForList(sql);
for(Map<String, Object> stringObjectMap : list) {
System.out.println(stringObjectMap);
}
}
/**
* 6、查询所有记录,将其封装为Emp对象的List集合
*/
@Test
public void test6_1() {
String sql = "select * from emp";
List<Emp> list = template.query(sql, new RowMapper<Emp>() {
@Override
public Emp mapRow(ResultSet rs, int i) throws SQLException {
Emp emp = new Emp();
int id = rs.getInt("id");
String ename = rs.getString("ename");
int job_id = rs.getInt("job_id");
int mgr = rs.getInt("mgr");
Date joindate = rs.getDate("joindate");
double salary = rs.getDouble("salary");
double bonus = rs.getDouble("bonus");
int dept_id = rs.getInt("dept_id");
emp.setId(id);
emp.setEname(ename);
emp.setJob_id(job_id);
emp.setMgr(mgr);
emp.setJoindate(joindate);
emp.setSalary(salary);
emp.setBonus(bonus);
emp.setDept_id(dept_id);
return emp;
}
});
for(Emp emp : list) {
System.out.println(emp);
}
}
/**
* 6、查询所有记录,将其封装为Emp对象的List集合
*/
@Test
public void test6_2() {
String sql = "select * from emp";
List<Emp> list = template.query(sql, new BeanPropertyRowMapper<Emp>(Emp.class));
for(Emp emp : list) {
System.out.println(emp);
}
}
/**
* 7、查询总记录数
*/
@Test
public void test7() {
String sql = "select count(id) from emp"; //计算主键数
Long total = template.queryForObject(sql, Long.class);
System.out.println(total);
}
}