本篇内容包括:JDBC 概述、JDBC 的执行流程(包括注册驱动、获取连接对象、创建 SQL 执行对象、执行SQL语句、遍历结果集、关闭资源(处理异常))以及 JDBC 的 Demo。
JDBC(Java Database Connectivity)即 「Java 数据库连接」,是 Java 语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸如查询和更新数据库中数据的方法。JDBC 提供一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能编写数据库应用程序。JDBC 也是 Sun Microsystems 的商标。我们通常说的 JDBC 是面向关系型数据库的。
JDBC 也是 Java 访问数据库的标准规范,真正怎么操作数据库还需要具体的实现类,也就是数据库驱动。每个数据库厂商根据自家数据库的通信格式编写好自己数据库的驱动。JDBC 即可以为多种关系数据库提供统一访问,而我们只需要会调用 JDBC 接口中的方法即可。
使用 JDBC 的好处:
执行JDBC的流程可分为:注册驱动(加载数据库驱动)、获取连接对象(Connection)、创建 SQL 执行对象(Statement)、执行 SQL 语句和关闭资源五个步骤。
在连接数据库之前,首先要加载想要连接的数据库的驱动到 Jvm(Java虚拟机),这通过 java.lang.Class
类的静态方法 forName(String className)
实现:
try {
//加载MySql的驱动类
Class.forName("com.mysql.jdbc.Driver");
} catch(ClassNotFoundException e) {
System.out.println("找不到驱动程序类 ,加载驱动失败!");
e.printStackTrace();
}
成功加载后,会将 Driver 类的实例注册到 DriverManager 类中。
要连接数据库,需要向 java.sql.DriverManager
请求并获得 Connection 对象,该对象就代表一个数据库的连接,使用 DriverManager 的 getConnectin(String url, String username, String password)
方法传入指定的欲连接的数据库的路径、数据库的用户名和密码来获得:
//连接MySql数据库,用户名和密码都是root
String url = "jdbc:mysql://localhost:3306/test" ;
String username = "root" ;
String password = "root" ;
try{
Connection con = DriverManager.getConnection(url , username , password) ;
}catch(SQLException se){
System.out.println("数据库连接失败!");
se.printStackTrace() ;
}
要执行 SQL 语句,必须获得 java.sql.Statement
实例,Statement 实例分为以下 3 种类型:
执行静态 SQL 语句。通常通过 Statement 实例实现,每次执行 sql 语句,数据库都要执行 sql 语句的编译 ,最好用于仅执行一次查询并返回结果的情形,效率高于PreparedStatement;
执行动态 SQL 语句。通常通过 PreparedStatement 实例实现,PreparedStatement 继承自 Statement,PreparedStatement 是预编译的,在执行可变参数的一条 sql 时,PreparedStatement 比 Statement 的效率高,因为 DBMS 预编译一条 sql 当然会比多次编译一条 sql 的效率要高。
执行数据库存储过程。通常通过 CallableStatement 实例实现,CallableStatement 继承自 PreparedSatement,CallableStatement 提供了对输出和输入/输出参数的支持。Ps:
存储过程(Stored Procedure),是一组为了完成特定功能的 sql 语句集,用户通过指定存储过程的名字并给出参数(如果该存储过程有参数的话)来执行它。
总结:Statement 接口提供了执行语句和获取结果的基本方法;PreparedStatement 接口添加了处理 IN 参数的方法;而 CallableStatement 添加了处理 OUT 参数的方法。
具体的实现方式:
Statement stmt = con.createStatement() ;
String sql="select count(*) from login where uname=? and upwd=?";
PreparedStatement pstmt = con.prepareStatement(sql) ;
pstmt.setString(1, "userName");
pstmt.setString(2, "password");
// 默认存储过程demoSp的内容为: 'select count(*) from login where uname=? and upwd=?'
CallableStatement cstmt = con.prepareCall("{CALL demoSp(?,?)}") ;
cstmt.setString(1, "userName");
cstmt.setString(2, "password");
cstmt.executeUpdate();
Statement 接口提供了三种执行 SQL 语句的方法:executeQuery 、executeUpdate 和 execute:
ResultSet executeQuery(String sqlString)
:执行查询数据库的 SQL 语句(DQL),用于产生单个结果集(ResultSet)的对象,例如 SELECT 语句;int executeUpdate(String sqlString)
:用于执行 DML(INSERT、UPDATE 或 DELETE等)语句以及 DDL(CREATE、DROP等);boolean execute(String sqlString)
:用于执行返回多个结果集、多个更新计数或二者组合的语句。多数程序员不会需要该高级功能。具体实现的代码:
ResultSet rs = stmt.executeQuery("SELECT * FROM ...") ;
int rows = stmt.executeUpdate("INSERT INTO ...") ;
boolean flag = stmt.execute(sqlString ) ;
两种情况:执行更新返回的是本次操作影响到的记录数;执行查询返回的结果是一个ResultSet对象。ResultSet 包含符合 sql 语句中条件的所有行,并且它通过一套 get 方法提供了对这些行中数据的访问。
使用结果集(ResultSet)对象的访问方法获取数据:
while(rs.next()){
String name = rs.getString("name") ;
// 此方法比较高效(列是从左到右编号的,并且从列1开始)
String pass = rs.getString(1) ;
}
操作完成以后要把所有使用的 Jdbc 对象全都关闭,以释放 Jdbc 资源,关闭顺序和声明顺序相反:
// 关闭记录集
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();
}
}
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.27version>
dependency>
# 配置数据库的连接信息
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/databaseName
username=userName
password=password
public class DbUtils {
private static String driver;
private static String url;
private static String username;
private static String password;
static {
//创建一个读取配置文件的属性对象
Properties prop = new Properties();
//获取文件的输入流
InputStream ips = DbUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
try {
//把文件加载到属性对象中
prop.load(ips);
//获取数据
driver = prop.getProperty("driver");
url = prop.getProperty("url");
username = prop.getProperty("username");
password = prop.getProperty("password");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
//关闭流
ips.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 注册驱动,获取连接
*
* @return Connection
* @throws Exception e
*/
public static Connection getConn() throws Exception {
//注册驱动
Class.forName(driver);
//获取连接
Connection conn = DriverManager.getConnection(url, username, password);
//返回连接对象Connection
return conn;
}
/**
* 关闭资源
*
* @param conn Connection
* @param stat Connection
* @param rs Connection
*/
public static void close(Connection conn, Statement stat, ResultSet rs) {
try {
//判断有值时关闭
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (stat != null) {
stat.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
BUtils 类封装好了,我们后续直接调用就可以了。
public void insert() {
//声明连接对象Connection和执行Sql的Statement对象
Connection conn = null;
Statement stat = null;
try {
//调用上面我们封装的获取Connection对象的方法
conn = DbUtils.getConn();
//通过Connection获取Statement对象
stat = conn.createStatement();
//创建一条SQL语句
String sql = "insert into table_user(user_id, user_name, user_telephone) values(1,'Tom','1132210')";
//执行SQL语句(这里也可以接受一下int返回值,查看受影响条数
stat.executeUpdate(sql);
} catch (Exception e) {
e.printStackTrace();
} finally {
//最后调用我们封装好的close方法关闭资源
DbUtils.close(conn, stat, null);
}
}
public void update() {
Connection conn = null;
Statement stat = null;
try {
conn = DbUtils.getConn();
stat = conn.createStatement();
String sql = "update table_user set user_name='Jerry' where user_id=1";
stat.executeUpdate(sql);
} catch (Exception e) {
e.printStackTrace();
} finally {
DbUtils.close(conn, stat, null);
}
}
public void delete() {
Connection conn = null;
Statement stat = null;
try {
conn = DbUtils.getConn();
stat = conn.createStatement();
String sql = "delete from table_user where user_id=1";
stat.executeUpdate(sql);
} catch (Exception e) {
e.printStackTrace();
} finally {
DbUtils.close(conn, stat, null);
}
}
public void select() {
Connection conn = null;
Statement stat = null;
//这里多声明一个结果集对象ResultSet
ResultSet rs = null;
try {
conn = DbUtils.getConn();
stat = conn.createStatement();
//执行查询并获取结果集
rs = stat.executeQuery("select * from table_user");
while (rs.next()) {
//使用next()方法逐条遍历所有结果
int userId = rs.getInt("user_id");
String userName = rs.getString("user_name");
String userTelephone = rs.getString("user_telephone");
System.out.println(userId + " " + userName + " " + userTelephone);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭资源
DbUtils.close(conn, stat, rs);
}
}
public static void main(String[] args) {
//create table person (id int,name varchar(10),money int);
//insert into person values(1,'小明',500),(2,'小红',5000);
String sql1 = "update person set money=money+2000 where id=1";
String sql2 = "update person set money=money-2000 where id=2";
Connection conn = null;
Statement stat = null;
ResultSet rs = null;
try {
conn = DbUtils.getConn();
stat = conn.createStatement();
//关闭自动提交(开启事务)
conn.setAutoCommit(false);
// 让小明+2000
stat.executeUpdate(sql1);
// 让小红-2000
stat.executeUpdate(sql2);
//查询小红剩余的钱是否>=0
rs = stat.executeQuery("select money from person where id=2");
while (rs.next()) {
int money = rs.getInt("money");
if (money >= 0) {
//事务提交
conn.commit();
System.out.println("转账成功");
} else {
//事务回滚
conn.rollback();
System.out.println("转账失败,小红余额不足");
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
DbUtils.close(conn, stat, rs);
}
}