JDBC基础编程六步+应用实例_最易理解!
Posted 葡萄籽-June
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JDBC基础编程六步+应用实例_最易理解!相关的知识,希望对你有一定的参考价值。
JDBC(Java DataBase Connectivity)
本文主要是通过简单的例子理解JDBC运行的基础原理,以及简单的JDBC工具类如何创建与使用。
文章目录
前言
JDBC的理解
首先思考两个问题。
1、为何SUN定制的一套JDBC接口? (因为每一个数据库的底层实现原理都不一样。)
2、JDBC的本质? (一套接口。)
通过下面的图进行一个简单的梳理与关系介绍。
由上述可知,JDBC提供一套连接并操作数据库的接口,各大数据库厂家根据此接口编写了各自的相应实现类。
一、大致原理
本节主要通过简单的栗子来了解相应的底层原理。(作为编程人员,有时不需要磕相关的底层代码到底吖)
走一下思路,捋一下~
1、连接数据库的接口
2、实现者将各个数据库根据接口封装具体连接驱动的方法,实现此接口
3、调用者需要先创建我们想要连接的数据库对象
4、调用者根据相关的数据库来带调用此数据库的具体连接方法
自定义JDBC接口:
public interface JDBC {
default void getConnection(){};
}
mysql实现类:
public class MySQL implements JDBC{
@Override
public void getConnection(){
// 具体的实现代码与java程序员无关系,只需要会使用即可
System.out.println("连接MySQL数据库成功!");
}
}
Oracle实现类:
public class Oracle implements JDBC{
@Override
public void getConnection(){
System.out.println("连接Oracle数据库成功!");
}
}
应用主类:
public class JavaProgrammer {
public static void main(String[] args) throws Exception {
// 模拟实现
// 创建需要连接的数据库对象
JDBC jdbc = new MySQL();
// 调用连接方法
jdbc.getConnection();
// 如上,只是使用不同的数据库对象
JDBC jdbc1 = new Oracle();
jdbc1.getConnection();
}
}
二、Java中JDBC编程六步(Important!!!)
1.JDBC六大步骤
- 注册驱动
作用:告诉Java程序,即将要连接的是哪个品牌的数据库。- 获取连接
表示JVM进程和数据库进程之间的通道打开了,这属于进程之间的通信。重量级的,使用完后一定要关闭通道。- 获取数据库操作对象
专门执行sql语句的对象- 执行SQL语句
主要执行DQL和DML- 处理查询结果集
只有当第四步执行的是DQL(SELECT语句)时,才会有第五步的处理查询结果集。- 释放资源
使用完资源一定要关闭资源。Java和数据库之间的通信属于进程间的通信。若开启之后,一定要关闭!
【注】一般资源的释放需要从里到外进行释放。
2. 实际应用
在实际应用之前,先介绍一下需要了解的知识点
2.1 应用前准备
2.1.1 JDBC中url连接地址
格式:
jdbc:mysql://[host][,failoverhost...][:port]/[database] [?propertyName1][=propertyValue1][&propertyName2][=propertyValue2]...
mysql的JDBC连接时的URL参数:
主要参数 | 参数说明 |
---|---|
user | 数据库用户名(用于连接数据库) |
password | 用户密码(用于连接数据库) |
useUnicode | 是否使用Unicode字符集,如果参数characterEncoding设置为gb2312或gbk,本参数值必须设置为true |
characterEncoding | 当useUnicode设置为true时,指定字符编码。比如可设置为gb2312或gbk |
autoReconnect | 当数据库连接异常中断时,是否自动重新连接? |
autoReconnectForPools | 是否使用针对数据库连接池的重连策略 |
failOverReadOnly | 自动重连成功后,连接是否设置为只读? |
maxReconnects | autoReconnect设置为true时,重试连接的次数 |
initialTimeout | autoReconnect设置为true时,两次重连之间的时间间隔,单位:秒 |
connectTimeout | 和数据库服务器建立socket连接时的超时,单位:毫秒。 0表示永不超时,适用于JDK 1.4及更高版本 |
socketTimeout | socket操作(读写)超时,单位:毫秒。 0表示永不超时 |
举个小例子:
String url = "jdbc:mysql://localhost:3306/my_employees?useUnicode=true&characterEncoding=utf8&useSSL=true";
上述例子中,my_employees是数据库的名字;serverTimezone:url的时区使用中国标准时间,UTC+8才是准确时间。
2.1.2 URL:统一资源定位符
URL包括:
- 协议
- IP
- PORT
- 资源名
2.2 Statement作为数据库操作对象的应用实例
下面的栗子主要通过 MySQL驱动进行应用吖(其实什么驱动都可以,只要了解了原理,就很容易定位需要改动哪里啦)
根据上述JDBC连接数据库的6大步骤进行实例操作。(上述主要的六大步骤已经在代码中注释。)
package demo;
import com.mysql.jdbc.Driver;
import com.mysql.jdbc.Statement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
* @description: 通过简单的栗子,使用JDBC连接数据库并进行交互。
* @author: Grape_Pip
*/
public class JDBCDemo {
// 主入口
public static void main(String[] args) throws SQLException {
// 插入数据
insertData();
// 更新数据
updateData();
}
/**
* @MethodName : insertData()
* @Description //TODO 插入数据
* @Param []
* @return void
**/
public static void insertData(){
Statement stmt = null; //数据库操作对象
Connection conn = null; //连接对象
try {
// * 1. **注册驱动**
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
// oracle的驱动: DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
// * 2. **获取连接**
String url = "jdbc:mysql://localhost:3306/myemployees?characterEncoding=utf8&useSSL=true";
String user = "root";
String password = "root";
conn = DriverManager.getConnection(url, user, password);
// System.out.println("数据库连接对象:"+conn);
// * 3. **获取数据库操作对象**
stmt = (Statement) conn.createStatement();
// * 4. **执行SQL语句**
String sql = "INSERT INTO departments(department_id,department_name,manager_id,location_id) \\n" +
"VALUES(117,'Twa',100,1700);";
/*
* 专门执行DML语句的(insert delete update)
* 返回值是“影响数据库中的记录条数”
* */
int res = stmt.executeUpdate(sql);
System.out.println(res == 1 ? "保存成功" : "保存失败");
// * 5. **处理查询结果集**
/*若不是select则不需要处理结果集,只需要判断是否成功即可
* */
} catch (SQLException e) {
e.printStackTrace();
} finally {
// * 6. **释放资源**
/*
* 为了保证资源一定释放,在finally语句块中关闭资源
* 并且要遵循从小到大依次关闭
* 分别对其try-catch(如果一起try-catch,顺序执行,上面的捕获下面的语句就不执行了,所以要分开)
* */
try {
if (stmt != null) {
stmt.close();
}
} catch (SQLException e1) {
e1.printStackTrace();
}
try {
if (conn != null) {
conn.close();
}
} catch (SQLException e2) {
e2.printStackTrace();
}
}
}
/**
* @MethodName : updateData()
* @Description //TODO 更新数据
* @Param
* @return
**/
public static void updateData(){
Statement stmt = null;
Connection conn = null;
try {
// * 1. **注册驱动**
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
// * 2. **获取连接**
String url = "jdbc:mysql://localhost:3306/myemployees?autoReconnect=true&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=true";
String user = "root";
String password = "root";
conn = DriverManager.getConnection(url, user, password);
// * 3. **获取数据库操作对象**
stmt = (Statement) conn.createStatement();
// * 4. **执行SQL语句**
String sql = "UPDATE departments set department_name='Rup' where department_id=117;";
int res = stmt.executeUpdate(sql);
System.out.println(res == 1 ? "更新成功" : "更新失败");
// * 5. **处理查询结果集**
} catch (SQLException e) {
e.printStackTrace();
} finally {
// * 6. **释放资源**
try {
if (stmt != null) {
stmt.close();
}
} catch (SQLException e1) {
e1.printStackTrace();
}
try {
if (conn != null) {
conn.close();
}
} catch (SQLException e2) {
e2.printStackTrace();
}
}
}
}
2.3 PrepareStatement作为数据库操作对象的应用实例
PrepareStatement的大致原理是预先对SQL语句的框架进行编译,然后再给SQL语句传“值”。
PrepareStatement 有以下特点:
1、接口继承了java.sql.Statement
2、属于预编译的数据库操作对象。
package demo;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
/**
* @description: 使用PreparedStatement作为数据库操作对象
* @author: Grape_Pip
*/
public class PrepareStmtJDBCDemo {
public static void main(String[] args) {
Connection conn=null;
PreparedStatement pstmt=null;
ResultSet rs=null;
String loginName="test";
String loginPwd="1234";
try{
// 1、注册驱动
Class.forName("com.mysql.jdbc.Driver");
// 2、连接数据库
String url = "jdbc:mysql://localhost:3306/myemployees?characterEncoding=utf8";
String user = "root";
String password = "root";
conn=DriverManager.getConnection(url,user,password);
// 3、获取预编译的数据库操作对象
//SQL语句的?,表示一个占位符。一个?接受一个值。【注】占位符不可使用单引号
String sql = "select * from employees where loginName= ? and loginPwd= ? ";
//程序执行到此,会发送sql语句给DBMS。然后DBMS进行sql语句的预先编译。
pstmt= conn.prepareStatement(sql);
//给占位符?传值,第一个问号下标是1,第二个?下标是2。【注】JDBC中,所有下标从1开始
pstmt.setString(1,loginName);
pstmt.setString(2,loginPwd);
// 4、执行sql语句
rs=pstmt.executeQuery();
// 5、处理查询结果集
if (rs.next()){
//登陆成功
}
}catch(Exception e){
e.printStackTrace();
}finally {
// 6、释放资源
if (rs != null) {
try {
rs.close();
} catch (Exception e1) {
e1.printStackTrace();
}
}
if (pstmt != null) {
try {
pstmt.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
}
}
2.4 Statement 与 PreparedStatement 的区别
Statement | PreparedStatement | |
---|---|---|
SQL注入问题 | 存在 | 解决 |
编译 | 编译一次执行一次 | 编译一次,可执行N次 |
效率 | 较低 | 较高 |
安全性 | 直接编译,不做检查(会导致SQL注入) | 会在编译阶段做类型的安全检查 |
上述表中提到SQL注入,那么什么是SQL注入呢?有兴趣可以查看本篇文章☞SQL注入_实例总结
3、总结——JDBC工具类的封装
思考一个问题:
可以看到上述2.2中,以Statement为数据库操作对象时所举的例子包含有插入和更新方法,在每个方法中都有代码重复的地方,那么为了提高代码的复用率,是不是可以把代码重复(可复用)的地方进行封装呢?
————此时,JDBC工具类的封装变得更加有必要了。
在封装工具类之前,需要考虑清楚,哪一步是否值得封装,封装的时候使用什么方式会更合适等问题。带着这些问题,我们来进行工具类的封装。
首先,对于连接数据库的6步步骤中,都有哪些步骤可以进行封装?
- 1、加载驱动;2、连接数据库;3、创建数据库对象 ;6、关闭资源
其次,考虑每个步骤的在使用过程中或者从其性质方面上看,用什么进行封装编译运行比较好?
- 加载驱动和获取连接(加载驱动需要损耗一定的时间,而获取连接是要打开程序与数据库之间的通信,已经明确需要加载以及获取连接,此时可以在编译时就准备好。因此可以放在静态代码块中,使其在类编译的时候就加载驱动和获取对应的连接。)
- 创建连接对象(每次进行通信都需要获取数据库操作对象,因此,把它封装进静态的方法中,其他类可以直接类名.调用,而不需要创建对象,造成资源的浪费。)
- 关闭资源(关闭资源是很重要的一步,其实就是关闭通信。因此,把它封装进静态的方法中,并直接类名.调用即可。)
最终,封装为一个基本的JDBC工具类,如下代码所示。
package util;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
/**
* @description: JDBC工具类,简化JDBC编程
* @description: 工具类中的方法都是私有的。因为工具类当中的方法都是静态的,不需要new对象,直接采用类名调用。
* @author: Grape_Pip
*/
public class DBUtil {
private static String driver;
private static String url;
private static String user;
private static String password;
/**
* @Description //TODO 静态代码块:类加载的时候就加载
* @Param
* @return
**/
static {
//新建属性对象
Properties property = new Properties();
//通过反射,新建字符输入流,读取mysql.properties文件
InputStream input = DBUtil.class.getClassLoader().getResourceAsStream("jdbc.properties");
//将输入流中读取到的属性,加载到properties属性及对象中
try {
//将input 加载到property对象中
property.load(input);
//根据键,获取properties中对应的值赋值
driver = property.getProperty("driver");
url = property.getProperty("url");
user = property.getProperty("user");
password = property.getProperty("password");
} catch (IOException e) {
e.printStackTrace();
}
// 加载驱动
try {
Class.forName(driver);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* @Description //TODO 获取数据库对象
* @Param []
* @return java.sql.Connection
**/
private static Connection getConnection() throws SQLException {//不需要try-catch,向上抛出被捕获即可
return DriverManager.getConnection(url,user,password);
}
/**
* @Description //TODO 关闭资源
* @Param [conn] 连接对象
* @Param [stmt] 数据库操作对象
* @Param [rs] 结果集
* @return void
**/
private static void close(Connection conn, Statement stmt, ResultSet rs){
if (rs!=null){
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (stmt!=null){
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (conn!=null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
[补充]_JDBC工具类的应用
本小结主要是根据上述JDBC工具类的封装后,进行实例应用。
package util;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
/**
* @description: JDBC工具类的使用 :测试DBUtil;模糊查询
* @author: Grape_Pip
*/
public class JDBCTest {
public static void main(String[] args) {
Connection conn=null;
PreparedStatement pstmt=null;
ResultSet rs=null;
try {
// 获取连接
conn=DBUtil.getConnection();
// 获取预编译的数据库操作对象
/*错误的写法:
String sql="select employee_id,last_name" +
"from employees" +
"where last_name LIKE '?%'";
pstmt=conn.prepareStatement(sql);
pstmt.setString(1,"A");
*/
String sql="select employee_id,last_name " +
"from employees " +
"where last_name like ?";
pstmt=conn.prepareStatement(sql);
pstmt.setString(1,"A%");
rs=pstmt.executeQuery();
while (rs.next()){
System.out.println("员工编号:"+rs.getInt("employee_id")+
"\\t员工名:"+rs.getString("last_name"));
}
} catch (Exception e) {
e.printStackTrace();
}finally {
// 释放资源
DBUtil.close(conn,pstmt,rs);
}
}
}
其实,上述工具类的封装只是一种封装方式。自己进行封装的时候,可以根据自己的需求设计JDBC的工具类。
本周的JDBC分享就到这里叭。~~~
以上是关于JDBC基础编程六步+应用实例_最易理解!的主要内容,如果未能解决你的问题,请参考以下文章