1. JDBC的概念
Java Database Connectivity,是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。
1.1 快速入门
步骤:
- 导入驱动jar包
把要导入的包复制文件夹到 out 目录下的 meta-inf,在下面新建 lib 把 jar 放进去,右键 -> Add As Library即可 - 注册驱动
- 获取数据库连接对象 Connection
- 定义sql
- 获取执行sql语句的对象 Startement
- 执行sql,接受返回结果
- 处理结果
- 释放资源
public class Demo1 {
public static void main(String[] args) throws Exception {
// 1
// 2
Class.forName("com.mysql.cj.jdbc.Driver");
// 3
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai", "root", "123456Ljq");
// 4
String sql = "update account set balance = 500 where id = 1";
// 5
Statement statement = con.createStatement();
// 6
int count = statement.executeUpdate(sql);
// 7
System.out.println(count);
// 8
statement.close();
con.close();
}
}
1.2 JDBC各个类详解
1. DriverManager
驱动管理对象
功能:
- 注册驱动: 告诉程序应该使用哪一个数据库驱动jar
mysql5之后的驱动jar包可以省略注册驱动的步骤 - 获得数据库连接
- 方法: static Connection getConnection(String url, String user, String password)
- 参数:
- url:指定连接的路径 jdbc:mysql://ip地址或域名:端口号/数据库名称(如果连接的是本机mysql服务器,并且mysql服务器默认端口是3306,则可以简写为jdbc:mysql:///数据库名称)
- user:用户名
- password:密码
2. Connection
数据库连接对象
功能:
- 获取执行 sql 对象
- Statement createStatement()
- PreparedStatement prepareStatement(String sql)
- 管理事务
- 开启事务:void setAutoCommit(boolean autoCommit),调用该方法设置参数为false,即开启事务
- 提交事务:commit()
- 回滚事务:rollback()
3. Startement
执行sql语句对象
执行sql:
- boolean execute(String sql):可以执行任意的sql;
- int executeUpdate(String sql):执行DML(insert、update、delete)语句、DDL(create、alter、drop)语句,返回值是印象的行数;
- ResultSet executeQuery(String sql):执行DQL(select)语句。
练习: 添加、删除、修改account表的数据
public class Demo2 {
public static void main(String[] args) {
Connection con = null;
Statement sta = null;
try{
// 1. 注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2. 定义sql
String sql = "insert into account values (null, '张三', 1000)";
//String sql = "update account set balance = 1500 where id = 3"; //修改
//String sql = "delete from account where id = 3"; // 删除
// 3. 获取Connection对象
con = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai", "root", "123456Ljq");
// 4. 获取执行sql对象Statement
sta = con.createStatement();
// 5. 执行sql语句
int i = sta.executeUpdate(sql);
// 6. 处理结果
System.out.println(i);
if(i > 0) {
System.out.println("添加成功!");
} else {
System.out.println("添加失败!");
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 7. 释放资源
if(sta != null) {
try {
sta.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(con != null) {
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
4. ResultSet
结果集对象,封装查询结果
- boolean next():游标向下移动一格,判断当前行是否是最后一行末尾,没数据则返回false
- getXxx(参数):
- 获取数据,Xxx代表数据类型(比如getInt、getString)
- 参数:
- int:代表列的编号,从1开始,如getString(1)
- String:代表列名称,如getDouble(“balance”)
public class Demo4 {
public static void main(String[] args) {
Connection con = null;
Statement sta = null;
ResultSet rs = null;
try{
// 1. 注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2. 定义sql
String sql = "select * from account";
// 3. 获取Connection对象
con = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai", "root", "123456Ljq");
// 4. 获取执行sql对象Statement
sta = con.createStatement();
// 5. 执行sql语句
rs = sta.executeQuery(sql);
// 6. 处理结果
/*
(获取第一行数据)
rs.next(); // 游标向下移动一行,指向第一行数据,然后获取每个数据并输出
int id = rs.getInt(1);
String name = rs.getString("name");
double balance = rs.getDouble(3);
System.out.println(id + "-" + name + "-" + balance);
*/
// 游标向下移动一行,判断是否有数据,若有则获取每个数据并输出
while(rs.next()) {
int id = rs.getInt(1);
String name = rs.getString("name");
double balance = rs.getDouble(3);
System.out.println(id + "-" + name + "-" + balance);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 7. 释放资源
if(sta != null) {
try {
sta.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(con != null) {
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
5. PreparedStatement
执行sql的对象
sql注入问题: 在拼接sql时,有一些sql的特殊关键字参与字符串的拼接,会造成安全性问题。
解决sql注入问题: 使用PreparedStatement对象来解决
sql的参数使用?作为占位符,例如 select * from user where username = ? and password = ?
给 ?赋值: setXxx(参数1, 参数2),参数1:?的位置编号(从1开始),参数2:?的值
public boolean login(String username, String password) {
if(username == null || password == null) {
return false;
}
Connection con = null;
PreparedStatement preparedStatement = null;
Statement sta = null;
ResultSet rs = null;
try{
con = JDBCUtils.getConnection();
String sql = "select * from account where username = ? and password = ?";
preparedStatement = con.prepareStatement(sql);
preparedStatement.setString(1, username);
preparedStatement.setString(2, password);
rs = preparedStatement.executeQuery();
return rs.next();
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.close(rs, preparedStatement, con);
}
return false;
}
1.3 JDBC工具类
目的:简化书写
// 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();
//获取src路径下的文件--->ClassLoader类加载器
/*
ClassLoader classLoader = JDBCUtils.class.getClassLoader();
URL resource = classLoader.getResource("jdbc.properties");;
String path = resource.getPath();
*/
//2.加载文件
pro.load(new FileReader("D:\\wenjian\\Project01\\src\\JDBC\\jdbc.properties"));
//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 {
Connection conn = DriverManager.getConnection(url, user, password);
return conn;
}
/**
* 释放资源
* @param rs
* @param st
* @param conn
*/
public static void close(ResultSet rs, Statement st,Connection conn){
if (rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(st != null){
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void close(Statement stmt, Connection con) {
if(stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(con != null) {
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
简化后使用
public class Demo5 {
public static void main(String[] args) {
Connection con = null;
Statement sta = null;
ResultSet rs = null;
try{
con = JDBCUtils.getConnection();
String sql = "select * from account";
sta = con.createStatement();
rs = sta.executeQuery(sql);
while(rs.next()) {
int id = rs.getInt(1);
String name = rs.getString("name");
double balance = rs.getDouble(3);
System.out.println(id + "-" + name + "-" + balance);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
JDBCUtils.close(rs, sta, con);
}
}
}
1.4 JDBC管理事务
使用Connection对象来管理事务
- 在执行sql之前开启事务
- 在 所有sql都执行完提交事务
- 在catch中回滚事务
public class Demo6 {
public static void main(String[] args) {
Connection con = null;
// 开启事务
try {
con.setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
PreparedStatement pre1 = null;
PreparedStatement pre2 = null;
try {
con = JDBCUtils.getConnection();
String sql1 = "update account set balance = balance - ? where id = ?";
String sql2 = "update account set balance = balance + ? where id = ?";
pre1 = con.prepareStatement(sql1);
pre2 = con.prepareStatement(sql2);
pre1.setDouble(1, 500);
pre2.setDouble(1, 500);
pre1.setInt(2, 1);
pre2.setInt(2, 2);
pre1.executeUpdate();
pre2.executeUpdate();
// 提交事务
con.commit();
} catch (SQLException e) {
// 事务回滚
try {
if(con != null) {
con.rollback();
}
} catch (SQLException ex) {
ex.printStackTrace();
}
e.printStackTrace();
} finally {
JDBCUtils.close(pre1, con);
JDBCUtils.close(pre2, null);
}
System.out.println("111");
}
}
2. 数据库连接池
概念:其实就是一个容器(集合),用来存放数据库连接
当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完之后,会将连接对象归还给容器
好处:节约资源、用户访问高效
实现:
- 标准接口:DataSource,java.sql包下
- 获取连接:getConnection()
- 归还链接:如果连接对象Connection是从连接池中获取的,那么调用Connection.close()方法,就不会再关闭连接了,而是归还连接
- 一般我们不去实现它,有数据库厂商来实现
- C3P0
- Druid
2.1 C3P0
使用步骤:
- 导入jar包
- 定义配置文件
- 创建核心对象 数据库连接池对象:ComboPooledDataSource
- 获取连接:getConnection
public class Demo1_C3P0 {
public static void main(String[] args) throws SQLException {
// 1. 创建数据库连接池对象
DataSource ds = new ComboPooledDataSource();
// 2. 获取连接对象
Connection con = ds.getConnection();
System.out.println(con);
}
}
2.2 Druid
步骤:
- 导入jar包
- 定义配置文件
- 获取数据库连接池对象,通过工厂来获取:DruidDataSourceFactory
- 获取连接
public class Demo1_Druid {
public static void main(String[] args) throws Exception {
// 加载配置文件
Properties pro = new Properties();
InputStream is = Demo1_Druid.class.getClassLoader().getResourceAsStream("druid.properties");
pro.load(is);
// 获得连接池对象
DataSource ds = DruidDataSourceFactory.createDataSource(pro);
// 获得链接
Connection con = ds.getConnection();
System.out.println(con);
}
}
定义工具类
public class JDBCUtils {
// 定义成员变量
private static DataSource ds;
static {
// 1. 加载配置文件
Properties pro = new Properties();
try {
pro.load(JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties"));
// 2. 获取DataSource对象
ds = DruidDataSourceFactory.createDataSource(pro);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
// 获取连接
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
// 释放资源
public static void close(ResultSet res, Statement stmt, Connection con) {
if(res != null) {
try {
res.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();
}
}
}
public static void close(Statement stmt, Connection con) {
close(null, stmt, con);
}
// 获取连接池
public static DataSource getDataSource() {
return ds;
}
}
3. JDBCTemplate
JdbcTemplate是Spring对JDBC的封装,目的是使JDBC更加易于使用。JdbcTemplate是Spring的一部分。
在JdbcTemplate中执行SQL语句的方法大致分为3类:
- execute:可以执行所有SQL语句,一般用于执行DDL语句。
- update:用于执行INSERT、UPDATE、DELETE等DML语句。
- queryXxx:用于DQL数据查询语句。