参考博文: JDBC是什么
10.1JDBC是什么
10.1.1 JDBC介绍
JDBC(Java DataBase Connectivity) 称为Java数据库连接,它是一种用于数据库访问的应用程序API,由一组用Java语言编写的类和接口组成,有了JDBC就可以用同一的语法对多种关系数据库进行访问,而不用担心其数据库操作语言的差异。 有了JDBC,就不必为访问Mysql数据库专门写一个程序,为访问Oracle又专门写一个程序等等。
10.1.2 JDBC的体系结构
JDBC的结构可划分为两层:一个是面向底层的JDBC Driver Interface(驱动程序管理器接口),另一个是面向程序员的JDBC API。
使用JDBC编程,可让开发人员从复杂的驱动器调用命令和函数中解脱出来,可以致力于应用程序中的关键地方。JDBC支持不同的关系数据库,这使得程序的可移植性大大加强。JDBC API是面向对象的,可以让用户把常用的方法封装为—个类,以备后用。但是它也有缺点,一是使用JDBC,访问数据记录的速度会受到一定程度的影响。二是JDBC结构中包含不同厂家的产品,这就给更改数据源带来了很大的麻烦。
10.1.3 JDBC核心接口与类
JDBC核心类库包含在java.sql包中。
接口:
Connection:特定数据库的连接(会话)。在连接上下文中执行SQL语句并返回结果。
PreparedStatement:表示预编译的 SQL 语句的对象。
Statement:用于执行静态 SQL 语句并返回它所生成结果的对象。
ResultSet :表示数据库结果集的数据表,通常通过执行查询数据库的语句生成 。
CallableStatement :用于执行 SQL 存储过程的接口 。
类:
DriverManager:负责管理JDBC驱动程序。使用JDBC驱动程序之前,必须先将驱动程序加载并注册后才可以使用,同时提供方法来建立与数据库的连接。
SQLException:有关数据库操作的异常。
10.1.4 JDBC优缺点
优点:
JDBC使得编程人员从复杂的驱动器调用命令和函数中解脱出来,可以致力于应用程序中的关键地方。
JDBC支持不同的关系数据库,这使得程序的可移植性大大加强。
JDBC API是面向对象的,可以让用户把常用的方法封装为—个类,以备后用。
缺点:
使用JDBC,访问数据记录的速度会受到一定程度的影响。
JDBC结构中包含不同厂家的产品,这就给更改数据源带来了很大的麻烦。
10.1.5 数据库驱动程序
第一类: jdbc-odbc桥
把JDBC API调用转换成ODBC API 调用, 然后ODBC API调用针对供应商的ODBC 驱动程序来访问数据库, 即利用JDBC- ODBC 桥通过ODBC来存储数据源 。
第二类: 本地API驱动
本地api驱动直接把jdbc调用转变为数据库的标准调用再去访问数据库. 这种方法需要本地数据库驱动代码。
第三类: 网络协议驱动
它使用一种与具体数据库无关的协议将数据库请求发送给一个中间服务器。
第四类: 本地协议驱动
这种驱动直接把jdbc调用转换为符合相关数据库系统规范的请求.由于4型驱动写的应用可以直接和数据库服务器通讯,这种类型的驱动完全由java实现,因此实现了平台独立性。 通常开发中多采用第四种方式,这种驱动不需要先把jdbc的调用传给odbc或本地数据库接口或者是中间层服务器,所以它的执行效率是非常高的驱动。
注:各数据库厂商均提供对 JDBC 的支持,即提供数据库连接使用的驱动程序文件需要为数据库应用程序正确加载驱动程序文件以获得数据库连接,实施操作。
10.1.6 创建JDBC应用程序的步骤 *(重要)
1. 载入JDBC驱动程序
2. 定义连接URL
3. 建立连接
4. 创建Statement对象
5. 执行查询或更新
6. 结果处理
7. 关闭连接
10.2 实现JDBC程序
10.2.1 实现第一JDBC程序
数据库文件:
CREATE DATABASE jdbc;
USE jdbc;
CREATE TABLE users(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(40),
PASSWORD VARCHAR(40),
email VARCHAR(60),
birthday DATE
)CHARACTER SET utf8 COLLATE utf8_general_ci;
INSERT INTO users(NAME,PASSWORD,email,birthday) VALUES('zs','123456','zs@sina.com','1980-12-04');
INSERT INTO users(NAME,PASSWORD,email,birthday) VALUES('lisi','123456','lisi@sina.com','1981-12-04');
INSERT INTO users(NAME,PASSWORD,email,birthday) VALUES('wangwu','123456','wangwu@sina.com','1979-12-04');
CREATE TABLE tb_user (
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,-- id主键
username VARCHAR(40) NOT NULL,-- 账户名称,设置不为空
PASSWORD VARCHAR(40) NOT NULL,-- 密码,设置不为空
NAME VARCHAR(40) DEFAULT NULL,-- 用户真实姓名,默认为空
gender VARCHAR(20) DEFAULT NULL,-- 用户性别,默认为空
phonenumber VARCHAR(30) DEFAULT NULL, -- 用户手机号码,默认为空
identitycode VARCHAR(30) DEFAULT NULL-- 用户身份证号码,默认为空
);
INSERT INTO tb_user VALUES(1,'张三','123','张三','male','13888888888','110202107075023');
读取数据库中的users表,并将读取到的数据输出到控制台:
package chapter10;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Date;
public class Example01 {
public static void main(String[] args) throws SQLException {
Statement stmt = null;
ResultSet rs = null;
Connection conn = null;
try {
// 1. 注册数据库的驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2.通过DriverManager获取数据库连接
String url = "jdbc:mysql://localhost:3306/jdbc?serverTimezone=GMT%2B8";
String username = "root";//数据库名称
String password = "123456";//数据库密码
conn = DriverManager.getConnection (url, username,
password);
// 3.通过Connection对象获取Statement对象
stmt = conn.createStatement();
// 4.使用Statement执行SQL语句。
String sql = "select * from users";
rs = stmt.executeQuery(sql);
// 5. 操作ResultSet结果集
System.out.println("id | name | password | email | birthday");
while (rs.next()) {
int id = rs.getInt("id"); // 通过列名获取指定字段的值
String name = rs.getString("name");
String psw = rs.getString("password");
String email = rs.getString("email");
Date birthday = rs.getDate("birthday");
System.out.println(id + " | " + name + " | " + psw + " | " + email
+ " | " + birthday);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally{
// 6.回收数据库资源
if(rs!=null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
rs = null;
}
if(stmt!=null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if(conn!=null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
}
}
}
注意:
1、注册驱动:虽然使用DriverManager.registerDriver(new com.mysql.jdbc.Driver())方法也可以完成注册,但此方式会使数据库驱动被注册两次。这是因为Driver类的源码,已经在静态代码块中完成了数据库驱动的注册。所以,为了避免数据库驱动被重复注册,需要在程序中使用Class.forName()方法加载驱动类。需要注意的是,MySQL 5.5及之前的版本使用的是旧版驱动,使用Class.forName("com.mysql.jdbc.Driver")方式加载驱动类;MySQL 5.6以及之后的版本需要更新到新版驱动,使用Class.forName("com.mysql.cj.jdbc.Driver")方式加载驱动类。
2、释放资源:由于数据库资源非常宝贵,数据库允许的并发访问连接数量有限,所以,当数据库资源使用完毕后,一定要记得释放资源。为了保证资源的释放,在Java程序中,应该将最终必须要执行的操作放在finally代码块中。
10.2.2 PreparedStatement对象
SQL语句的执行是通过Statement对象实现的。Statement对象每次执行SQL语句时,都会对其进行编译。当相同的SQL语句执行多次时,Statement对象就会使数据库频繁编译相同的SQL语句,从而降低数据库的访问效率。
为了解决上述问题,Statement提供了一个子类PreparedStatement。PreparedStatement对象可以对SQL语句进行预编译,预编译的信息会存储在PreparedStatement对象中。当相同的SQL语句再次执行时,程序会使用PreparedStatement对象中的数据,而不需要对SQL语句再次编译去查询数据库,这样就大大提高了数据的访问效率。
package cn.itcast.jdbc.example;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement ;
import java.sql.SQLException;
public class Example02 {
public static void main(String[] args) throws SQLException {
Connection conn = null;
PreparedStatement preStmt = null;
try {
// 加载数据库驱动
Class.forName("com.mysql.cj.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/jdbc?serverTimezone=GMT%2B8";
String username = "root";
String password = "123456";
// 创建应用程序与数据库连接的Connection对象
conn = DriverManager.getConnection(url, username, password);
// 执行的SQL语句
//String sql = "INSERT INTO users(name,password,email,birthday)"
// + "VALUES(wl,123456,wl@sohu.com,1982-9-10)";
String sql = "INSERT INTO users(name,password,email,birthday)"
+ "VALUES(?,?,?,?)";
// 1.创建执行SQL语句的PreparedStatement对象
preStmt = conn.prepareStatement(sql);
// 2.为SQL语句中的参数赋值
preStmt.setString(1, "wl");
preStmt.setString(2, "123456");
preStmt.setString(3, "wl@sina.com");
preStmt.setString(4, "1982-12-23");
// 3.执行SQL
preStmt.executeUpdate();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally { // 释放资源
if (preStmt != null) {
try {
preStmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
preStmt = null;
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
}
}
}
10.2.3 ResultSet对象
ResultSet主要用于存储结果集,可以通过next()方法由前向后逐个获取结果集中的数据,如果想获取结果集中任意位置的数据,则需要在创建Statement对象时,设置两个ResultSet定义的常量,具体设置方式如下:
Statement st = conn.createStatement(ResultSet.TYPE_SCROLL_INSENITIVE,
ResultSet.CONCUR_READ_ONLY);
ResultSet rs = st.excuteQuery(sql);
在上述方式中,常量“Result.TYPE_SCROLL_INSENITIVE”表示结果集可滚动,常量“ResultSet.CONCUR_READ_ONLY”表示以只读形式打开结果集。
package chapter10;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class Example03 {
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/jdbc?serverTimezone=GMT%2B8";
String username = "root";
String password = "123456";
//1.获取Connection对象
conn = DriverManager.getConnection(url, username, password);
String sql = "select * from users";
//2.创建Statement对象并设置常量
stmt =conn.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY);
//3.执行SQL并将获取的数据信息存放在ResultSet中
ResultSet rs = stmt.executeQuery(sql);
//4.取出ResultSet中指定数据的信息
System.out.print("第2条数据的name值为:");
rs.absolute(2); //将指针定位到结果集中第2行数据
System.out.println(rs.getString("name"));
System.out.print("第1条数据的name值为:");
rs.beforeFirst(); //将指针定位到结果集中第1行数据之前
rs.next(); //将指针向后滚动
System.out.println(rs.getString("name"));
System.out.print("最后一条数据的name值为:");
rs.afterLast(); //将指针定位到结果集中最后一条数据之后
rs.previous(); //将指针向前滚动
System.out.println(rs.getString("name"));
} catch (Exception e) {
e.printStackTrace();
} finally { // 释放资源
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
}
}
}
10.2.4 使用JDBC完成数据的增删改查
1、构建一个用户实体类User类来映射数据表中的用户的属性;
2、创建一个DBUtils工具类用于封装数据库的连接信息;
3、创建一个DAO类,在该类中编写对数据库进行增删改查的方法。
User类
package chapter10;
import java.util.Date;
public class User {
private int id;
private String username;
private String password;
private String email;
private Date birthday;
public User() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
JDBCUtils
package chapter10;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class JDBCUtils {
// 加载驱动,并建立数据库连接
public static Connection getConnection() throws SQLException,
ClassNotFoundException {
Class.forName("com.mysql.cj.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/jdbc?serverTimezone=GMT%2B8";
String username = "root";
String password = "123456";
Connection conn = DriverManager.getConnection(url, username,
password);
return conn;
}
// 关闭数据库连接,释放资源
public static void release(Statement stmt, Connection conn) {
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
}
public static void release(ResultSet rs, Statement stmt,
Connection conn){
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
rs = null;
}
release(stmt, conn);
}
}
UsersDao类主要用于程序与数据库的交互,在该类中封装了对数据库表users的添加、查询、删除和更新等操作。
JdbcUserOpeTest 测试
package chapter10;
import java.util.Date;
import chapter10.UsersDao;
import chapter10.User;
import java.util.ArrayList;
import java.util.Scanner;
public class JdbcUserOpeTest {
public static void main(String[] args) {
System.out.println("//");
System.out.println("/* 选择要进行的操作 ");
System.out.println("/* 1、输出所有数据 2、插入数据 3、根据姓名查找记录 4、修改用户数据 5、删除用户 6、退出 ");
System.out.println("//");
Scanner scan = new Scanner(System.in);
int scanInt = scan.nextInt();
//System.out.println("输入的数据为:" + scanInt);
switch (scanInt) {
case 1:
OutAllUsers();//输出集合中的数据
break;
case 2:
InserUser(); //插入一条记录
break;
case 3:
FindUserById("zl"); //
break;
case 4:
//修改用户记录
UpdateUser();
break;
case 5:
//删除用户
OutAllUsers();//输出集合中的数据
break;
case 6:
OutAllUsers();//输出集合中的数据
break;
default:
break;
}
}
//插入一条记录
public static boolean InserUser() {
UsersDao ud = new UsersDao();
User user=new User();
user.setId(7);
user.setUsername("hl");
user.setPassword("123");
user.setEmail("hl@sina.com");
user.setBirthday(new Date());
boolean b=ud.insert(user);
if(b) {
System.out.println("插入成功!");
OutAllUsers();//输出集合中的数据
}
else {
System.out.println("插入失败!");
}
return true;
}
//循环输出集合中的数据
public static void OutAllUsers()
{
//创建一个名称为usersDao的对象
UsersDao usersDao = new UsersDao();
//将UsersDao对象的findAll()方法执行后的结果放入list集合
ArrayList<User> list = usersDao.findAll();
//循环输出集合中的数据
System.out.println("/User表数据//");
System.out.println("id | name | password | email | birthday");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i).getId() + " | " + list.get(i).getUsername() + " | " + list.get(i).getPassword() + " | " + list.get(i).getEmail()
+ " | " + list.get(i).getBirthday());
// System.out.println("第" + (i + 1) + "条数据的username值为:"
// + list.get(i).getUsername());
}
}
//根据 name 找到记录 返回第一个结果
public static void FindUserById(String name) {
UsersDao usersDao = new UsersDao();
User user = usersDao.findByName(name);
//System.out.println("id为1的User对象的name值为:"+user.getUsername());
System.out.println("id | name | password | email | birthday");
System.out.println(user.getId() + " | " + user.getUsername() + " | " + user.getPassword() + " | " + user.getEmail()
+ " | " + user.getBirthday());
}
//修改用户记录
public static void UpdateUser() {
System.out.println("输入要修改用户的ID");
Scanner scan = new Scanner(System.in);
int scanInt = scan.nextInt();
// 修改User对象的数据
UsersDao usersDao = new UsersDao();
User user = new User();
user.setId(scanInt);
user.setUsername("zhaoxiaoliu");
user.setPassword("456");
user.setEmail("zhaoxiaoliu@sina.com");
user.setBirthday(new Date());
boolean b = usersDao.update(user);
if(b) {
System.out.println("修改成功!");
OutAllUsers();//输出集合中的数据
}
else {
System.out.println("修改失败!");
}
}
//删除用户
public static void DeleteUser() {
OutAllUsers();//
System.out.println("输入要删除用户的ID");
Scanner scan = new Scanner(System.in);
int scanInt = scan.nextInt();
// 删除操作
UsersDao usersDao = new UsersDao();
boolean b = usersDao.delete(scanInt);
if(b) {
System.out.println("删除成功,删除后记录为!");
OutAllUsers();//输出集合中的数据
}
else {
System.out.println("删除失败!");
}
}
}