文章目录
1 综述
本文档主要对JDBC
进行详细说明,包括JDBC的相关概念、架构说明、工作原理、常用接口以及具体的使用方法等。
2 JDBC相关概念
2.1 什么是JDBC
JDBC英文名为:Java Data Base Connectivity(Java数据库连接)
官方解释它是Java编程语言和广泛的数据库之间独立于数据库的连接标准的Java API,根本上说JDBC是一种规范,它提供的接口,一套完整的,允许便捷式访问底层数据库。可以用JAVA来写不同类型的可执行文件:JAVA应用程序、JAVA Applets、Java Servlet、JSP等,不同的可执行文件都能通过JDBC访问数据库,又兼备存储的优势。简单说它就是JAVA与数据库的连接的桥梁或者插件,用JAVA代码就能操作数据库的增删改查、存储过程、事务等。
2.2 JDBC有什么用
我们用JAVA就能连接到数据库;创建SQL或者MYSQL语句;执行SQL或MYSQL的查询数据库;查看和修改结果记录。
2.3 数据库驱动
我们安装好数据库之后,我们的应用程序也是不能直接使用数据库的,必须要通过相应的数据库驱动程序,通过驱动程序去和数据库打交道。其实也就是数据库厂商的JDBC接口实现,即对Connection等接口的实现类的jar文件。
下载地址:https://mvnrepository.com/artifact/mysql/mysql-connector-java
2.4 JDBC架构
分为双层架构和三层架构。
2.4.1 双层结构
- 作用:此架构中,Java Applet 或应用直接访问数据源。
- 条件:要求 Driver 能与访问的数据库交互。
- 机制:用户命令传给数据库或其他数据源,随之结果被返回。
- 部署:数据源可以在另一台机器上,用户通过网络连接,称为 C/S配置(可以是内联网或互联网)。
2.4.2 三层架构
3 JDBC常用接口
3.1 Driver接口
Driver接口由数据库厂家提供,作为java开发人员,只需要使用Driver接口就可以了。在编程中要连接数据库,必须先装载特定厂商的数据库驱动程序,不同的数据库有不同的装载方法。如:
- 装载MySql驱动:
Class.forName("com.mysql.jdbc.Driver");
- 装载Oracle驱动:
Class.forName("oracle.jdbc.driver.OracleDriver");
3.2 Connection接口
Connection与特定数据库的连接,在连接上下文中执行sql语句并返回结果。DriverManager.getConnection(url, user, password)方法建立在JDBC URL中定义的数据库Connection连接上。
- 连接MySql数据库:
Connection conn = DriverManager.getConnection("jdbc:mysql://host:port/database", "user", "password");
- 连接Oracle数据库:
Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@host:port:database", "user", "password");
- 连接SqlServer数据库:
Connection conn = DriverManager.getConnection("jdbc:microsoft:sqlserver://host:port; DatabaseName=database", "user", "password");
常用方法:
- createStatement():创建向数据库发送sql的statement对象。
- prepareStatement(sql) :创建向数据库发送预编译sql的PrepareSatement对象。
- prepareCall(sql):创建执行存储过程的callableStatement对象。
- setAutoCommit(boolean autoCommit):设置事务是否自动提交。
- commit() :在链接上提交事务。
- rollback() :在此链接上回滚事务。
3.3 Statement接口
用于执行静态SQL语句并返回它所生成结果的对象。
三种Statement类:
- Statement:由createStatement创建,用于发送简单的SQL语句(不带参数)。
- PreparedStatement :继承自Statement接口,由preparedStatement创建,用于发送含有一个或多个参数的SQL语句。PreparedStatement对象比Statement对象的效率更高,并且可以防止SQL注入,所以我们一般都使用PreparedStatement。
- CallableStatement:继承自PreparedStatement接口,由方法prepareCall创建,用于调用存储过程。
常用Statement方法:
- execute(String sql):运行语句,返回是否有结果集
- executeQuery(String sql):运行select语句,返回ResultSet结果集。
- executeUpdate(String sql):运行insert/update/delete操作,返回更新的行数。
- addBatch(String sql) :把多条sql语句放到一个批处理中。
- executeBatch():向数据库发送一批sql语句执行。
3.4 ResultSet接口
ResultSet提供检索不同类型字段的方法,常用的有:
- getString(int index)、getString(String columnName):获得在数据库里是varchar、char等类型的数据对象。
- getFloat(int index)、getFloat(String columnName):获得在数据库里是Float类型的数据对象。
- getDate(int index)、getDate(String columnName):获得在数据库里是Date类型的数据。
- getBoolean(int index)、getBoolean(String columnName):获得在数据库里是Boolean类型的数据。
- getObject(int index)、getObject(String columnName):获取在数据库里任意类型的数据。
- 等还有其他的获取类型方法。
ResultSet还提供了对结果集进行滚动的方法:
- next():移动到下一行
- Previous():移动到前一行
- absolute(int row):移动到指定行
- beforeFirst():移动resultSet的最前面。
- afterLast():移动到resultSet的最后面。
注意:使用后依次关闭对象及连接:ResultSet → Statement → Connection。
4 JDBC使用方法
4.1 下载jar包
上面以及展示下载地址,下载合适的版本即可。
4.2 导入jar包
- 在项目下建立lib文件夹
- 将jar包复制到文件夹中
- 将文件夹添加为Library
4.3 JDBC使用步骤
使用JDBC的步骤:
- 加载JDBC驱动程序
- 构造数据库链接信息
- 建立数据库连接Connection
- 创建执行SQL的语句Statement对象
- 使用Statement对象执行SQL
- 处理执行结果ResultSet
- 释放资源
4.4 使用代码示例
package com.wzh.demo01;
import java.sql.*;
public class JdbcDemo01 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
// 1 加载JDBC驱动程序
Class.forName("com.mysql.jdbc.Driver");
// 2 构造数据库链接信息(以MySql为例)
String url = "jdbc:mysql://localhost:3306/test_go?useUnicode=true&characterEncoding=utf8&useSSL=true";
String username = "root";
String pwd = "root";
// 3 建立数据库连接Connection
Connection connection = DriverManager.getConnection(url, username, pwd);
// 4 创建执行SQL的语句Statement对象
Statement statement = connection.createStatement();
// 5 使用Statement对象执行SQL
String sql = "select * from user";
ResultSet resultSet = statement.executeQuery(sql);
// 6 处理执行结果ResultSet
while (resultSet.next()) {
System.out.println("id=" + resultSet.getObject("id"));
System.out.println("name=" + resultSet.getObject("name"));
System.out.println("birthday=" + resultSet.getObject("birthday"));
}
// 7 释放资源 (注意关闭顺序)
resultSet.close();
statement.close();
connection.close();
}
}
4.4.1 插入更新操作
String sql = "insert into user(name,birthday) value ('hao','2021-06-28')";
int i = statement.executeUpdate(sql);
if(i>0){
System.out.println("插入数据成功");
}
String sql1 = "update user set name='nn' where id = 2";
int i1 = statement.executeUpdate(sql1);
if(i1 > 0){
System.out.println("更新数据成功");
}
5 封装提取工具类
- 在src目录下新建db.properites用于保存数据库配置信息;
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test_go?useUnicode=true&characterEncoding=utf8&useSSL=true
username=root
password=root
- 新建utils包,再新建JdbcUtils.java文件,写入如下代码
package com.wzh.demo02.utils;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class JdbcUtils {
private static String driver = null;
private static String url = null;
private static String username = null;
private static String password = null;
static {
try {
InputStream is = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
Properties properties = new Properties();
properties.load(is);
driver = properties.getProperty("driver");
url = properties.getProperty("url");
username = properties.getProperty("username");
password = properties.getProperty("password");
// 1 驱动只用加载一次
Class.forName(driver);
} catch (Exception e) {
e.printStackTrace();
}
}
// 获取链接
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, username, password);
}
// 释放资源
public static void release(Connection conn, Statement st, ResultSet rs) throws SQLException {
if (rs != null) {
rs.close();
}
if (st != null) {
st.close();
}
if (conn != null) {
conn.close();
}
}
}
- 后可在主函数使用
package com.wzh.demo02;
import com.wzh.demo02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class Test {
public static void main(String[] args) {
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
st = conn.createStatement();
// 新增
String sql_i = "insert into user(name,birthday) value ('张三','2021-06-28')";
int i = st.executeUpdate(sql_i);
if (i > 0) {
System.out.println("插入成功");
}
// 修改
String sql_u = "update user set name='李四' where id = 3";
int i1 = st.executeUpdate(sql_u);
if (i1 > 0) {
System.out.println("更新数据成功");
}
// 删除
String sql_d = "delete from user where id = 3";
int i2 = st.executeUpdate(sql_d);
if (i2 > 0) {
System.out.println("删除成功");
}
// 查找
String sql_q = "select * from user";
rs = st.executeQuery(sql_q);
while (rs.next()){
System.out.println(rs.getString("name"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
JdbcUtils.release(conn, st, rs);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
6 SQL注入
详细可观看此篇博文:https://www.cnblogs.com/myseries/p/10821372.html
6.1 sql注入演示
package com.wzh.demo02;
import com.wzh.demo02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class Sql注入 {
public static void main(String[] args) {
//正常使用
//login("hao", "123456");
// 此时为sql注入
login(" 'or '1=1","123456");
}
public static void login(String username, String password) {
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
st = conn.createStatement();
String sql_login = "select * from user where name='" + username + "' and password='" + password + "'";
rs = st.executeQuery(sql_login);
if (rs.next()) {
System.out.println("登录成功");
}else{
System.out.println("登录失败");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
JdbcUtils.release(conn, st, rs);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
由上可知Statement是不安全的,PreparedStatement可以防止SQL注入。效率更高。
6.2 PreparedStatement使用示例
package com.wzh.demo02;
import com.wzh.demo02.utils.JdbcUtils;
import java.sql.*;
public class JdbcDemo03 {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
// 区别:先写sql
// 使用 ? 占位符代替参数
String sql_i = "insert into user(name,birthday) value (?,?)";
st = conn.prepareStatement(sql_i); // 预编译sql,
// 手动给参数赋值
st.setString(1, "lisi");
st.setDate(2, new java.sql.Date(new java.util.Date().getTime()));
int i = st.executeUpdate();
if (i > 0) {
System.out.println("插入成功");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
JdbcUtils.release(conn, st, rs);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
此种方式可以避免sql注入。
7 JDBC操纵事务
事务的四大特性:
- 原子性(Atomictiy):不可再分割
- 一致性(Consistency):数据前后一致,总量保持不变
- 隔离性(Isolation):多个事务互不干扰
- 持久性(Durability):一旦提交不可逆,持久化到数据库
事务的隔离级别:
- 读未提交(Read Uncommitted):最低级别,存在脏读、幻读、不可重复读问题;
- 读已提交(Read Committed):可避免脏读情况发生;
- 可重复读(Repeatable Read):可避免脏读、不可重复读情况的发生;
- 序列化(Serializable):可避免脏读、不可重复读、幻读情况的发生。(串行化)
隔离性问题:
- 脏读:一个事务读取了另一个没有提交的事务数据
- 不可重复读:在同一个事务内,相同的语句读取出来的数据不一致
- 幻读:在一个事务内,读取到别的数据插入的数据,导致前后读出来的数据结果条数不一致。
package com.wzh.demo02;
import com.wzh.demo02.utils.JdbcUtils;
import java.sql.*;
public class JdbcTransaction {
public static void main(String[] args) {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
// 关闭自动提交 并且会自动开启事务
conn.setAutoCommit(false);
String sql1 = "update user set money = money - 100 where id = 1";
st = conn.prepareStatement(sql1);
st.executeUpdate();
String sql2 = "update user set money = money + 100 where id = 2";
st = conn.prepareStatement(sql2);
st.executeUpdate();
// 业务完毕 提交事务
conn.commit();
} catch (Exception e) {
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
} finally {
try {
JdbcUtils.release(conn, st, rs);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
修改事务隔离级别
conn = JdbcUtils.getConnection();
conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);//避免脏读
conn.setAutoCommit(false);
8 批处理
package com.wzh.demo02;
import com.wzh.demo02.utils.JdbcUtils;
import java.sql.*;
public class JdbcDemo04 {
public static void main(String[] args) {
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();
conn.setAutoCommit(false); //设为手动提交
long start = System.currentTimeMillis();
st = conn.createStatement();
for (int i = 0; i < 100; i++) {
st.addBatch("insert into user (name,birthday) values ('hao'" + i + ",'2021-06-28')");
}
st.executeBatch();
conn.commit(); //提交事务
long end = System.currentTimeMillis();
System.out.println("插入100条数据,耗时(ms):" + (end - start));
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
JdbcUtils.release(conn, st, rs);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
以上基本就是jdbc基本的使用方式,后续会再专门写一篇关于数据库连接池技术c3p0,dbcp与druid的使用。