JDBC
JDBC(Java数据库连接)是一种Java API(应用程序编程接口),允许Java程序与数据库交互。它提供了一个标准接口,用于从Java程序连接到关系数据库(如MySQL、Oracle和Microsoft SQL Server)并与之交互。JDBC允许您执行各种数据库操作,例如插入、更新和从数据库检索数据。它提供了一组类和接口,允许您连接到数据库、发送SQL语句和处理结果。JDBC是Java标准版(JavaSE)平台的一部分,广泛用于企业应用程序中的数据库连接。
前置知识:
- 需要软件: mysql mysql可视化工具 idea
- sql语句:掌握数据库连接命令;掌握基本的 DDL DML DQL等命令;掌握数据库事务的概念
- Java基础语法:基本容器的使用(集合和数组);泛型;多态机制;反射技术。
学习知识体系:
- 阶段一:jdbc版本和概念的理解
- 阶段二:jdbc的核心使用
- 阶段三:jdbc连接性能的优化 连接池的使用
- 阶段四:jdbc使用优化工具高阶的封装
- 阶段五:实践。
jdbc的技术和概念的理解
DBC(Java Database Connectivity)是Java语言中用于操作关系型数据库的一种API(Application Programming Interface)。它提供了一组Java接口,可以让Java程序与关系型数据库进行交互,包括连接数据库、执行SQL语句、处理结果等操作。
jdbc:Java database connetctivty Java连接数据库
jdbc是Java程序连接数据库技术的统称。
jdbc:是有Java语言的规范(接口)和各个数据库厂商的实现驱动组成。
jdbc是一种面向接口的编程
jdbc优势:
只要学习jdbc规范的方法,便可以操作所有的关系型数据库。
项目中要切换数据库软件,只需要更换对应的数据集驱动jar包,不需要更改代码。
JDBC的核心概念包括:
-
JDBC驱动:JDBC驱动是一种软件组件,用于将JDBC API与特定数据库的实现相连接。JDBC驱动通常由数据库供应商提供,可以分为四种类型:JDBC-ODBC桥、本地API驱动、网络协议驱动和本地协议驱动。
-
连接:通过JDBC API,Java程序可以建立与数据库的连接。连接是一个重要的资源,需要在使用完成后关闭,以释放相关资源。
-
Statement:Statement是执行SQL语句的接口,包括执行查询语句和更新语句等。Statement还可以设置参数和查询结果的处理方式。
-
ResultSet:ResultSet是查询结果的接口,包含了查询返回的数据集。可以使用ResultSet来遍历查询结果,获取每一行的数据。
-
数据库事务:JDBC支持数据库事务的处理,可以通过Connection接口的begin、commit和rollback方法来实现事务操作。
-
数据源:数据源是一种JDBC的高级概念,它提供了一种池化的方式来管理JDBC连接。通过使用数据源,可以提高JDBC连接的效率和可靠性。
总之,JDBC是Java程序与关系型数据库进行交互的重要手段,理解JDBC的技术和概念对于Java程序员来说是非常必要的。
JDBC核心优势
-
可移植性: JDBC API 是标准的 Java API,因此可以在任何支持 JDBC 的平台上使用。这使得开发人员可以轻松地将应用程序从一个平台迁移到另一个平台。
-
安全性: JDBC API 提供了一种安全的方式来访问数据库。开发人员可以使用 JDBC API 来确保应用程序不会遭受 SQL 注入攻击等安全漏洞。
-
灵活性: JDBC API 允许开发人员使用任何数据库管理系统(DBMS)。开发人员可以使用 JDBC API 与任何 DBMS 进行交互,而不需要学习不同的 API。
-
性能: JDBC API 是一种高效的方式来访问数据库。开发人员可以使用 JDBC API 来执行高效的数据库操作,从而提高应用程序的性能。
-
可扩展性: JDBC API 提供了一种可扩展的方式来访问数据库。开发人员可以使用 JDBC API 来访问任何类型的数据库,包括关系型数据库、非关系型数据库、大数据存储系统等。
jdbc核心api和使用路线
jdbc技术的组成
1.jdbc的规范接口是包含在jdk中,存储在java.sql和Javax.sql包中。
2 jdbc的实现类是由各个数据库厂商提供,提供的是jar包,将这正jar 包称之为驱动
JDBC技术的组成包括以下几个部分:
-
JDBC API:提供了访问数据库的标准接口,包括连接数据库、执行SQL语句、处理结果集等功能。
-
JDBC Driver Manager:负责加载并管理JDBC驱动程序,通过它可以获取数据库连接。
-
JDBC Driver:实现了JDBC API,用于与特定数据库进行通信。
-
JDBC Test Suite:用于测试JDBC驱动程序的兼容性和性能。
-
JDBC-ODBC桥接器:用于连接基于ODBC的数据库系统。
-
SQL接口:用于执行SQL语句和管理数据库对象,如表、视图等。
-
数据源对象:用于管理数据库连接池,提高应用程序的性能和可靠性。
总之,JDBC技术的核心是JDBC API,而JDBC Driver Manager和JDBC Driver则是实现JDBC API的关键组件。其他组件如JDBC Test Suite和数据源对象等则是为了增强JDBC的功能和性能。
涉及的核心类和接口
JDBC涉及的核心类和接口如下:
DriverManager:管理JDBC驱动程序的类。
Connection:表示与数据库建立的连接。
Statement:执行SQL语句的接口。
PreparedStatement:预编译的SQL语句的接口。
CallableStatement:调用存储过程的接口。
ResultSet:表示查询结果集的接口。
ResultSetMetaData:结果集的元数据接口。
DatabaseMetaData:数据库元数据接口。
SQLException:处理JDBC异常的类。
Driver:JDBC驱动程序接口。
Blob:二进制数据的接口。
Clob:字符数据的接口。
Savepoint:保存点的接口,表示事务中的一个特定点。
DataSource:数据源接口,提供连接池和事务管理等功能。
ConnectionPoolDataSource:连接池数据源接口,用于实现连接池。
XADataSource:分布式事务数据源接口,用于实现分布式事务。
DriverManager
- 将第三方数据库厂商实现的驱动jar包注册到程序中
- 可以根据数据库连接信息获取Connection
Connction
- 和数据库建立的连接,在连接对象上可以多次执行CRUD动作
- 可以获取statement和preparedstatement,callablestatement对象
statement|preparedstatement|callablestatement
- 发送具体的sql语句到数据库对象
- 不同的是发送方稍有不同。
ResultSet
- 抽象了数据的查询结果集
- 存储了dql查询数据的结果对象
- 需要我们对这个结果进行解析,获取具体的数据库中的数据
jdbc使用的路线
- 静态sql(没有动态值的语句):DriverManager---->Connction—>Statement–>ResultSet
- 预编译sql(有动态值的语句):
DriverManager---->Connction—>Preparedstatement–>ResultSet - 执行存储过程的sql:DriverManager---->Connction—>Callablestatement–>ResultSet
jdbc的使用
配置jar包,导入到项目的lib目录中
jdbc的使用步骤
- 注册驱动
- 获取连接
- 创建发送sql语句的对象
- 发送sql 执行sql 斌返回结果
- 结果集的解析
- 释放资源
基于statement查询
基于statement查询是指使用Java程序中的Statement对象来执行SQL查询语句。Statement对象是Java程序中的一个接口,它允许程序员向数据库发送简单的SQL语句并获取结果。基于statement查询的优点是可以执行任何SQL语句,包括动态SQL语句。但是,基于statement查询也有一些缺点,比如容易受到SQL注入攻击,同时也不够灵活。在实际应用中,建议使用PreparedStatement对象来代替Statement对象进行查询操作,��提高代码的安全性和效率。
import com.mysql.cj.jdbc.Driver;
import java.sql.*;
/**
* 1. 注册驱动
*
* 2. 获取连接
*
* 3. 创建发送sql语句的对象
*
* 4. 发送sql 执行sql 斌返回结果
*
* 5. 结果集的解析
*
* 6. 释放资源
*/
public class JDBCBaseDemo1 {
public static void main(String[] args) throws SQLException {
// 1 注册驱动 Driver -->com.mysql.cj.jdbc.Driver;
// DriverManager.registerDriver(new Driver());
DriverManager.registerDriver(new Driver());
// 2. 获取连接
Connection conn =
DriverManager.getConnection("jdbc:mysql://localhost:3306/studydb","root","amp126578");
// 3 创建发送sql语句的对象
Statement stmt = conn.createStatement();
// 4 发送sql语句
String sql = "select id,name,age from emp ";
ResultSet rs = stmt.executeQuery(sql);
// 5 解析得到结果集
while(rs.next()){
int id = rs.getInt("id");
String name = rs.getString("name");
int age = rs.getInt("age");
System.out.println(id +"--"+name+"--"+age+"---");
}
//6 释放资源 先开后关
rs.close();
stmt.close();
conn.close();
}
}
代码的重构:
public class JDBCBaseDemo1 {
Statement stmt = null;
Connection conn = null;
ResultSet rs = null;
@Before
public void start() throws SQLException {
// 1 注册驱动 Driver -->com.mysql.cj.jdbc.Driver;
DriverManager.registerDriver(new Driver());
// 2. 获取连接
Connection conn =
DriverManager.getConnection("jdbc:mysql://localhost:3306/studydb","root","root");
// 3 创建发送sql语句的对象
stmt = conn.createStatement();
}
@After
public void destroy(){
//6 释放资源 先开后关
try {
if(rs !=null){
rs.close();
}
if(stmt != null){
stmt.close();
}
if(conn !=null){
conn.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
@Test
public void queryTest() throws SQLException {
// 4 发送sql语句
String sql = "select id,name,age,job,salary from emp where id= 1";
ResultSet rs = stmt.executeQuery(sql);
// 5 解析得到结果集
while(rs.next()){
int id = rs.getInt("id");
String name = rs.getString("name");
int age = rs.getInt("age");
System.out.println(id +"--"+name+"--"+age+"---");
}
}
@Test
public void insertTest() throws SQLException {
// 4 发送sql语句
String sql = "insert into emp(name,age,job,salary) values ('张三',22,)";
int rows = stmt.executeUpdate(sql);
// 5 解析得到结果集
System.out.println("受影响的行数:"+ rows);
}
@Test
public void updateTest() throws SQLException {
// 4 发送sql语句
String sql = "update emp set salary = 25000 where id = 23";
int rows = stmt.executeUpdate(sql);
// 5 解析得到结果集
System.out.println("受影响的行数:"+ rows);
}
@Test
public void deleteTest() throws SQLException {
// 4 发送sql语句
String sql = "delete from emp where id=23";
int rows = stmt.executeUpdate(sql);
// 5 解析得到结果集
System.out.println("受影响的行数:"+ rows);
}
}
*// 1* *注册驱动* *Driver -->com.mysql.cj.jdbc.Driver;
**// DriverManager.registerDriver(new Driver());
注册驱动的操作在8.0以后可以省略,当我们获取连接的时候,DriverManager会根据连接的url地址来自动
加载驱动。
url解析:
jdbc:mysql://localhost:3306/studydb
jdbc: 协议
mysql:子协议
localhost: 数据库服务器的地址
3306:mysql服务的端口
studydb: 要连接的数据库名称
加载和注册驱动的另外的方式:
Class.forName("com.mysql.cj.jdbc.Driver")
public static void main(String[] args) throws SQLException {
// 1 让用户输入账户和密码
Scanner sc = new Scanner(System.in);
String username = sc.nextLine();
String password = sc.nextLine();
sc.close();
//2 通过账户和密码来查询用户是否存在
Connection conn =
DriverManager.getConnection("jdbc:mysql:///studydb","root","root");
String sql = "select * from users where username= '"+ username+ "' and
password='"+password+"'";
Statement stmt = conn.createStatement();
System.out.println(sql);
ResultSet rs = stmt.executeQuery(sql);
//3 判断查询的结果 如果查询结构不为null 就成功 否则就失败
if(rs.next()){
System.out.println("登录成功");
}else{
System.out.println("登录失败");
}
//4 释放资源
rs.close();
stmt.close();
conn.close();
}
}
通过以上代码,那么看到存在这一种风险:sql注入。系统存在安全隐患
preparedStatement
JDBC中的preparedStatement是一种预编译的SQL语句,用于执行SQL查询和更新。与普通的Statement不同,preparedStatement在执行时会将SQL语句预编译并缓存起来,这样可以避免重复编译SQL语句,提高了执行效率。此外,preparedStatement还支持参数绑定,可以在执行时动态地设置参数值,提高了代码的灵活性和安全性。
public static void main(String[] args) throws SQLException {
// 1 让用户输入账户和密码
Scanner sc = new Scanner(System.in);
String username = sc.nextLine();
String password = sc.nextLine();
sc.close();
//2 通过账户和密码来查询用户是否存在
Connection conn =
DriverManager.getConnection("jdbc:mysql:///studydb","root","root");
String sql = "select * from users where username=? and password=?";//?占位符
PreparedStatement pstmt = conn.prepareStatement(sql);//对sql语句进行预编译
//设置sql语句中的参数 参数从左到右 依次设置 索引从1开始
pstmt.setString(1,username);
pstmt.setString(2,password);
System.out.println(sql);
ResultSet rs = pstmt.executeQuery();
//3 判断查询的结果 如果查询结构不为null 就成功 否则就失败
if(rs.next()){
System.out.println("登录成功");
}else{
System.out.println("登录失败");
}
//4 释放资源
rs.close();
pstmt.close();
conn.close();
}
}
@Test
public void insertTest() throws SQLException {
//2 通过账户和密码来查询用户是否存在
Connection conn =
DriverManager.getConnection("jdbc:mysql:///studydb","root","root");
String sql = "insert into users(username,password) values(?,?)";//?占位符
PreparedStatement pstmt = conn.prepareStatement(sql);//对sql语句进行预编译
//设置sql语句中的参数 参数从左到右 依次设置 索引从1开始
pstmt.setString(1,"tom");
pstmt.setString(2,"321654");
System.out.println(sql);
int rs = pstmt.executeUpdate();
//4 释放资源
pstmt.close();
conn.close();
}
将查询的结果集封装成一个对象
package cn.sxjzit.domain;
public class User {
private int id;
private String username;
private String password;
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
public User(int id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
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;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}}
public static void main(String[] args) throws SQLException {
// 1 让用户输入账户和密码
Scanner sc = new Scanner(System.in);
String username = sc.nextLine();
String password = sc.nextLine();
sc.close();
//2 通过账户和密码来查询用户是否存在
Connection conn =
DriverManager.getConnection("jdbc:mysql:///studydb","root","root");
String sql = "select * from users where username=? and password=?";//?占位符
PreparedStatement pstmt = conn.prepareStatement(sql);//对sql语句进行预编译
//设置sql语句中的参数 参数从左到右 依次设置 索引从1开始
pstmt.setString(1,username);
pstmt.setString(2,password);
System.out.println(sql);
ResultSet rs = pstmt.executeQuery();
//3 判断查询的结果 如果查询结构不为null 就成功 否则就失败
User user = null;
while(rs.next()){
user = new User();
user.setId(rs.getInt(1));
user.setUsername(rs.getString("username"));
user.setPassword(rs.getString(3));
}
if (user !=null){
System.out.println("登录成功");
}else{
System.out.println("登录失败");
}
//4 释放资源
rs.close();
pstmt.close();
conn.close();
}
作业:完成删除和更新
自增长主键回显的实现
public class JDBCBaseDemo3 {
public static void main(String[] args) throws SQLException {
//1 注册驱动 获取连接
Connection conn =
DriverManager.getConnection("jdbc:mysql:///studydb","root","root");
String sql = "insert into users(username,password) values(?,?)";
// 第二个参数 Statement.RETURN_GENERATED_KEYS 告诉statement携带回数据库生成的主键
PreparedStatement pstmt =
conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
// 参数赋值
pstmt.setString(1,"lucy");
pstmt.setString(2,"123456");
int rows = pstmt.executeUpdate();
System.out.println(rows);
// 获取新增记录的id值
ResultSet rs = pstmt.getGeneratedKeys();
if (rs.next()){
int newId= rs.getInt(1);
System.out.println("newId=" + newId);
}
// 释放资源
pstmt.close();
conn.close();
}
}
批量插入数据
public class JDBCBaseDemo3 {
public static void main(String[] args) throws SQLException {
//1 注册驱动 获取连接
Connection conn =
DriverManager.getConnection("jdbc:mysql:///studydb","root","root");
String sql = "insert into users(username,password) values(?,?)";
// 第二个参数 Statement.RETURN_GENERATED_KEYS 告诉statement携带回数据库生成的主键
PreparedStatement pstmt =
conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
// 参数赋值
pstmt.setString(1,"lucy");
pstmt.setString(2,"123456");
int rows = pstmt.executeUpdate();
System.out.println(rows);
// 获取新增记录的id值
ResultSet rs = pstmt.getGeneratedKeys();
if (rs.next()){
int newId= rs.getInt(1);
System.out.println("newId=" + newId);
}
// 释放资源
pstmt.close();
conn.close();
}
}
代码的优化重构
将建立连接和断开连接的重复代码集合成一块(形成一个工具类)
public class JDBCUtil {
//1 获取连接
public static Connection getConnection(){
Connection conn = null;
try {
conn =
DriverManager.getConnection("jdbc:mysql://localhost:3306/studydb","root","root");
} catch (SQLException e) {
throw new RuntimeException(e);
}
return conn;
}
//2 释放连接
public static void releaseSource(ResultSet rs, Statement stmt,Connection conn){
try {
if(rs !=null){
rs.close();
}
if (stmt!=null){
stmt.close();
}
if (conn !=null)
conn.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
@Test
public void queryTest() throws SQLException {
//通过工具类获得连接
conn = JDBCUtil.getConnection();
stmt = conn.createStatement();
// 4 发送sql语句
String sql = "select id,name,age,job,salary from emp where id= 1";
ResultSet rs = stmt.executeQuery(sql);
// 5 解析得到结果集
while(rs.next()){
int id = rs.getInt("id");
String name = rs.getString("name");
int age = rs.getInt("age");
String job = rs.getString("job");
int sal = rs.getInt("salary");
System.out.println(id +"--"+name+"--"+age+"---"+ job+"---"+sal);
}
//通过工具类释放连接
JDBCUtil.releaseSource(rs,stmt,conn);
}
将数据库连接属性写在一个配置文件中jdbc.properties
driverClassName = com.mysql.cj.jdbc.Driver
url= jdbc:mysql://localhost:3306/studydb
username=root
password=root
public class JDBCUtil {
private static String driverClassName;
private static String url;
private static String username;
private static String password;
static {
InputStream is=
JDBCUtil.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties prop = new Properties();
try {
prop.load(is);
driverClassName = prop.getProperty("driverClassName");
url = prop.getProperty("url");
username = prop.getProperty("username");
password = prop.getProperty("password");
Class.forName(driverClassName);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
//1 获取连接
public static Connection getConnection(){
Connection conn = null;
try {
conn = DriverManager.getConnection(url,username,password);
} catch (SQLException e) {
throw new RuntimeException(e);
}
return conn;
}
//2 释放连接
public static void releaseSource(ResultSet rs, Statement stmt,Connection conn){
try {
if(rs !=null){
rs.close();
}
if (stmt!=null){
stmt.close();
}
if (conn !=null)
conn.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
jdbc中事务的实现
涉及的动作:开启事务 提交/回滚事务
事务的特性:ACID
银行转钱案例
/**
* 数据访问层
*/
public class BankDao {
/**
* 加钱的方法
* @param account 被转账的账户
* @param money 转账的金额
* @return
*/
public int addMoney(Connection conn,String account, int money){
String sql = "update account set money = money+? where aname=?";
int rows = CRUDTemplate.executeUpdate(conn,sql,money,account);
return rows;
}
/**
* 减钱的方法
* @param account 转账的账户
* @param money 转账的金额
* @return
*/
public int subMoney(Connection conn,String account,int money){
String sql = "update account set money = money-? where aname=?";
int rows = CRUDTemplate.executeUpdate(conn,sql,money,account);
return rows;
}
}
/**
* 业务类 实现转账操作
*/
public class BankService {
private BankDao dao = new BankDao();
public void transfer(String addAccount,String subAccount,int money){
Connection conn = JDBCUtil.getConnection();
// 开启事务(关闭自动提交)
try {
conn.setAutoCommit(false);
dao.addMoney(conn,addAccount,money);
System.out.println(10/0);
dao.subMoney(conn,subAccount,money);
conn.commit();
} catch (SQLException e) {
try {
conn.rollback();
} catch (SQLException ex) {
throw new RuntimeException(ex);
}
throw new RuntimeException(e);
}finally {
try {
conn.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
}
public static int executeUpdate(Connection conn,String sql,Object ... params){
PreparedStatement pstmt = null;
int rows = 0;
try {
//获取sql语句的预编译对象
pstmt = conn.prepareStatement(sql);
//给占位符赋值
for (int i= 0 ;i <params.length;i++){
pstmt.setObject(i +1,params[i]);
}
// 执行sql
rows = pstmt.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException(e);
}finally {
JDBCUtil.releaseSource(null,pstmt,null);
}
return rows;
}