1. 概述
JDBC(Java Data Base Connectivity):简单说,就是使用 Java 语言操作数据库。
JDBC是一套标准,它是由一些接口与类组成的。
学习中涉及到的类与接口,主要在两个包下:
-
java.sql
-
类
-
DriverManger
-
-
接口
-
Connection
-
Statement
-
ResultSet
-
PreparedStatement
-
-
-
javax.sql
-
接口
-
DataSource
-
-
2. JDBC入门
-
创建初始表及数据
create table user( id int primary key auto_increment, username varchar(20) unique not null, password varchar(20) not null, email varchar(40) not null ); INSERT INTO user VALUES(NULL,'tom','123','tom@163.com'); INSERT INTO user VALUES(NULL,'fox','123','fox@163.com');
-
创建一个普通类,在类中创建JDBC连接并进行数据库操作
import com.mysql.cj.jdbc.Driver; import java.sql.*; public class Test02 { public static void main(String[] args) throws SQLException { DriverManager.registerDriver(new Driver()); Connection connection = DriverManager.getConnection("jdbc:mysql://ip-address:3306/codingfuture", "root", "root"); Statement statement = connection.createStatement(); String selectSql = "select * from user"; ResultSet resultSet = statement.executeQuery(selectSql); while (resultSet.next()) { String username = resultSet.getString("username"); String password = resultSet.getString("password"); String email = resultSet.getString("email"); System.out.println(username + "\t" + password + "\t" + email); } resultSet.close(); statement.close(); connection.close(); } }
3. JDBC操作详解
-
DriverManager
-
注册驱动
DriverManager.registerDriver(new Driver());
DriverManager是java.sql包下的一个驱动管理的工具类,可以理解成是一个容器(Vector),可以装入很多数据库驱动。
上述代码存在一点问题,会在驱动管理器中会装入两个mysql驱动,因为在
com.mysql.jdbc.Driver
类中有一段静态代码块:static { try { DriverManager.registerDriver(new Driver()); } catch (SQLException var1) { throw new RuntimeException("Can't register driver!"); } }
解决方案是使用反射来加载该类,在Class.forName加载完驱动类,开始执行静态初始化代码时,会自动新建一个Driver的对象,并调用
DriverManager.registerDriver(new Driver())
把自己注册到DriverManager中去。使用反射的方式来加载驱动,使得
com.mysql.jdbc.Driver
只加载一次,装入一个驱动对象,并且可以降低耦合,不依赖于驱动。Class.forName("com.mysql.cj.jdbc.Driver");
-
在java 6中,引入了service provider的概念,即可以在配置文件中配置service(可能是一个interface或者abstract class)的provider(即service的实现类)。配置路径是:/META-INF/services/下面。
而java.sql.DriverManager也添加了对此的支持,因此,在JDK6中,DriverManager的查找Driver的范围为:
因此,在jdk6中,其实是可以不用调用Class.forName来加载mysql驱动的,因为mysql的驱动程序jar包中已经包含了java.sql.Driver配置文件,并在文件中添加了com.mysql.cj.jdbc.Driver。但在JDK6之前版本,还是要调用这个方法。
-
system property "jdbc.drivers" 中配置的Driver值;
-
用户调用Class.forName()注册的Driver
-
service provider配置文件java.sql.Driver中配置的Driver值。
-
-
通过DriverManager来获取连接对象
Connection connection = DriverManager.getConnection(String url, String username, String password);
-
url的作用是用于确定使用哪一个驱动
url格式
localhost:3306为默认值,因此该url可省略为jdbc:mysql:///mysql1
url后面可以带参数,比如:
jdbc:mysql:///mydb1?useUnicode=true&characterEncoding=UTF-8
-
mysql:jdbc:mysql://localhost:3306/数据库名
-
oracle:jdbc:oracle:thin:@localhost:1521:sid
-
主协议:子协议://主机:端口/数据库
-
jdbc:mysql://localhost:3306/mydb1
-
-
Connection
java.sql.Connection
代表的是程序与数据库之间的连接对象。Connection作用:
-
可以通过Connection获取操作sql的Statement对象
Statement statement = connection.createStatement();
-
可以获取执行预处理的PreparedStatement对象。
PreparedStatement ps = connection.prepareStatement(sql);
-
操作事务
connection.setAutoCommit(boolean flag); // 开启事务 connection.rollback(); // 事务回滚 connection.commit(); // 事务提交
-
-
Statement
java.sql.Statement
用于执行sql语句。
Statement作用:
-
执行sql
-
int executeUpdate(String sql)
执行DML(增删改),返回值代表更新条数,利用返回值判断非0来确定sql语句是否执行成功。
-
ResultSet executeQuery(String sql)
执行DQL(查询),返回结果集。
-
execute(String sql)
用于向数据库发送任意sql语句
-
-
批处理操作
addBatch(String sql); // 将sql语句添加到批处理 executeBatch(); // 批量执行 clearBatch(); // 清空批处理
PreparedStatement
sql注入:由于没有对用户输入进行充分检查,而SQL又是拼接而成,在用户输入参数时,在参数中添加一些SQL关键字,达到改变SQL运行结果的目的,完成恶意攻击。
-
示例:
在输入用户名时 tom' or '1'='1
这时就不会验证密码了。
-
解决方案:
PreparedStatement
它是一个预处理的Statement,它是
java.sql.Statement
接口的一个子接口。 -
PreparedStatement使用:
-
在sql语句中,使用"?"占位
-
String sql="select * from user where username=? and password=?";
2.得到PreparedStatement对象
PreparedStatement ps = con.prepareStatement(String sql);
3.对占位符赋值
ps.setXxx(int index, Xxx obj);
4. 执行sql
DML:ps.executeUpdate()
DQL:ps.executeQuery()
- ResultSet
-
java.sql.ResultSet
它是用于封装select语句执行后查询的结果。常用API:
-
getXxx()
常用:
-
getInt()
-
getString()
-
getDate()
-
getDouble()
参数有两种
-
getInt(int columnIndex)
-
getInt(String columnName)
如果列的类型不知道,可以通过下面的方法来操作
-
getObject(int columnIndex)
-
getObject(String columnName)
-
-
boolean next()
用于判断是否有下一条记录。
如果有返回true,并且让游标向下移动一行。
如果没有返回false。
-
关闭资源
Jdbc程序运行完后,切记要释放程序在运行过程中,创建的那些与数据库进行交互的对象,这些对象通常是ResultSet、Statement和Connection对象。
特别是Connection对象,它是非常稀有的资源,用完后必须马上释放,如果Connection不能及时、正确的关闭,极易导致系统宕机。
Connection的使用原则是尽量晚创建,尽量早的释放。
为确保资源释放代码能运行,资源释放代码也一定要放在finally语句中。
-
4. JDBC中的事务
在jdbc中处理事务,都是通过Connection完成的。
同一事务中所有的操作,需要同一个Connection对象。
Connection的三个方法与事务相关:
setAutoCommit(boolean)
默认值就是true,表示自动提交,也就是每条执行的SQL语句都是一个单独的事务。
如果设置false,即关闭自动提交,改为手动提交事务,也就相当于开启了事务。
con.setAutoCommit(false);
commit()
提交事务
con.commit();
rollback()
回滚事务
con.rollback();
jdbc处理事务的代码格式:
try {
con.setAutoCommit(false); // 开启事务
// …
con.commit(); // try的最后提交事务
} catch() {
con.rollback(); // 回滚事务
}
5. 抽取 JDBC 工具类
-
JDBCUtils.java
package com.codingfuture;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;
public class JDBCUtils {
private static final String URL;
private static final String USERNAME;
private static final String PASSWORD;
static {
InputStream resource = ClassLoader.getSystemResourceAsStream("jdbc.properties");
Properties properties = new Properties();
try {
properties.load(resource);
} catch (IOException e) {
e.printStackTrace();
}
URL = properties.getProperty("url");
USERNAME = properties.getProperty("username");
PASSWORD = properties.getProperty("password");
}
public static Connection getConnection() {
Connection connection = null;
try {
connection = DriverManager.getConnection(URL, USERNAME, PASSWORD);
} catch (SQLException e) {
System.err.println("数据库连接参数错误,Connection获取失败!");
System.err.println(e.getMessage());
}
return connection;
}
}
- jdbc.properties
url=jdbc:mysql:///mydb06 username=root password=root