JDBC配置
一、最基础的配置(6步)
1、注册于实例化驱动(加载Driver驱动)
(1) 利用反射机制(最常用)
格式:
Class.forName("com.mysql.cj.jdbc.Driver");
注:因为Driver中用一个静态方法块(里面包含第二步),当执行反射动作时,就会执行静态代码块的代码,所以可以通过反射动作实例化驱动。
(2) 通过多态创建driver对象(底层实现)
格式:
Driver(接口) driver=new Driver(类)( );
DriverManager.registerDriver(driver);
2、获取数据库对象(获取数据库的连接对象Connection)
格式:
Connection 数据库对象 = DriverManager.getConnection(url, name, psw);
注:
Url: 统一资源定位符
Name:数据库账户名
Psw: 数据库密码
3、获取数据库操作对象(获取数据库的操作对象Statement(查询器);【执行增删改查】)
例如:
Statement 数据库操作对象 = 数据库对象.createStatement();
备注:
(1) sql注入:使用Statement会产生漏洞,也就是sql注入问题。
(2) sql注入产生的原因: 用户提供的信息,参与了sql语句的编译过程。
(我们会在下面解决这种sql注入的问题)
4、执行SQL语句
(1)执行查询操作,并返回查询结果集
ResultSet 查询结果集 = 数据库操作对象.executeQuery(sql);
(2)执行增删改操作,并返回影响数据库的行数
int 行数 = 数据库操作对象.executeUpdate(sql);
5、处理查询结果集(转储Resultset查询结果集)
Resultset结果集:
存储结构:
(1)Rsesultset对象.next();(返回值为boolean类型)
注:遍历结果集,next()当光标下一个行有元素,返回true,并移进下一行,如果没有行,则放回false。
(2)取数据
通过Resultset对象.getString(int dex)方法(JDBC中,所有的下表从1开始),通过while循环就可以迭代取出
注:
1)getString(int dex);
方法的特点是:不管数据库中的数据类型是什么,都已String的形式取出(dex代表的是第几列,也可以换成列名 “username”)
2) 对象.getint();
当数据内部int类型存储,可以用此方法
3) 对象.getdouble();
当数据库内部的数据是double类型存储,可以用此方法
6、释放资源(一定要在finally语句块中,一定会执行,首先判断是否为空)
(1)将数据库操作对象和数据库对象关闭,通过close方法
(2)优先级:从小到大
(3)分别处理异常,判断是否为空
代码:
finally {
try{
if (查询结果集对象!=null){
查询结果集对象.close();
}
}catch (Exception E){
System.out.println(E);
}
try{
if (数据库操作对象t!=null){
数据库操作对象.close();
}
}catch (Exception E){
System.out.println(E);
}
try{
if (数据库对象!=null){
数据库对象.close();
}
}catch (Exception E){
System.out.println(E);
}
}
二、JDBC的后续补充
1、上文中sql注入问题:
sql注入产生的原因: 用户提供的信息中存在着sql语句的关键字,并且这些关键字参与了sql语句的编译过程。
如何解决:让用户提供的信息,不参与sql语句的编译过程
要想用户提供的信息不参与sql语句的执行,那么必须使用PrepareStatement接口
PsrparedStatement接口(java.sql.PreparedStatement):
(1)
如何使用PerparedStatement:
(1)书写sql语句
String sql="SELECT * FROM `userinfo` WHERE userName= ? And userPsw= ?";
注:问号为占位符
(2)获取PerparedStatement操作对象,并预编译sql语句
PerparedStatement 数据库操作对象 = 数据库对象.prepareStatement(sql);
注:对sql语句预编译,可以检错
(3)对占位符进行赋值(JDBC的下标都是从1开始)
PerparedStatement对象.setString(1,username);
PerparedStatement对象.setString(2,userpsw);
(4)获取数据库操作对象
RequestSet对象 = PerparedStatement对象.executeQuery();
Int 行数 = PerparedStatement对象.executeUpdate();
代码实现:
public static boolean login(String username, String userpsw) {
boolean b = false;
String url = "jdbc:mysql://localhost:3306/test1?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false";
String name = "root";
String psw = "root";
Connection conn = null;
PreparedStatement pr = null;
ResultSet re = null;
String sql = "SELECT * FROM `userinfo`WHERE userName= ? AND userPsw= ? ";
try {
Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection(url, name, psw);
pr = conn.prepareStatement(sql);
pr.setString(1,username);
pr.setString(2,userpsw);
re = pr.executeQuery();
while (re.next()) {
b = true;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (re != null) {
try {
re.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (pr != null) {
try {
pr.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return b;
}
2、JDBC事务的处理
JDBC的事务机制,默认是自动提交的(只要执行一条DML语句,则就会自动提交一次)
在现实业务中,这种默认提交机制,不满足我们的需求,因为我们需要n条DML语句同时满足条件,我们才能够完成事务。(需求:在同一个事务中,同时成功,同时失败)
将JDBC中自动提交变为手动提交(3步走)
(1) 开启事务
数据库对象.SetAutoCommit(false);
(2) 提交事务
在保证事务成功的时候,提交事务
数据库对象.commit();
(3) 回滚事务
为了保证数据的安全性,在catch中添加上手动回滚
If(数据库对象!=null){
Try{
数据库对象.rollback();
}catch(SQLException e){
e.printStackTrace();
}
}
3、JDBC工具类
工具类的静态方法一般是私有的。(因为工具类当中,不需要new对象,直接采用类名调用就可以)
工具类的主要目的:为了代码的复用,减少的重复代码。
(1)因为加载驱动只需要执行一次,可以把驱动的加载写在静态代码块中,当类加载的时候,去执行,也利用了静态代码块,只会被执行一次。
(2)把建立连接用静态方法封装到“getconnection();”方法中,直接通过“类名.方法名();” (异常处理:因为我们调用这个方法的时候,就已经有了try-catch,我们只需要把异常直接抛出就可以,不需要再写try-catch)
调用就可以。
(3)可以把数据库对象、数据库操作对象、查询结果集对象的关闭,封装到一个类中,其中关闭,首先判断是否为空,在进行关闭。(异常处理:当我们在finally方法体内调用这个方法的时候,因为外面没有try-catch,所以我们需要在里面写上)
(4)代码实现:
//静态方法:实例化驱动
static {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//构造方法私有化,不需要产生对象
private JDBCdemo(){
}
//获取连接对象
public static Connection getConnection() throws SQLException {
Connection conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/test1?serverTimezone=UTC&characterEn" +
"coding=utf8&useUnicode=true&useSSL=false","root","root");
return conn;
}
//关闭对象
public static void close(Connection conn, Statement ps, ResultSet re){
if( re!=null){
try {
re.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if( ps!=null){
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if( conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
4、悲观锁与乐观锁机制
悲观锁:事务必须排队执行,数据锁住了,不允许并发(行级锁)
乐观锁:支持并发,事务也不需要排队,只不过需要一个版本号,只有前后版本号相同时,才会提交事务,否则回滚事务。
悲观锁:在sql语句最后面加上 “for update”