JDBC简介
Java数据库连接,(Java Database Connectivity,简称JDBC)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成,是Java访问数据库的标准规范。JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序。JDBC需要连接驱动,驱动是两个设备要进行通信,满足一定通信数据格式,数据格式由设备提供商规定,设备提供商为设备提供驱动软件,通过软件可以与该设备进行通信。
一、JDBC原理
Java提供访问数据库规范称为JDBC,而生产厂商提供规范的实现类称为驱动。JDBC是接口,驱动是接口的实现类,没有驱动将无法完成数据库连接,从而不能操作数据库!每个数据库厂商都需要提供自己的驱动,用来连接自己公司的数据库,也就是说驱动一般都由数据库生成厂商提供。
二、开发步骤
1.注册驱动
- 告知JVM使用的是哪一个数据库的驱动
2.获取数据库连接
- 使用JDBC中的类,完成对MySQL数据库的连接
3.获取数据库操作对象
- 通过连接对象获取对SQL语句的执行者对象
4.执行sql语句
- 使用执行者对象,向数据库执行SQL语句 获取到数据库的执行后的结果
5.处理结果
- 处理 resultSet 数据库结果集的数据表
6.释放资源
- 调用一堆close()方法
下面展示示例代码
import java.sql.*;
public class JDBCDemo01 {
/**
* JDBC连接数据库的主要六步骤
* @param args
*/
public static void main(String[] args) {
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
//1、注册驱动 com.mysql.jdbc.Driver 是 mysql-connector-java5 中的 更高版本为 com.mysql.cj.jdbc.Driver
Class.forName("com.mysql.jdbc.Driver");
//2、获取数据库连接
/* url 连接数据库地址 jdbc:mysql 协议 localhost 本地ip地址 3306 mysql默认端口号
test_database 数据库
useUnicode=true&characterEncoding=UTF-8 指定字符的编码、解码格式
例如:mysql数据库用的是 gbk编码,而项目数据库用的是 utf-8编码。
如果添加了useUnicode=true&characterEncoding=UTF-8,那么作用有如下两个方面:
存数据时:数据库在存放项目数据的时候会先用 UTF-8格式将数据解码成字节码,
然后再将解码后的字节码重新使用 GBK编码存放到数据库中。
取数据时:在从数据库中取数据的时候,数据库会先将数据库中的数据按 GBK格式解码成字节码,
然后再将解码后的字节码重新按 UTF-8格式编码数据,最后再将数据返回给客户端。
useSSL=false MySQL在高版本需要指明是否进行SSL连接 1.true 需要连接 2.false 不需要连接
JDBC连接 mysql6 需要指定时区serverTimezone 这时 url则为:
url: jdbc:mysql://localhost:3306/test_database?
serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false
在设定时区的时候,如果设定serverTimezone=UTC,会比中国时间早8个小时,
如果在中国,可以选择Asia/Shanghai或者Asia/Hongkong,例如
?serverTimezone=Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false */
String url = "jdbc:mysql://localhost:3306/test_database?" +
"useUnicode=true&characterEncoding=utf8&useSSL=false";
String user = "root"; //user 用户名
String password = "123456"; //password 密码
connection = DriverManager.getConnection(url,user,password);
System.out.println("数据库连接对象为:"+connection);
//3、获取数据库操作对象
statement = connection.createStatement();
//4、执行sql语句
String sql = "select * from tb_stu";
/*如果执行的是删除、插入语句
String sql = "insert into tb_stu(name,age,address) values('李四',18,'上海市')";
// num 为受到影响的语句数
int num = statement.executeUpdate(sql);
if(num == 1){
System.out.println("插入数据成功");
}else{
System.out.println("插入数据失败");
}*/
//5、处理 resultSet 数据库结果集的数据表
resultSet = statement.executeQuery(sql);
//遍历结果集
while(resultSet.next()){
String name = resultSet.getString("name");
int age = resultSet.getInt("age");
String address = resultSet.getString("address");
System.out.println(name+" "+age+" "+address);
}
} catch (Exception exception) {
exception.printStackTrace();
}finally {
//6、关闭数据库资源 必须放在finally中,这样不会因为抛异常而不执行,需要做好非空校验
try {
if (resultSet != null) {
resultSet.close();
}
if (statement != null) {
statement.close();
}
if (connection != null) {
connection.close();
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
三、根据配置文件连接数据库
JDBC通过配置文件(properites)读取数据库配置信息
采用ResourceBundle进行数据库配置文件读取,比通过获取IO流的方式更加地简便!
创建一个默认的ResourceBundle对象,ResourceBundle会查找资源包下的properties的文件。资源包的命名,它跟普通java类的命名规则完全一样,区分大小写,资源文件必须位于指定包的路径之下(位于所指定的classpath中)。假如你是在非Web项目中使用,则一定要写资源文件的路径,也就是包路径必须存在。如果是Web项目,不写包路径可以,此时将资源文件放在WEB-INF\classes\目录下就可以。
配置文件db.properites
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test_database?useUnicode=true&characterEncoding=utf8&useSSL=false
user=root
password=123456
下面展示示例代码
import java.util.ResourceBundle;
/**
* 从配置文件读取数据库配置信息
*/
public class JDBCDemo02 {
public static void main(String[] args) {
ResourceBundle jdbc = ResourceBundle.getBundle("db");
String driver = jdbc.getString("driver");
String url = jdbc.getString("url");
String user = jdbc.getString("user");
String password = jdbc.getString("password");
System.out.println(driver);
System.out.println(url);
System.out.println(user);
System.out.println(password);
}
}
四、预处理对象,防止SQL注入
SQL注入攻击是黑客对数据库进行攻击的常用手段之一。其本质是在前端输入数据中夹带数据库SQL语句的关键字,达到绕开数据库数据判定,访问受限数据的目的。
在Java中Statement主要是用拼串传值的,这会造成可能出现危险!为此,我们使用PreparedStatement(预编译对象)来解决对应的问题。
下面展示示例代码
import java.sql.*;
public class JDBCDemo03 {
/**
* PreparedStatement 可以防止sql注入
* @param args
*/
public static void main(String[] args) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/test_database?useUnicode=true&characterEncoding=utf8&useSSL=false";
String user = "root";
String password = "123456";
connection = DriverManager.getConnection(url,user,password);
System.out.println("数据库连接对象为:"+connection);
//获取数据库操作对象 并且预编译sql
preparedStatement = connection.prepareStatement("SELECT * from tb_user WHERE user = ? AND password = ?");
//给占位赋值
preparedStatement.setString(1,"jack");
preparedStatement.setString(2,"1234 'or '1' = '1");
//执行sql语句
resultSet = preparedStatement.executeQuery();
if (resultSet.next()){
System.out.println("登录成功");
}else {
System.out.println("登录失败");
}
} catch (Exception exception) {
exception.printStackTrace();
}finally {
try {
if (resultSet != null) {
resultSet.close();
}
if (preparedStatement != null) {
preparedStatement.close();
}
if (connection != null) {
connection.close();
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
五、封装JDBC工具类
下面展示示例代码
import java.sql.*;
/**
* 封装JDBC工具类
* 1、构造方法私有化 不希望工具类可以实例化对象
* 2、所有使用的方法用 static 关键字修饰 让其变成是类级别的方法 可以被类直接调用
*/
public class JDBCUtil {
//构造方法私有化
private JDBCUtil(){
}
/**
* 把注册驱动的过程 写在静态代码块里,静态代码块在类加载的时候执行且只执行一次
*/
static {
//1、注册驱动
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException exception) {
exception.printStackTrace();
}
}
/**
* 获取数据库连接
* @return
*/
public static Connection getConnection() throws SQLException {
//2、获取数据库连接
Connection connection =
DriverManager.getConnection("jdbc:mysql://localhost:3306/test_database","root","123456");
return connection;
}
/**
* 关闭数据库资源
*/
public static void JDBCclose(Connection connection, Statement statement, ResultSet resultSet) throws SQLException {
if (resultSet != null) {
resultSet.close();
}
if (statement != null) {
statement.close();
}
if (connection != null) {
connection.close();
}
}
}
六、案例
下面展示示例代码
import java.sql.*;
/**
* jbdc 事务
* 在不开启jdbc情况下,只要执行了任意一条DML语句(增删改语句) 都会有一次事务的提交
* 例子:银行 A账户 转账 给B账户 (事务的一致性)A账务 扣钱,发生异常,B不加钱
*/
public class JDBCDemo04 {
public static void main(String[] args) {
Connection connection =null;
PreparedStatement preparedStatement = null;
try {
//注册驱动
Class.forName("com.mysql.jdbc.Driver");
//获取链接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test_database","root","123456");
//开启事务 关闭自动提交模式 开启手动提交
connection.setAutoCommit(false);
//获取数据库操作对象并且预编译sql
preparedStatement = connection.prepareStatement("update bank set a_bank = a_bank - ? where id = ?");
//给占位赋值
preparedStatement.setDouble(1,10000);
preparedStatement.setInt(2,1);
//执行sql语句
int num = preparedStatement.executeUpdate();
//模拟异常
String str = null;
str.split("123");
//给账户加钱
preparedStatement = connection.prepareStatement("update bank set b_bank = b_bank + ? where id = ?");
//给占位赋值
preparedStatement.setDouble(1,10000);
preparedStatement.setInt(2,1);
//执行sql语句
num += preparedStatement.executeUpdate();
System.out.println(num == 2?"执行成功":"执行失败");
//提交事务
connection.commit();
}catch(Exception e){
e.printStackTrace();
}finally {
try {
if(connection != null){
//事务回滚
connection.rollback();
}
if (preparedStatement != null) {
preparedStatement.close();
}
if (connection != null) {
connection.close();
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}