JDBC
JDBC(Java DataBase Connectivity)是Java和数据库之间的一个桥梁,是一个规范,能够执行SQL语句。
由一组用Java语言编写的 类 和 接口 组成
各数据库厂商根据此规范开发出相应的功能,称之为 驱动
有了 JDBC 提供的api,我们 几乎可以不改代码,就能操作不同的数据库,简化的开发难度,提高开发速度,也就是说:JDBC 实现了 跨数据库 操作
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GiagpsfV-1629773538468)(C:/Users/libra/Desktop/5/5.4/image-20210821130141581.png)]
核心组件
DriverManager: 此类管理数据库驱动程序列表。使用通信子协议将来自java应用程序的连接请求 与适当的数据库驱动程序匹配。
Driver:此接口处理与数据库服务器的通信,我们很少会直接与Driver对象进行交互。而是使用 DriverManager对象来管理这种类型的对象。
Connection:该界面具有用于联系数据库的所有方法。连接对象表示通信上下文,即,与数据库 的所有通信仅通过连接对象。
Statement:使用从此接口创建的对象将SQL语句提交到数据库。除了执行存储过程之外,一些派生接口还接受参数。
ResultSet:在使用Statement对象执行SQL查询后,这些对象保存从数据库检索的数据。它作为一 个迭代器,允许我们移动其数据。
SQLException:此类处理数据库应用程序中发生的任何错误
操作流程
构建JDBC应用程序涉及以下六个步骤:
导入包
需要包含包含数据库编程所需的JDBC类的包。大多数情况下,使用import java.sql.*就足够 了。
注册JDBC驱动程序
要求您初始化驱动程序,以便您可以打开与数据库的通信通道。此步骤将使JVM将所需的驱动程序实现加载到内存中,以便它可以满足您的JDBC 请求。
驱动地址
RDBMS | JDBC驱动程序名称 | 网址格式 |
---|---|---|
MYSQL8 | com.mysql.cj.jdbc.Driver | jdbc:mysql://hostname:3306/databaseName?serverTimezone=UTC |
MySQL | com.mysql.jdbc.Driver | jdbc:mysql://hostname:3306/databaseName |
RACLE | oracle.jdbc.driver.OracleDriver | jdbc:oracle:thin:@hostname:port Number: databaseName |
DB2 | com.ibm.db2.jdbc.net.DB2Driver | jdbc:db2:hostname:port Number / databaseName |
SYBASE | com.sybase.jdbc.SybDriver | jdbc:sybase:Tds:hostname:port Number / databaseName |
Class.forName();
注册驱动程序最常见的方法是使用Java的Class.forName()方法,将驱动程序的类文件动态加载到内存 中,并将其自动注册
try {
Class.forName("com.mysql.cj.jdbc.Driver");
}catch(ClassNotFoundException ex) {
System.out.println("Error: unable to load driver class!");
System.exit(1);
}
DriverManager.registerDriver()
第二种方法是使用静态DriverManager.registerDriver()方法。
try {
Driver myDriver = new com.mysql.cj.jdbc.Driver();
DriverManager.registerDriver( myDriver );
}catch(ClassNotFoundException ex) {
System.out.println("Error: unable to load driver class!");
System.exit(1);
}
数据库URL配置
加载驱动程序后,可以使用DriverManager.getConnection()方法建立连接。
创建数据库连接对象
String URL = "jdbc:mysql://localhost:3306/yhp2?serverTimezone=UTC";
String USER = "username";
String PASS = "password"
Connection conn = DriverManager.getConnection(URL, USER, PASS);
打开连接
需要使用DriverManager.getConnection()方法创建一个Connection对象,该对象表 示与数据库的物理连接。
使用数据库URL和属性对象 DriverManager.getConnection()方法的形式需要一个数据库URL和一个Properties对象
DriverManager.getConnection(String url, Properties info);
import java.util.*;
String URL = "jdbc:mysql://localhost:3306/yhp2?serverTimezone=UTC";
Properties info = new Properties( );
info.put( "user", "username" );
info.put( "password", "password" );
Connection conn = DriverManager.getConnection(URL, info)
关闭数据库连接
conn.close();
执行查询
需要使用类型为JDBC Statement和PreparedStatement的对象来构建和提交SQL语句到数据库。
Statement接口不能 接受参数,在运行时使用静态SQL语句时很有用。
PreparedStatement接口在运行时接受 输入参数,当您计划多次使用SQL语句时使用。
Statement(状态通道)
1.在使用Statement对象执行SQL语句之前,需要使用Connection对象的createStatement()方法创建 一个对象
Statement stmt = null;
try {
stmt = conn.createStatement( );
. . .
}
catch (SQLException e) {
. . .
}
finally {
stmt.close();
}
2.创建Statement对象后,您可以使用它来执行一个SQL语句,其中有三个执行方法之一。
boolean execute(String SQL):如果可以检索到ResultSet对象,则返回一个布尔值true; 否则返 回false。使用此方法执行SQL DDL语句或需要使用真正的动态SQL时。
int executeUpdate(String SQL):返回受SQL语句执行影响的行数。使用此方法执行预期会影响 多个行的SQL语句,例如INSERT,UPDATE或DELETE语句。
ResultSet executeQuery(String SQL):返回一个ResultSet对象。当您希望获得结果集时,请使 用此方法,就像使用SELECT语句一样。
3.关闭Statement对象
就像我们关闭一个Connection对象以保存数据库资源一样,由于同样的原因,还应该关闭Statement对 象。**如果先关闭Connection对象,它也会关闭Statement对 象。**但是,应始终显式关闭Statement对象,以确保正确清理。
SQL注入
指使用Statement对象提交时通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执 行恶意的SQL命令。
PreparedStatement(预状态通道)
创建PreparedStatement对象
PreparedStatement pstmt = null;
try {
String SQL = "Update Employees SET age = ? WHERE id = ?";
pstmt = conn.prepareStatement(SQL);
. . .
}
catch (SQLException e) {
. . .
}
finally {
pstmt.close();
}
JDBC中的所有参数都由?符号,这被称为参数标记。在执行SQL语句之前,必须为每个参数提供值。
所述的setXXX()方法将值绑定到所述参数,其中XXX代表要绑定到输入参数的值的Java数据类型。如果 忘记提供值,将收到一个SQLException。
每个参数标记由其顺序位置引用。第一个标记表示位置1,下一个位置2等等。该方法与Java数组索引从0开始是不 同的。
关闭PreparedStatement对象 就像关闭Statement对象一样,由于同样的原因,还应该关闭PreparedStatement对象。 一个简单的调用close()方法将执行该作业。**如果先关闭Connection对象,它也会关闭 PreparedStatement对象。**但是,应始终显式关闭PreparedStatement对象,以确保正确清理。
对比statement和PreparedStatement
(1)statement属于状态通道,PreparedStatement属于预状态通道
(2)预状态通道会先编译sql语句,再去执行,比statement执行效率高
(3)预状态通道支持占位符?,给占位符赋值的时候,位置从1开始
(4)预状态通道可以防止sql注入,原因:预状态通道在处理值的时候以字符串的方式处理
从结果集中提取数据
查询:用ResultSet对象接收,需要使用相应的ResultSet.getXXX()方法(XXX为数据类型)从结果集中检索数据。
ResultSet对象维护指向结果集中当前行的游标。术语“结果集”是指包含在ResultSet对象中的行和列数 据。
类型 | 描述 |
---|---|
ResultSet.TYPE_SCROLL_INSENSITIVE | 光标可以向前和向后滚动,结果集对创建结果集后发生的数据库的其他更改不敏感。 |
ResultSet.TYPE_SCROLL_SENSITIVE。 | 光标可以向前和向后滚动,结果集对创建结果集之后发生的其他数据库所做的更改敏感。 |
ResultSet.TYPE_FORWARD_ONLY(默认状态) | 光标只能在结果集中向前移动。 |
修改:仅返回受到影响的行数,只需要基础类(如int),就能接收。
释放资源
需要明确地关闭所有数据库资源,而不依赖于JVM的垃圾收集。
为确保连接关闭,您可以在代码中提供一个“finally”块。一个finally块总是执行,不管是否发生异常。 要关闭上面打开的连接,你应该调用close();
通常需要关闭的有以下几种:
Connectionl;
PreparedStatement;
ResultSet;
JDBC批处理
批量处理允许您将相关的SQL语句分组到批处理中,并通过对数据库的一次调用提交它们。 当您一次向数据库发送多个SQL语句时,可以减少连接数据库的开销,从而提高性能。
Statement批处理
使用createStatement()方法创建Statement对象。
使用setAutoCommit()将auto-commit设置为false 。
使用addBatch()方法在创建的语句对象上添加您喜欢的SQL语句到批处理中。
在创建的语句对象上使用executeBatch()方法执行所有SQL语句。
最后,使用commit()方法提交所有更改。
Statement stmt = conn.createStatement(); conn.setAutoCommit(false); //sql1 String SQL = "INSERT INTO Employees (id, first, last, age) VALUES(200,'Zia', 'Ali', 30)"; stmt.addBatch(SQL); //sql2 String SQL = "INSERT INTO Employees (id, first, last, age) VALUES(201,'Raj', 'Kumar', 35)"; stmt.addBatch(SQL); //sql3 String SQL = "UPDATE Employees SET age = 35 WHERE id = 100"; stmt.addBatch(SQL); int[] count = stmt.executeBatch(); conn.commit();
PreparedStatement批处理
使用占位符创建SQL语句。
使用prepareStatement() 方法创建PrepareStatement对象。
使用setAutoCommit()将auto-commit设置为false 。
使用addBatch()方法在创建的语句对象上添加您喜欢的SQL语句到批处理中。
在创建的语句对象上使用executeBatch()方法执行所有SQL语句。
最后,使用commit()方法提交所有更改。
String SQL = "INSERT INTO Employees (id, first, last, age) VALUES(?, ?, ?, ?)"; PreparedStatement pstmt = conn.prepareStatement(SQL); conn.setAutoCommit(false); // Set the variables pstmt.setInt( 1, 400 ); pstmt.setString( 2, "Pappu" ); pstmt.setString( 3, "Singh" ); pstmt.setInt( 4, 33 ); // Add it to the batch pstmt.addBatch(); // Set the variables pstmt.setInt( 1, 401 ); pstmt.setString( 2, "Pawan" ); pstmt.setString( 3, "Singh" ); pstmt.setInt( 4, 31 ); // Add it to the batch pstmt.addBatch(); //add more batches //Create an int[] to hold returned values int[] count = stmt.executeBatch(); //Explicitly commit statements to apply changes conn.commit();
反射处理结果集
java反射机制是什么
反射机制是在运行状态中,可以知道任何一个类的属性和方法,并且调用类的属性和方法;
反射机制能够做什么
1、判断运行对象的所属类
2、构造任意一个类的对象
3、获取任意一个类的属性和方法
4、调用任意属性和方法
5、生成动态代理
主要步骤
1.先利用 SQL 进行查询,得到结果集
2.利用反射创建实体类的对象:创建 Student 对象
3.获取结果集的列的数量,赋予”set+名“的字符串
4.再获取结果集的每一列的值, 结合 3的字符串,利用method写入Object无参对象,将Object的对象写入 list集合,就可以查看了
@Override
public List<Student> findallstudent(Class cla) {
Connection con = null;
PreparedStatement pps = null;
ResultSet rs =null;
try {
Class.forName("com.mysql.jdbc.Driver");
con = DriverManager.getConnection("jdbc:mysql://localhost:3306/yhp",
"root", "123456");
List list=new ArrayList();
String sqla = "select * from student";
pps = con.prepareStatement(sqla);
rs = pps.executeQuery();
//得到数据库中的所有的列有哪些?
ResultSetMetaData metaData = rs.getMetaData();//返回数据库中的相关信息
int count=metaData.getColumnCount();//得到列数
String[] columnnames=new String[count];
for (int i = 0; i < count; i++) {
// System.out.println(metaData.getColumnName(i+1));//列的位置从1开始
columnnames[i]=metaData.getColumnName(i+1);
}
//得到实体类中的所有的方法
Method[] methods =cla.getDeclaredMethods();
while(rs.next()){
Object s=cla.newInstance();//调取无参构造创建对象
for (String columnname : columnnames) {
String name="set"+columnname;//setstuid
for (Method method : methods) {
if(method.getName().equalsIgnoreCase(name)){
method.invoke(s,rs.getObject(columnname));//执行了对应的set方法
break;
}
}
}
list.add(s);
}
System.out.println("执行成功");
return list;
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (rs != null) {
rs.close();
}
if (pps != null) {
pps.close();
}
if (con != null) {
con.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
return null;
}
可以写一个util包的jdbc工具类,用于储存常用的连接,查找,修改,关闭资源等方法。
使用ResourceBundle访问本地资源
在设计时,我们往往需要访问一些适合本地修改的配置信息,如果作为静态变量,那么每次修改都 需要重新编译一个class,.config保存此类信息并不适合,这时我们需要ResourceBundle。
通过ResourceBundle,我们需要访问位于/WEB-INF/classes目录下的一个后缀名为properties的文本类型文件,从里面读取我们需要的值。
连接池
最小连接数:
是数据库一直保持的数据库连接数,所以如果应用程序对数据库连接的使用量不大,将有大量的数据库 资源被浪费。
初始化连接数:
连接池启动时创建的初始化数据库连接数量。
最大连接数:
是连接池能申请的最大连接数,如果数据库连接请求超过此数,后面的数据库连接请求被加入到等待队 列中。
最大等待时间:
当没有可用连接时,连接池等待连接被归还的最大时间,超过时间则抛出异常,可设置参数为0或者负 数使得无限等待(根据不同连接池配置)。
数据连接池原理
连接池基本的思想是在系统初始化的时候,将数据库连接作为对象存储在内存中,当用户需要访问数 据库时,并非建立一个新的连接,而是从连接池中取出一个已建立的空闲连接对象。使用完毕后,用户 也并非将连接关闭,而是将连接放回连接池中,以供下一个请求访问使用。而连接的建立、断开都由连 接池自身来管理。同时,还可以通过设置连接池的参数来控制连接池中的初始连接数、连接的上下限数 以及每个连接的最大使用次数、最大空闲时间等等,也可以通过其自身的管理机制来监视数据库连接的 数量、使用情况等。
注1:在DBCP连接池的配置中,还有一个maxIdle的属性,表示最大空闲连接数,超过的空闲连接将被释 放,默认值为8。对应的该属性在Druid连接池已不再使用,配置了也没有效果,c3p0连接池则没有对 应的属性。
注2:数据库连接池在初始化的时候会创建initialSize个连接,当有数据库操作时,会从池中取出一个连 接。如果当前池中正在使用的连接数等于maxActive,则会等待一段时间,等待其他操作释放掉某一个 连接,如果这个等待时间超过了maxWait,则会报错;如果当前正在使用的连接数没有达到 maxActive,则判断当前是否空闲连接,如果有则直接使用空闲连接,如果没有则新建立一个连接。在 连接使用完毕后,不是将其物理连接关闭,而是将其放入池中等待其他操作复用。