概述
JDBC:Java DataBase Connectity(java数据库的连接)是一种专门用于执行SQL语句的 Java API,可以为多种关系数据库提供统一的访问,它由一组用Java语言编写的接口组成。java代码操作各种数据库底层都是使用 JDBC。
JDBC规范定义接口,具体的实现由各大数据库厂商来实现 ,JDBC是Java访问数据库的标准规范。真正怎么操作数据库还需要具体的实现类,也就是数据库驱动(第三方 JAR 包)。每个数据库厂商根据自家数据库方式编写好自己数据库的驱动。数据库驱动由数据库厂商提供。
JDBC API
DriverManager 类
// 加载驱动
static void registerDriver(Driver driver) // 实际开发中不会选择 因为加载了2次驱动
Class.forName(Driver的全限定名) // 反射方式 加载一次驱动 适用开发
// 获取数据库连接的对象。该步骤可以放入try()中自动释放资源,否则需手动释放资源
static Connection getConnection(String url, String user, String password);
参数:
Url:数据库的地址 固定格式:jdbc:数据库类型://数据库地址:端口号/数据库名
user:数据库的用户名
Password:数据库的密码
注:
如果数据出现乱码需要加上参数: ?characterEncoding=utf8,表示让数据库以UTF-8编码来处理数据。
例如:jdbc:数据库类型://数据库地址:端口号/数据库名?characterEncoding=utf8
Connection 接口
// 获取执行SQL语句的Statement(预编译)对象 -- 常用
PreparedStatement PreparedStatement(String sql)
// 获取执行SQL语句的Statement(编译)对象
Statement createStatement();
// 操作事务。false:开启事务, ture:关闭事务(默认)
void setAutoCommit(boolean autoCommit); // 开始事务
void commit(); // 提交事务
void rollback(); // 回滚事务
// 注:在使用手动事务时,必须要抓取异常,在catch块中进行回滚事务。只要有事务,就必须try-catch捕捉处理。查询加不加事务都行
// 关闭连接,释放资源。重要!若未放入try()中自动释放资源,则需手动释放资源!
void close()
PreparedStatement:预编译对象
作用:用来执行sql语句的
语句执行者:Statement编译对象、preparedStatement预编译对象 的区别:
- Statement编译对象是有什么样的sql就执行什么样的sql语句,每次执行任何一条sql语句都得让数据库去编译执行,如果执行一万条同样的查询语句,数据库要编译一万次,效率低
- preparedStatement预编译对象是先将sql格式传递给数据库做预编译,其后拿着预编译结果传递参数执行sql,执行一万条同样的查询语句,数据库只编译一次,根据不同的参数做不用的执行
预编译的好处:
- 只需要编译一次,效率高
- 能让数据库提前知晓要执行的sql语句格式,只负责给数据库传参即可
语法格式:
select * from 表 where 字段1=值1 and 字段2=值2; -- Statement编译对象:
select * from 表 where name=? and password=?; -- preparedStatement预编译对象
注:
? : 占位符。所有的实际参数都用占位符替换了,而不在是直接设值了。默认从左向右第1个开始
通过外部方法来设置实际参数的值:set字段类型(占位符的序号,要设置的值);
占位符的序号是从1开始的
setString(1,"jack");
setString(2,"1234");
常用方法:
// 执行查询语句。返回结果集对象
ResultSet executeQuery();
// 执行sql语句(增删改),返回值为代表受影响的行数
int executeUpdate();
// 释放资源
void close()
Statement 接口
// 执行查询语句。返回结果集对象
ResultSet executeQuery()
// 执行sql语句(增删改),返回值为代表受影响的行数
int executeUpdate()
// 释放资源
void close()
Statement编译对象:拼接什么样的sql就执行什么样的sql语句
注意:使用Statement对象来操作sql语句会有缺陷:会造成数据的安全隐患!
- 比如登录中的sql拼接:select * from user where username='jack' #' and password='123';
相当于注释掉了密码的校验,这种安全隐患称为:Sql注入
Sql注入:将用户输入的内容作为了sql语句的一部分,改变了原有sql语句的含义
- 解决:使用另一个对象--preparedStatement对象来操作SQL语句
Resultset 接口
// 封装查询语句的结果集
boolean next() // 可以让指针向下移动一行,默认在第一行之前。返回值 true:代表有数据 false:代表没数据
// 获取结果集中每一行的数据
T get类型(int 列的位置)
T get类型(String 列名)
// 释放资源
void close()
原生 JDBC 代码示例
@Test
public void test1() throws Exception {
// 加载驱动
Class.forName("com.mysql.jdbc.Driver");
try(
// 获取数据库连接对象。放入try()中,自动释放资源
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/bookdb",
"root", "password");
) {
// 拼接sql语句
String sql="select * from user u where u.name=?";
// 获取执行SQL语句的对象(预编译对象)
PreparedStatement pst = connection.preparedStatement(sql);
// 设置具体的参数
pst.setString(1, "张三");
// 执行sql语句。resultSet: 结果集对象 (代表的就是查出来的表)
ResultSet resultSet = pst.executeQuery(sql);
// 遍历结果集
while(resultSet.next()){
// 获取数据打印
int id = resultSet.getInt("id");
String username = resultSet.getString("username");
String password = resultSet.getString("password");
System.out.println(id+":"+username+":"+password);
}
// 释放资源
resultSet.close();
pst.close();
// connection.close(); // 重要! 若没有放到try()内自动释放资源,则需要手动释放资源
} catch(Exception e){
...
}
}
连接池(数据源)
连接池:存放数据库连接的容器(集合)
连接池的作用及优化原理
之前在使用jdbc操作数据库数据的时候,有一个步骤是获取连接(创建连接);连接用完,还需要释放连接(销毁连接),这2个步骤太消耗资源了;创建连接=0.1秒,销毁连接=0.1秒,10000000*0.2=2000000秒。
连接池可以帮助解决创建连接和销毁连接带来的资源消耗问题。
连接池就是用来优化jdbc的以下2个步骤:
- 优化的是 jdbc 的创建连接部分
- 优化的是 jdbc 的销毁连接部分
优化原理
在连接池初始化时,就在容器中创建一定量的连接;当用户要连数据库的时候,就从容器中拿一个连接使用;使用完毕之后,不再是销毁,而是把使用后的连接还放回容器 供下一个用户去循环使用。
在企业中使用的都是已经成熟并且性能很高的提供好的连接池。
常见的连接池:
- DBCP:Apache提供的数据库连接池技术。
- C3P0:数据库连接池技术,目前使用它的开源项目有Hibernate、Spring等。
- HikaricP:日本开发的连接池技术,性能之王,目前使用它的开源项目有springBoot等。
Druid:阿里巴巴提供的数据库连接池技术,目前最好的数据库连接池
<!-- alibaba连接池 依赖--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.0.31</version> </dependency>
jdbc + druid 代码示例
配置文件:druid.properties
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/db88
username=root
password=password
initialSize=10
maxActive=20
minIdle=2
maxWait=2000
工具类:使用druid的方法加载的配置文件,连接从druid连接池获取的
// jdbc+druid的工具类
public class JDBCUtils {
// 数据源
private static DataSource ds;
static {
// // 加载配置。不使用druid
// ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
// driver = bundle.getString("driver");
// url = bundle.getString("url");
// username = bundle.getString("username");
// password = bundle.getString("password");
// // 加载驱动
// try {
// Class.forName(driver);
// } catch (ClassNotFoundException e) {
// e.printStackTrace();
// }
// 加载配置。使用druid的方法加载的配置文件,连接从druid连接池获取的
try {
// 创建一个配置文件对象
Properties properties=new Properties();
// 只要获取src下的资源文件流。”.class.getResourceAsStream“获取的是classess文件的路径,返回流
InputStream is = JDBCUtils.class.getResourceAsStream("/druid.properties");
// 配置文件对象加载配置文件
properties.load(is);
// 创建druid的数据源
ds = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
// 获取连接的方法
public static Connection getConnection() throws Exception {
Connection connection = ds.getConnection();
return connection;
}
// 释放资源的方法
public static void closeZY(ResultSet rs, Statement statement, Connection connection){
try {
if(rs!=null){
rs.close();
}
if(statement!=null){
statement.close();
}
if(connection!=null){
connection.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
jdbc + druid 测试代码
@Test
public void test1() {
// 从连接池中获取连接
Connection connection = JDBCUtils.getConnection();
// 拼接sql语句
String sql="select * from user u where u.name=?";
// 获取执行SQL语句的对象(预编译对象)
PreparedStatement preparedStatement = connection.preparedStatement(sql);
// 设置具体的参数
preparedStatement.setString(1, "张三");
ResultSet rs = preparedStatement.executeQuery();
while(rs.next()){
int id = rs.getInt("id");
String name = rs.getString("name");
String password = rs.getString("password");
System.out.println(id+"::"+name+"::"+password);
}
// 释放资源
JDBCUtils.closeZY(null, preparedStatement, connection);
}