com.mysql.cj.jdbc.Driver类的源代码:
可以看到有一个 静态代码块,是一个获取驱动的静态方法,
所以我们可以使用Class.forName()的反射方式来获取该类的字节码文件,该反射方式会导致该类的静态代码块执行,
这就是JDBC获取驱动的第二种方式(常用)
import java.sql.DriverManager;
import java.sql.SQLException;
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
public Driver() throws SQLException {
}
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
}
这就是JDBC获取驱动的第二种方式(常用)
使用 Class.forName()反射,具体看上面的介绍。
参数是一个字符串,可以写在xxx.properties文件中
Class.forName()方法不需要有返回值,我们只是想用它的类加载动作,执行静态代码块
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class JDBCTest04 {
public static void main(String[] args) {
try {
//获取驱动
//注册驱动的第一种方式(不常用)
// DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());
//获取驱动的第二种方式,记得处理异常
Class.forName("com.mysql.cj.jdbc.Driver");
//获取连接
Connection conn =
DriverManager.getConnection("jdbc:mysql://localhost:3306/db2?
useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC",
"root",
"****");
System.out.println(conn);
} catch (SQLException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
使用配置文件使用JDBC;
需要注意路径问题。
Java中不建议把数据库的信息写死在Java代码里面
以后就不用修改Java代码了只需要改变配置文件的value值就可以改变数据库的类型
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ResourceBundle;
public class JDBCTest05 {
public static void main(String[] args) {
//使用资源绑定器绑定资源配置文件
ResourceBundle bundle = ResourceBundle.getBundle("com.bjpowernode.JDBC");
//用key值获取value值
String driver=bundle.getString("driver");
String url=bundle.getString("url");
String user=bundle.getString("user");
String password=bundle.getString("password");
try {
Class.forName(driver);
Connection conn = DriverManager.getConnection(url, user, password);
System.out.println(conn);
} catch (SQLException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
处理查询结果集:
使用数据库操作对象,执行sql语句时,sql语句时DQL语句时,使用executeQuery()方法
返回的结果类型是:ResultSet
ResultSet rs=stmt.executeQuery(sql);
接口 ResultSet
ResultSet
对象具有指向其当前数据行的光标。最初,光标被置于第一行之前。next方法将光标移动到下一行;因为该方法在ResultSet
对象没有下一行时返回false
,所以可以在while
循环中使用它来迭代结果集。默认的
ResultSet
对象不可更新,仅有一个向前移动的光标。因此,只能迭代它一次,并且只能按从第一行到最后一行的顺序进行。可以生成可滚动和/或可更新的ResultSet
对象。
boolean | next() 将光标从当前位置向前移一行。 |
获取数据
String | getString(int columnIndex) 以 Java 编程语言中 String 的形式获取此 ResultSet 对象的当前行中指定列的值。 |
String | getString(String columnLabel) 以 Java 编程语言中 String 的形式获取此 ResultSet 对象的当前行中指定列的值。 |
里面的参数是表的 列的下标 或者 列的名称,JDBC所有的下标都是从1开始,不是从0开始
注意写列的名称时,是你查询语句的名称,如果你起了别名,需要使用别名来进行查询
还可以用特定的类型取出:
处理getSting方法还可以转换成其他的类型getInt()...等,它们可以之间做运算,
但是一般不这样使用,这样一定得和表中字段得数据类型一致
代码示例:
import java.sql.*;
import java.util.ResourceBundle;
public class JDBCTest06 {
public static void main(String[] args) {
ResourceBundle bundle = ResourceBundle.getBundle("com.bjpowernode.JDBC");
String driver=bundle.getString("driver");
String url=bundle.getString("url");
String user=bundle.getString("user");
String password=bundle.getString("password");
Connection conn=null;
Statement stmt=null;
ResultSet rs=null;
try {
//获取驱动
Class.forName(driver);
//获取连接
conn = DriverManager.getConnection(url, user, password);
//获取数据库操作对象
stmt = conn.createStatement();
String sql="select * from sc";
//执行sql,executeQuery专门执行DQL语句的方法
rs = stmt.executeQuery(sql);
// int executeUpdate(insert,delete,update)
// ResultSet executeQuery(select)
//处理查询处理结果集
while (rs.next()){
String sid = rs.getString(1);
String cid = rs.getString(2);
String score = rs.getString(3);
System.out.println(sid+" "+cid+" "+score);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}finally {
if (rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (stmt!=null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
} if (conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
sql注入:
用户输入的信息中含有sql语句的关键字,并且这些关键字参与了sql语句的编译过程
导致sql语句的原意被扭曲,进而达到sql注入。
解决sql注入问题:
如果把数据先编译,就可以解决sql注入问题。
只要用户提供的信息不参与sql语句的编译过程,问题就解决了。
即使用户提供的信息中含有SQL语句的关键字,但是没有参与编译,不起作用
要想用户信息不参与SQL语句的编译,那么必须使用java.sql.PreparedStatement
java.sql.PreparedStatement接口继承了java.sql.Statement接口
PreparedStatement是属于预编译的数据库操作对象。
PreparedStatement的原理是:
预先对SQL语句的框架进行编译,然后再给SQL语句传“值”。
Statemen 和PreparedStatement的区别:
Statement编译一次执行一次
PreparedStatement 编译一次可以执行N次,效率更高一点
PreparedStatement 会在编译阶段做数据类型的安全检查
PreparedStatement 使用较多,
但是有一些系统是需要sql注入的只能使用Statement(升序和降序的情况)
使用Statement
//获取驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//获取连接
conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/db2?" +
"useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC",
"root",
"****");
//获取数据看操作对象
stmt=conn.createStatement();
String sql="select * from jdbctest where id='"+user.get("userId") +" '" +
" and password='" +user.get("password")+"'";
//执行sql
rs = stmt.executeQuery(sql);
使用PreparedStatement
往“?”传值,提供了各种数据类型的方法,常用String
void | setString(int parameterIndex, String x) 将指定参数设置为给定 Java String 值。 |
//获取数据预编译操作作对象
//需要把sql语句写在上面,并且使用"?"号来代替传过来的值,不能用单引号括起来
//其中一个问号,表示一个占位符,一个占位符将来接收一个值
String sql="select * from jdbctest where id=? and password=? ";
//程序执行到此处,会发送sql语句的框子给DBMS(数据库管理系统),然后DBMS进行sql的预先编译
ps=conn.prepareStatement(sql);
//给占位符传值,第一个参数是第一个问号的下标,第二个是要传入的值
ps.setString(1,user.get("userId"));
ps.setString(2,user.get("password"));
//执行sql,使用预编译对象不需要在传参数sql了
rs = ps.executeQuery();
JDBC事务的提交
JDBC中的事务是自动提交的,只要执行任意一条DML语句,则提交一次。
但是实际业务中,通常都是多条DML语句共同联合才能完成,使用下面的方法。
void | setAutoCommit(boolean autoCommit) 将此连接的自动提交模式设置为给定状态。 |
将此连接的自动提交模式设置为给定状态。如果连接处于自动提交模式下,则它的所有 SQL 语句将被执行并作为单个事务提交。否则,它的 SQL 语句将聚集到事务中,直到调用
commit
方法或rollback
方法为止。默认情况下,新连接处于自动提交模式。提交发生在语句完成时。语句完成的时间取决于 SQL 语句的类型:
- 对于 DML 语句(比如 Insert、Update 或 Delete)和 DDL 语句,语句在执行完毕时完成。
- 对于 Select 语句,语句在关联结果集关闭时完成。
- 对于
CallableStatement
对象或者返回多个结果的语句,语句在所有关联结果集关闭并且已获得所有更新计数和输出参数时完成。注:如果在事务和自动提交模式更改期间调用此方法,则提交该事务。如果调用
setAutoCommit
而自动提交模式未更改,则该调用无操作(no-op)。参数:
autoCommit
- 为true
表示启用自动提交模式;为false
表示禁用自动提交模式
commit()手动提交事务,rollback()回滚事务
使用工具类进行模糊查询
JDBC工具类:
import java.sql.*;
/**
* JDBC工具类,简化JDBC编程
*/
public class DBUtils {
/**
* 工具类的构造方法一般都是私有的(不是必须的)
* 因为工具类当中的方法都是静态的,不需要new对象,直接采用类名调用
*/
private DBUtils() {
}
//静态代码块在类加载时执行,并且只执行一次
static {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 获取数据库连接对象
* @return
* @throws SQLException
*/
public static Connection getConnection() throws SQLException {
//第二步:获取连接
String url = "jdbc:mysql://localhost:3306/db2?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC";
String user = "root";
String password = "****";
return DriverManager.getConnection(url, user, password);
}
/**
* 关闭连接
* @param conn 连接对象
* @param stmt 操作对象
* @param rs 结果集
*/
public static void close(Connection conn, Statement stmt, ResultSet rs){
try {
if (rs!=null) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (stmt!=null) {
stmt.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
try {
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
模糊查询:
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* 测试工具类
* 模糊查询
*/
public class JDBCTest02 {
public static void main(String[] args) {
Connection conn=null;
PreparedStatement ps=null;
ResultSet rs=null;
try {
//使用工具类获取连接对象
conn = DBUtils.getConnection();
//获取数据库预编译对象
String sql="select * from student where name like ? ";
ps = conn.prepareStatement(sql);
ps.setString(1,"小%");
rs = ps.executeQuery();
while (rs.next()){
String id = rs.getString(1);
String name = rs.getString(2);
String sid= rs.getString(3);
System.out.println(id+","+name+","+sid);
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
DBUtils.close(conn,ps,rs);
}
}
}