文章目录
- 什么是JDBC
- 数据库编程的必要条件
- JDBC工作原理
- JDBC如何建立数据库连接
- JBDC的基本操作
- 添加操作
- 修改操作
- 删除操作
- 查询操作
- JDBC使用的步骤
- JDBC的批处理操作
- JDBC数据源连接池
- 关于执行sql语句的对象
什么是JDBC
数据库编程的必要条件
- 编程语言,如Java,C、C++、Python等
- 数据库,如Oracle,MySQL,SQL Server等
- 数据库驱动包:不同的数据库,对应不同的编程语言提供了不同的数据库驱动包,如:MySQL提 供了Java的驱动包mysql-connector-java,需要基于Java操作MySQL即需要该驱动包。同样的, 要基于Java操作Oracle数据库则需要Oracle的数据库驱动包jdbc。
- JAVA的驱动包就是JDBC
- JDBC,即Java Database Connectivity,java数据库连接。是一种用于执行SQL语句的Java API,它是Java中的数据库连接规范。这个API由 java.sql.,javax.sql. 包中的一些类和接口组成,它为Java开发人员操作数据库提供了一个标准的API,可以为多种关系数据库提供统一访问
JDBC工作原理
- JDBC 为多种关系数据库提供了统一访问方式,作为特定厂商数据库访问API的一种高级抽象,它主要包含一些通用的接口类
- sun发布的Java程序与数据库通信的
规范
(也就是一系列接口) - Java语言访问数据库操作完全面向抽象接口编程
- 开发数据库应用不用限定在特定数据库厂商的API
- 程序的可移植性大大增强
- JDBC只支持关系型数据库,也就是使用表结构的数据库
- sun发布的Java程序与数据库通信的
- JDBC的驱动层是各大数据库厂商去实现JDBC的规范,然后将这些实现类打成压缩包,就是所谓的jar包
JDBC如何建立数据库连接
public class Demo1 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//1导入jar包——也就是对应数据块的驱动包
//2加载驱动——驱动其实是一个类,在JVM中我们学到了关于反射的forname,会引起我们的类加载和初始化
//也就把我们的相关的类加载到内存
Class.forName("org.gjt.mm.mysql.Driver");
//3通过驱动管理获取连接对象
//3.1 准备url
String url="jdbc:mysql://localhost:3306/fruitdb";
//"jdbc:mysql://127.0.0.1:3306/rocket_class?characterEncoding=utf8&useSSL=false?characterEncoding=utf8&useSSL=false")
//3.2准备用户名
String user="root";
//3.3准备密码
String pwd="******";
//4获取网络请求,根据上面的属性获得数据库的连接
//java.sql.Connection 连接对象
Connection con= DriverManager.getConnection(url,user,pwd);
System.out.printf("Connection"+con);
}
}
-
jar包的导入,只要把对应的项目中导入对应要操作数据库的jar包
-
加载驱动,其实就是将对应的数据库的类加载到我们的内存中
-
通过我们的驱动对象获取我们的连接对象,配置相关的数据库信息
-
url 类似我们的http协议的请求形式
- jdbc:mysql://类似http://
- localhost:3306 对应ip地址和端口
- fruitdb对应着我们的数据库,我们的http中是一个文件路径来找到对应的资源
- ?表示连接我们的设置,设置的形式是key:value这种形式,多个参数用&连接
-
user 是我们的登录用户的账号
-
pwd 是我们登录用户的密码
-
JBDC的基本操作
添加操作
package com.lsc.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* 实现JDBC的添加操作
*/
public class Demo2 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.jdbc.Driver");
String url="jdbc:mysql://localhost:3306/fruitdb?characterEncoding=utf8&useSSL=false";
String user="root";
String pwd="*****";
Connection connection= DriverManager.getConnection(url,user,pwd);
//1编写sql语句
String sql="insert into t_fruit values(0,?,?,?,?)";
//2创建预处理命令对象
PreparedStatement pst=connection.prepareStatement(sql);
//3填充参数
pst.setString(1,"菠萝蜜");
pst.setInt(2,15);
pst.setInt(3,100);
pst.setString(4,"这是菠萝蜜");
//4执行操作
int count= pst.executeUpdate();//对于增删改 我们使用executeUpdate 返回的是行数
System.out.println(count>0?"添加成功":"添加失败");
//5释放资源
pst.close();
connection.close();
}
}
- 添加操作使用的执行方法的executeUpdate(),返回值是对数据库影响的行数
修改操作
package com.lsc.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/**
* 添加操作
*/
public class Demo3 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
Class.forName("org.gjt.mm.mysql.Driver");
String url="jdbc:mysql://localhost:3306/fruitdb?characterEncoding=utf8&useSSL=false";
String user="root";
String pwd="******";
Connection connection= DriverManager.getConnection(url,user,pwd);
//编写sql语句
String sql = "update t_fruit set fname = ? , remark = ? where fid = ? " ;
PreparedStatement ps=connection.prepareStatement(sql);
Fruit fruit=new Fruit(33,"猕猴桃","这是一个猕猴桃");
ps.setString(1,fruit.getFname());
ps.setString(2,fruit.getRemark());
ps.setInt(3,fruit.getFid());
int count=ps.executeUpdate();
System.out.println(count>0?"修改成功":"修改失败");
//5释放资源
ps.close();
connection.close();
}
}
- 修改操作使用的执行方法的executeUpdate(),返回值是对数据库影响的行数
删除操作
/**
* 删除操作
*/
public class Demo4 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
Class.forName("org.gjt.mm.mysql.Driver");
String url="jdbc:mysql://localhost:3306/fruitdb?characterEncoding=utf8&useSSL=false";
String user="root";
String pwd="*****";
Connection connection= DriverManager.getConnection(url,user,pwd);
//编写sql语句
String sql = "delete from t_fruit where fid= ?" ;
PreparedStatement ps=connection.prepareStatement(sql);
Fruit fruit=new Fruit(33,"猕猴桃","这是一个猕猴桃");
ps.setInt(1,fruit.getFid());
int count=ps.executeUpdate();
System.out.println(count>0?"修改成功":"修改失败");
//5释放资源
ps.close();
connection.close();
}
}
- 删除操作使用的执行方法的executeUpdate(),返回值是对数据库影响的行数
查询操作
/**
* 查询操作
*/
public class Demo5 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
Class.forName("org.gjt.mm.mysql.Driver");
String url="jdbc:mysql://localhost:3306/fruitdb?characterEncoding=utf8&useSSL=false";
String user="root";
String pwd="*****";
//获得连接
Connection connection= DriverManager.getConnection(url,user,pwd);
//编写sql语句
String sql="select * from t_fruit";
//获取预处理对象
PreparedStatement ps=connection.prepareStatement(sql);
//获得结果集
ResultSet rs= ps.executeQuery();
//解析结果集
while (rs.next()){
int fid=rs.getInt("fid");
String fname=rs.getString("fname");
int price=rs.getInt("price");
int fcount=rs.getInt("fcount");
String remark=rs.getString("remark");
Fruit fruit=new Fruit(fid,fname,price,fcount,remark);
System.out.println(fruit);
}
//7.释放资源
rs.close();
ps.close();
connection.close();
}
}
- 查询操作使用的执行方法的executeQuery(),返回值是我们的ResultSet,里面存储着我们的数据
- 我们返回的ResultSet相当于这个橙色的箭头,next一下,箭头就会往下滑动,如果到头了,就会返回false
JDBC使用的步骤
-
导入相关数据库的jar包到我们的项目中
-
加载JDBC驱动程序
Class.forName("org.gjt.mm.mysql.Driver");
-
获取数据库的连接
String url="jdbc:mysql://localhost:3306/fruitdb?characterEncoding=utf8&useSSL=false"; String user="root"; String pwd="******"; Connection connection= DriverManager.getConnection(url,user,pwd);
-
编写我们的SQL语句
-
获取预处理对象
// 查询操作 preparedStatement.executeQuery(sql); // 新增、修改、删除操作 preparedStatement.executeUpdate(sql);
-
获得结果并处理
// 新增、修改、删除操作 返回的是一个整数 // 查询操作 返回的是ResultSet对象 while (resultSet.next()) { int xxx = resultSet.getInt("xxx"); String yyy= resultSet.getString("yyy"); ... }
-
关闭资源
try { if(resultSet != null){ resultSet.close(); } if(preparedStatement != null){ preparedStatement.close(); } if(connection != null){ connection.close(); } } catch (SQLException e) { e.printStackTrace(); throw new RuntimeException("数据库错误"); }
JDBC的批处理操作
/**
* 实现JDBC的批处理添加操作
*/
public class Demo1Batch {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.jdbc.Driver");
//批处理操作1 如果要进行批处理任务,URL需要添加一个参数rewriteBatchedStatements=true
String url="jdbc:mysql://localhost:3306/fruitdb?characterEncoding=utf8&useSSL=false&rewriteBatchedStatements=true";
String user="root";
String pwd="*****";
Connection connection= DriverManager.getConnection(url,user,pwd);
//1编写sql语句
String sql="insert into t_fruit values(0,?,?,?,?)";
//2创建预处理命令对象
PreparedStatement pst=connection.prepareStatement(sql);
//3批处理填充参数
for(int i=0;i<10;i++){
pst.setString(1,"批处理水果"+i);
pst.setInt(2,15);
pst.setInt(3,100);
pst.setString(4,"这是批处理水果");
//批处理操作2 addBatch()
pst.addBatch();
//如果批处理的次数太多,进行清空一次队列再进行
if(i%1000==0){
pst.executeBatch();
pst.clearBatch();
}
}
//批处理执行操作3
int [] count=pst.executeBatch();
for (int i: count) {
System.out.println(i);
}
//5释放资源
pst.close();
connection.close();
}
}
- 对于我们的批处理操作,我们的url必须要配置这个参数rewriteBatchedStatements=true
- 利用循环给我们的批处理去填充参数 addBatch()
- 进行执行批处理操作 executeBatch()
JDBC数据源连接池
先导入对应连接池的jar包
package com.lsc.jdbc;
import com.alibaba.druid.pool.DruidDataSource;
import java.sql.Connection;
import java.sql.SQLException;
//验证连接池中的connection可以重复使用
public class Demo02Druid {
public static void main(String[] args) throws SQLException, SQLException {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("org.gjt.mm.mysql.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/fruitdb?useSSL=false&useUnicode=true&characterEncoding=utf-8");
dataSource.setUsername("root");
dataSource.setPassword("123456");
//证明两点:
//1. 被close的连接对象并没有真正关闭,而是将状态重新设置为空闲状态,然后放回池子,这样下次获取连接对象,这个对象会被重复使用
//2. 没有close的连接对象会被一直占用,那么下次继续获取连接对象,是不会获取到这个对象的(hashcode没有重复,只出现一次)
for(int i = 0 ; i<5 ; i++){
Connection conn1 = dataSource.getConnection();
Connection conn2 = dataSource.getConnection();
System.out.println(conn1);
System.out.println(conn2);
if(i%3==0){
conn1.close();
conn2.close();
}
}
}
}
//输出结果
1com.mysql.jdbc.JDBC4Connection@2957fcb0
2com.mysql.jdbc.JDBC4Connection@1376c05c
3com.mysql.jdbc.JDBC4Connection@1376c05c
4com.mysql.jdbc.JDBC4Connection@2957fcb0
com.mysql.jdbc.JDBC4Connection@51521cc1
com.mysql.jdbc.JDBC4Connection@1b4fb997
com.mysql.jdbc.JDBC4Connection@deb6432
com.mysql.jdbc.JDBC4Connection@28ba21f3
com.mysql.jdbc.JDBC4Connection@28ba21f3
com.mysql.jdbc.JDBC4Connection@deb6432
- 我们通过结果可以发现1和4的数据源是一样的
- 被close的连接对象并没有真正关闭,而是将状态重新设置为空闲状态,然后放回池子,这样下次获取连接对象,这个对象会被重复使用
- 没有close的连接对象会被一直占用,那么下次继续获取连接对象,是不会获取到这个对象的(hashcode没有重复,只出现一次)
两种方式
DateSource方式(实际都是使用这种)
- 在内部创建Connection对象的连接池,池就是为了资源重复利用,当一个Connection对象调用close方法关闭不是真正关闭,这个对象会被DateSource回收,进入连接池,若此时有别的用户需要建立连接,不是创建一个新的连接,若此时连接池中有空闲连接,直接使用此链接,也就是不需要关闭物理连接,只重置,重置初始化后放入线程池
- 市面上有不同的连接池实现方式
DriverMannger方式
- DriverManager 类来获取的 Connection 连接,是无法重复利用的,每次使用完以后释放资源时,通过 connection.close() 都是关闭物理连接。
关于执行sql语句的对象
Statement
- 用于执行不带参数的简单SQL语句 比如select * from p
PreparedStatement——预处理对象
- 用于执行带或者不带参数的SQL语句
- 可以使用占位符 ? 下标从1开始
- SQL语句会预编译在数据库系统
- 执行速度快于Statement对象
- 这个sql语句就会保存到mysql系统中,这个SQL语句的语法和词法分析都已经执行过了,下一次使用时候,只是简单的替换内容即可
//编写sql语句
String sql="select * from t_fruit";
//获取预处理对象
PreparedStatement ps=connection.prepareStatement(sql);
//获得结果集
ResultSet rs= ps.executeQuery();
- 比statement更加安全,可以阻止常见的sql注入攻击
public class Demo06 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/fruitdb? useSSL=false&useUnicode=true&characterEncoding=utf-8","root","123456");
//String fname = "西瓜";
String fname = "西瓜' or 1=1 or fname='";
String sql = "select * from t_fruit where fname = '" + fname +"'";
System.out.println(sql);
Statement stmt = conn.createStatement() ;
ResultSet rs = stmt.executeQuery(sql);
while(rs.next()){
System.out.println(rs.getInt(1));
System.out.println(rs.getString("fname"));
System.out.println(rs.getInt(3));
System.out.println(rs.getInt("fcount"));
System.out.println(rs.getString("remark"));
}
}
}
//我们这个sql语句就把所有的信息都查出来,因为1=1是恒成立的