JDBC访问数据库操作详解(一)之JDBC基本用法:以MySQL为例
一、JDBC简介
1. JDBC是什么
Java Database Connectivity(JDBC)是Java程序访问数据库的方式
-
提供了一套用于访问数据库的接口(API),其独立于特定的数据库(不区分特定数据库)
-
不同的数据库产品,实现方式和通信协议都是不一样的
-
由数据库厂商对接口进行具体实现,然后以
jar包
的形式提供实现类,这个jar包称为数据库驱动包注:jar包就是一个以
.jar
结尾的文件,其中包含了一些已经写好的类和接口,只需要将jar包导入到项目中,就可以直接使用jar包中的类和接口
2. 相关API
JDBC相关的类和接口都在java.sql包中
类/接口 | 作用 |
---|---|
DriverManager | 用于管理数据库驱动,建立与数据库的连接 |
Connection | 表示与数据库的连接 |
Statement | 用于执行SQL语句,并返回结果集ResultSet |
PreparedStatement | 用于执行预编译的SQL语句 |
ResultSet | 表示结果集,封装了数据库返回的结果 |
二、JDBC基本用法
1. 下载jar包
下载地址:https://dev.mysql.com/downloads/connector/j/
注意要下载与自己操作系统以及mysql版本相对应的jar包版本
2. jar包的管理
需要将jar包导入到项目中才能使用
导入:
- 在项目根目录下创建一个lib文件夹
- 将jar包拷贝到该文件夹中
- 右击jar包——>Build Path——>Add to Build Path
Referenced Libraries
表示引用的库,jar包就被导入到该库中
移除:
Referenced Libraries
——>右击jar包——>Build Path——>Remove from Build Path
批量管理:右击项目——>Build Path——>Configure Build Path——>Libraries
3. JDBC访问数据库的操作步骤
1. 注册驱动
2. 获取连接
3. 获取Statement,用于执行sql语句
4. 执行sql语句
5. 处理结果
6. 关闭资源
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class Test01 {
public static void main(String[] args) {
String driverClassName = "com.mysql.cj.jdbc.Driver";
String url = "jdbc:mysql://localhost:3306/my_database?
useUnicode=true&characterEncoding=utf8&serverTimezone=GMT";
String username = "root";
String password = "aa54545677877";
/**
*提前声明Statement,Connection,不要在try里面声明,否则finally里关闭资源时访
*问不到
*/
Statement stmt = null;
Connection conn = null;
try {
/**1.注册驱动
* Class.forName方法用于加载某个类,并返回这个类的对象,当然这里不需要接收
* 这个对象
* Class.forName方法需要捕获一个ClassNotFoundException异常
*/
Class.forName(driverClassName);
/**2.获取连接
* url固定写法:"jdbc:mysql://服务器ip地址(如果是本地的话可以写
* localhost或是127.0.0.1):数据库连接端口
* (如果没有修改过,mysql默认的端口号是3306)/数据库名”
* username和password是自己数据库的用户名和密码
* DriverManager.getConnection需要捕获一个SQLException异常
*/
conn = DriverManager.getConnection(url,username,password);
//3.获取Statement,用于执行sql语句
stmt = conn.createStatement();
/**4.执行sql语句
* 调用int executeUpadte(sql)方法,用于执行更新操作(增、删、改),返回
* 受影响的行数
*/
String sql = "insert into t_user(username,password,age) values
('aaa','123',18)";
int num = stmt.executeUpdate(sql);
//5.处理结果
System.out.println(num); //对于更新操作,结果一般无需处理
} catch (SQLException e) {
e.printStackTrace();
}catch (ClassNotFoundException e) {
e.printStackTrace();
}finally {
/**6.关闭资源
* 关闭的顺序要与打开的顺序相反:先关Statement,再关Connection
* close()方法也要捕获一个SQLException异常
*/
if(stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
4. 数据库中文乱码的解决
细心的朋友也许会发现在上面的代码中url后面我加上了
?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT
这句话会起到什么作用呢?
上面的代码中第四步执行sql语句时,如果我们把sql 语句做一点小小的改变,例如:
`String sql = "insert into t_user(username,password,age) values
('王二','123',18)";`
把原来的’aaa’改成’王二’我们会发现表中的这项数据会出现乱码。
当我们通过控制台插入中文数据是没有问题的,而当我们通过Java插入中文数据却可能会出现乱码,因为Java使用的编码跟我们的数据库编码可能不一致,所以在url中我们需要去指定编码
?useUnicode=true&characterEncoding=utf8
当然这样做完你还是可能会出现报错
原因是你使用了最新版驱动,使用旧版驱动不会报错,可以在url后面再加上serverTimezone=GMT
来解决,所以建议url的写法参照我上面的代码,当然关于这个报错还有其他解决方案,想要具体了解的话,请参考下面链接:https://blog.csdn.net/dreamboy_w/article/details/96505068
5. 数据库操作
5.1 更新
使用int executeUpdate(sql)
方法,用于执行更新操作(增、删、改),返回影响的行数
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class Test02_数据库操作 {
public static void main(String[] args) {
/*
*写增删改或建表、删表或建数据库、删数据库的sql语句作为参数传入update方法中即可
*实现更新操作
*String sql = "insert into t_user(username,password,age) values
*('aaa','123',18)";
*String sql = "delete from t_user where id>=5";
*String sql = "update t_user set password=666,age=18 where
*username='tom'";
*String sql = "create table t_student(id int,name varchar(100))";
*String sql = "drop table t_student";
*String sql = "drop database my_database";
*/
update(sql);
}
/*
* 更新操作,适用于:
* insert/delete/update/create/drop
* 凡是在命令行中返回Query OK, 1 row affected(0.01 sec)这样形式的都适用
*/
public static void update(String sql) {
Connection conn = null;
Statement stmt = null;
String driverClassName = "com.mysql.cj.jdbc.Driver";
String url = "jdbc:mysql://localhost:3306/my_database?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT";
String username = "root";
String password = "aa54545677877";
try {
Class.forName(driverClassName);
conn = DriverManager.getConnection(url, username, password);
stmt = conn.createStatement();
int num = stmt.executeUpdate(sql);
System.out.println(num);//对于更新操作,结果一般无需处理
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
if(stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
5.2 查询
使用ResultSet executeQuery(String sql)
方法,用于执行查询操作,返回查询到的结果集
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
public class Test02_数据库操作 {
public static void main(String[] args) {
/*
* 实际开发中,严禁写select*
* 原因:1.效率低 2.可读性差 3.可能会导致错误
*/
String sql = "select id,username,password,age from t_user";
List<User> users = query(sql);
System.out.println(users);
}
/*
* 查询操作
*/
public static List<User> query(String sql) {
List<User> list = new ArrayList<User>();
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
String driverClassName = "com.mysql.cj.jdbc.Driver";
String url = "jdbc:mysql://localhost:3306/my_database?
useUnicode=true&characterEncoding=utf8&serverTimezone=GMT";
String username = "root";
String password = "aa54545677877";
try {
Class.forName(driverClassName);
conn = DriverManager.getConnection(url, username, password);
stmt = conn.createStatement();
rs = stmt.executeQuery(sql);//返回查询到的结果,以ResultSet形式返回
/*
* ResultSet.next()方法:默认指向第一行上面,调用next()方法指针往下走一
*次,
*若有数据返回true,没有返回false
*作用:1.指向下一条数据2.返回下一条数据是否存在
*在一次循环中,不要多次调用next()方法,会跳过部分数据
*/
while(rs.next()) {
int id = rs.getInt(1);//根据列的编号来获取,从1开始
String un = rs.getString(2);
String pw = rs.getString("password");//根据列名来获取,实际中都采用这种方式
int age = rs.getInt("age");
User user = new User(id, un, pw, age);
list.add(user);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} finally {
if(rs != null) {
try {
rs.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();
}
}
}
return list;
}
}
/*
* 实体类
*/
class User {
private Integer id;
private String username;
private String password;
private Integer age;
public User() {
super();
}
public User(Integer id, String username, String password, Integer age) {
super();
this.id = id;
this.username = username;
this.password = password;
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer 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 Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "user [id=" + id + ", username=" + username + ", password=" + password + ", age=" + age + "]\n";
}
}
一般返回结果都放在一个数据实体类型的集合中。
6. 封装JdbcUtil工具类
通常习惯定义一个工具类将重复冗长的代码(比如数据库连接和关闭资源)封装
import java.sql.*;
/*
* JDBC工具类,将重复的步骤封装
*/
public class JdbcUtil {
/**
* 获取数据库连接
*/
public static Connection getConnection() {
String driverClassName = "com.mysql.cj.jdbc.Driver";
String url = "jdbc:mysql://localhost:3306/my_database?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT";
String username = "root";
String password = "aa54545677877";
Connection conn = null;
try {
Class.forName(driverClassName);
conn = DriverManager.getConnection(url,username,password);
}catch (SQLException e) {
e.printStackTrace();
}catch (ClassNotFoundException e) {
e.printStackTrace();
}
return conn;
}
/**
* 关闭资源
*/
public static void close(Connection conn,Statement stmt,ResultSet rs) {
if(rs != null) {
try {
rs.close();
}catch(SQLException e) {
e.printStackTrace();
}
}
if(stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void close(Connection conn,Statement stmt) {
close(conn, stmt, null);
}
}
7. PreparedStatement
PreparedStatement继承Statement,实际开发中推荐使用PreparedStatement
优点:
- 可以使用
?
占位符,简单易读(称为动态SQL) - 预编译SQL语句,效率高
- 安全,避免出现SQL注入
import java.sql.*;
public class Test03 {
public static void main(String[] args) {
/*
* SQL注入:
* 当使用Statement和对应的sql语句时,填有些特殊错误密码可以符合条件chaxun
* 例如boolean flag = login("tom" ,"123' or '1=1");
* 此时拼接的sql语句:
* String sql = "select id,username,password,age from t_user where
* username='tom' and password=123' or '1=1'";
* 此时where后面部分永远都是true,不管用户名和密码怎么写都能通过
*/
boolean flag = login("tom" ,"666");
System.out.println(flag);
User user = new User(null, "ccc", "111", 25);
register(user);
System.out.println("注册成功");
}
/*
* 用户登录
*/
public static boolean login(String username,String password)
{
/*
* 正常sql语句写法:
* String sql = "select id,username,password,age from t_user where
* username='" + username + "' and password='" + password + "'";
* 这种拼接容易出错且可读性差
* 使用?占位符后写法:
* String sql = "select id,username,password,age from t_user where
* username=? and password=?";
* 实际写的时候建议使用StringBuffer或StringBuilder拼接SQL语句,关键字、表
* 名、列名、条件等独占一行
*/
String sql = new StringBuilder()
.append(" select ")
.append(" id,constant,date ")
.append(" from ")
.append(" tb_test ")
.append(" where ")
.append(" id = ? and constant = ? ")
.toString();
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = JdbcUtil.getConnection();
ps = conn.prepareStatement(sql); //获取PreparedStatement,需要传入sql,进行预编译
ps.setString(1, username); //为占位符?赋值,编号从1开始,set后的类型只代表第二个参数类型,不代表数据库对应的类型,所以用setObject()也可以
ps.setString(2, password);
rs = ps.executeQuery();
if(rs.next()) { //如果返回结果集中最多只有一条记录,可以使用if
return true;
}
}catch(SQLException e){
e.printStackTrace();
}finally {
JdbcUtil.close(conn, ps, rs);
}
return false;
}
/**
* 用户注册
*/
public static void register(User user) {
String sql = new StringBuilder()
.append(" insert into ")
.append(" t_usert ")
.append(" (username,password,age) ")
.append(" values ")
.append(" (?,?,?) ")
.toString();
Connection conn = null;
PreparedStatement ps = null;
try {
conn = JdbcUtil.getConnection();
ps = conn.prepareStatement(sql);
ps.setString(1, user.getUsername());
ps.setString(2, user.getPassword());
ps.setInt(3, user.getAge());
ps.executeUpdate();
}catch(SQLException e){
e.printStackTrace();
}finally {
JdbcUtil.close(conn, ps);
}
}
}
/*
* 实体类
*/
class User {
private Integer id;
private String username;
private String password;
private Integer age;
public User() {
super();
}
public User(Integer id, String username, String password, Integer age) {
super();
this.id = id;
this.username = username;
this.password = password;
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer 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 Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String toString() {
return "user [id=" + id + ", username=" + username + ", password=" + password + ", age=" + age + "]\n";
}
}
8. 实战练习:学生管理
主要功能:
- 查询所有学生
- 根据学号查询学生
- 根据姓名和年龄范围查询学生
- 添加学生
- 修改学生
- 根据学号删除学生
源码和SQL脚本文件也会上传,感兴趣的朋友可以下载。
下载链接:https://download.csdn.net/download/zdfsb/12656254
本人将自己的学习过程详细记录,分为:
- JDBC访问数据库操作详解(一)之JDBC基本用法:以MySQL为例
- JDBC访问数据库操作详解(二)之JDBC更多用法:以MySQL为例
- JDBC访问数据库操作详解(三)之数据库连接池:以MySQL为例
- JDBC访问数据库操作详解(四)之三层架构:以MySQL为例
对其他章节感兴趣的朋友可以关注我的博客浏览!感谢!