JDBC
概念
- Java DataBase Connectivity(Java数据库连接):Java语言操作数据库
- 本质:其实是官方(SUN公司)定义的一套操作所有关系型数据库的规则,即接口。各个数据库厂商去实现这套接口,提供数据库驱动和jar包。我们可以使用这套接口(JDBC)编程,真正执行的代码时驱动jar包中的实现类。
步骤
- 导入驱动jar包
- 复制mysql-connector-java-5.1.37-bin.jar到项目的libs目录下
- 右键–>Add As Library
- 注册驱动
DriverManager
- 获取数据库连接对象
Connection
- 定义SQL
- 获取执行SQL语句的对象
Statement
- 执行SQL,接收返回结果
- 处理结果
- 释放资源
对象
- DriverManager:驱动管理对象
- Connection:数据库连接对象
- Statement:执行SQL的对象
- ResultSet:结果集对象
- PreparedStatement:执行SQL的对象
DriverManager驱动管理对象
注册驱动:告诉程序该使用哪一个数据库驱动jar
- 方法:
static void registerDriver(Driver driver)
:注册与给定的驱动程序DriverManager
写代码时使用:
Class.forName("com.mysql.jdbc.Driver");
1
2
3
4
5
6
7
8// 通过查看源码发现:在com.mysql.jdbc.Driver类中存在静态代码块
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}注意:mysql5之后的驱动jar包可以省略注册驱动的步骤
- 方法:
获取数据库连接(DriverManager)
- 方法:
static Connection getConnection(String url,String user,String password)
- 参数:
- url:指定连接的路径
- 语法:
jdbc:mysql://ip地址(域名):端口号/数据库名称
如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,则url可以简写为:
jdbc:mysql:///数据库名称
- user:用户名
- password:密码
- 方法:
Connection数据库连接对象
- 功能:
- 获取执行SQL的对象
Statement createStatement()
PreparedStatement prepareStatement(String sql)
- 管理事务
- 开启事务:
void setAutoCommit(boolean autoCommit)
:调用改方法设置参数为false。即开启事务 - 提交事务:
void commit()
- 回滚事务:
void rollback()
- 开启事务:
- 获取执行SQL的对象
Statement执行静态SQL的对象
- 执行SQL
boolean execute(String sql)
:可以执行任意的sqlint executeUpdate(String sql)
:执行DML(insert、update、delete)语句,DDL(create、alter、drop)语句- 返回值:影响的行数。可以通过这个影响的行数判断DML语句是否执行成功,返回值>0则执行成功。
ResultSet executeQuery(String sql)
:执行DQL(select)语句
- 样例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class JdbcDemo01 {
public static void main(String[] args) {
// 导入驱动
Connection conn = null;
Statement smt = null;
try {
// 静态SQL语句
String s1 = "insert into emp values ()";
String s2 = "delete from emp where salary>8000";
String s3 = "update emp set gender='女' where name='jdbc'";
// 注册驱动
Class.forName("com.mysql.jdbc.Driver");
// 获取数据库连接对象Connection
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/demo",
"root", "root");
// 获取Statement对象
smt = conn.createStatement();
// 执行sql语句
int r1 = smt.executeUpdate(s1);
int r2 = smt.executeUpdate(s2);
int r3 = smt.executeUpdate(s3);
System.out.println(r1);
System.out.println(r2);
System.out.println(r3);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 释放资源
// 避免空指针异常
if (smt != null) {
try {
smt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
ResultSet结果集对象
- 封装查询结果
boolean next()
:游标向下移动一行,判断当前行是否是最后一行末尾(是否有数据)。如果是,则返回false,如果不是返回true;XXX getXXX(参数)
:获取一列数据- XXX:代表数据类型, 如:int getInt()
- 参数:
- int:代表列的编号,如: getString(1)
- String:代表列名称,如: getString(“name”)
- 使用步骤:
- 游标向下移动一行
- 判断是否有数据
- 获取数据
1
2
3
4ResultSet resultSet = smt.executeQuery(s1);
while(resultSet.next()){
System.out.println(resultSet.getString("name"));
}
PreparedStatement执行SQL语句的对象
SQL注入问题:在拼接SQL时,有一些SQL的特殊关键字参与字符串的拼接,会造成安全性问题
- 输入用户随便,输入密码:a’ or ‘a’ = ‘a’
- sql:select * from user where username = ‘fhdsjkf’ and password = ‘a’ or ‘a’ = ‘a’
解决方案:使用
PreparedStatement
对象- 预编译的SQL:参数使用
?
作为占位符 - 使用步骤:
- 导入驱动jar包
- 注册驱动
- 获取数据库连接对象Connection
- 定义SQL
sql的参数使用?作为占位符。 如:select * from user where username = ? and password = ?;
- 获取执行SQL语句的对象PreparedStatement
Connection.prepareStatement(String sql)
- 给?赋值:
方法:
setXXX(参数1,参数2)
- 参数1:?的位置编号,从1开始
- 参数2:?的值
- 执行SQL:接受返回值结果,不需要传递SQL语句
- 处理结果
- 释放资源
- 注意:使用PreparedStatement来完成增删改查的所有操作
- 可以防止SQL注入
- 效率更高
- 语句中使用了占位符,规定了sql语句的结构。用户可以设置”?”的值,但是不能改变sql语句的结构,因此解决了SQL注入
JDBC工具类:JDBCUtils
- 目的:简化书写
分析:
- 注册驱动抽取
抽取一个方法获取连接对象
- 需求:不想传递参数(麻烦),还得保证工具类的通用性。
- 解决:配置文件
jdbc.properties
(文件的读取,只需要读取一次即可拿到这些值。使用静态代码块)1
2
3
4url=
user=
password=
driver=
抽取一个方法释放资源
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96package com.nogizaka.jdbc;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.sql.*;
import java.util.Properties;
public class JDBCUtils {
private static String url;
private static String user;
private static String password;
private static String driver;
/*
文件的读取,只需要读取一次即可拿到这些值。使用静态代码块
* */
static {
// 读取资源文件
try {
// 1 创建Properties类
Properties prop = new Properties();
// 获取src路径下的文件的方式:ClassLoader 类加载器
ClassLoader classLoader = JDBCUtils.class.getClassLoader();
URL res = classLoader.getResource("jdbc.properties");
// 获取字符串路径
String path = res.getPath();
// 2 加载文件
prop.load(new FileReader(path));
// 3 获取数据 赋值
url = prop.getProperty("url");
user = prop.getProperty("user");
password = prop.getProperty("password");
driver = prop.getProperty("driver");
// 4 注册驱动
Class.forName(driver);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
// 获取数据库连接
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();
}
}
}
public static void close(ResultSet res, Statement stmt, Connection conn) {
if (res != null) {
try {
res.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();
}
}
}
}
注意:
- properties文件放在src目录下
- 获取src路径下的文件的方式:ClassLoader 类加载器
1
2
3
4ClassLoader classLoader = JDBCUtils.class.getClassLoader();
URL res = classLoader.getResource("jdbc.properties");
// 获取字符串路径
String path = res.getPath();
JDBC事务控制
- 使用
Connection
对象来管理事务- 开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务
在执行sql之前开启事务
- 提交事务:commit()
当所有sql都执行完提交事务
- 回滚事务:rollback()
在catch中回滚事务
- 开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务
数据库连接池
- 概念:其实就是一个容器(集合),存放数据库连接的容器。当系统初始化好后,容器被创建,容器中会申请一些连接对象当用户来访问数据库时,从容器中获取连接对象,用户访问完以后,会将连接对象归还给容器。
- 好处:节约资源,提供访问效率
- 实现:
- 标准接口:
javax.sql.DataSource
- 方法:
- 获取连接:
getConnection()
- 归还连接:
Connection.close()
如果连接对象Connection是从连接池中获取的,那么调用Connection.close()
方法,则不会再关闭连接,而是归还连接
- 获取连接:
- 一般由数据库厂商实现
- C3P0:数据库连接池技术
- Druid:数据库连接池实现技术(阿里)
- 标准接口:
C3P0 数据库连接池技术
- 步骤:
- 导入jar包
c3p0-0.9.5.2.jar
mchange-commons-java-0.2.12.jar
- 定义配置文件:
- 名称:c3p0.properties 或者 c3p0-config.xml
- 路径:src目录下
- 创建核心对象 数据率连接池对象
new ComboPooledDataSource()
- 获取连接:
getConnection()
- 导入jar包
1 | // 创建数据库连接池对象 |
- 配置文件:c3p0-config.xml
1
2
3
4
5
6
7<!-- 连接池参数 -->
<!--初始化申请的连接数-->
<property name="initialPoolSize">5</property>
<!--最大连接数量-->
<property name="maxPoolSize">10</property>
<!--超时时间-->
<property name="checkoutTimeout">3000</property>
Druid 数据库连接池实现技术
- 步骤:
- 导入jar包
druid-1.0.9.jar
- 定义配置文件
- Properties
- 可以放在任意目录下
- 加载配置文件
- 获取数据库连接池对象:通过工厂来获取
DruidDataSourceFactory.createDataSource()
- 获取连接:
getConnection()
- 导入jar包
1 | // 定义配置文件 |
- 定义工具类
- 定义一个类 JDBCUtils
- 提供静态代码块加载配置文件,初始化连接池对象
- 提供方法
- 获取连接方法:通过数据库连接池获取连接
- 释放资源
- 获取连接池的方法
1 | public class JDBCUtils { |
Spring JDBC:JDBC Template
- Spring框架对JDBC的简单封装,提供了一个JDBCTemplate对象简化JDBC的开发
步骤
导入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
创建JdbcTemplate对象,依赖于数据源DataSource
JdbcTemplate template = new JdbcTemplate(ds);
调用JdbcTemplate的方法来完成CRUD的操作
update()
:执行DML语句。增、删、改语句queryForMap()
:查询结果将结果集封装为map集合,将列名作为key,将值作为value 将这条记录封装为一个map集合注意:这个方法查询的结果集长度只能是1
queryForList()
:查询结果将结果集封装为list集合注意:将每一条记录封装为一个Map集合,再将Map集合装载到List集合中
query()
:查询结果,将结果封装为JavaBean对象- query的参数:RowMapper
- 一般我们使用
BeanPropertyRowMapper
实现类。可以完成数据到JavaBean的自动封装 new BeanPropertyRowMapper<类型>(类型.class)
queryForObject()
:查询结果,将结果封装为对象一般用于聚合函数的查询
1 | /* 需求: |