JDBC :连接数据库;JDBC相关API ;SQL注入问题解决;调用存储过程和函数;事务

JDBC

一、连接数据库

//JDBC:Java为连接数据库提供的一套接口(规范)
//1、导入数据库厂商提供的驱动
//	导入jar包,依赖jar包
//2、加载驱动
//驱动在5.0以上,此步骤可以省略不写

Class.forName("com.mysql.jdbc.Driver");

//3、建立连接
//URL:统一资源定位符
//格式:"主协议:子协议://ip:端口/资源"
//本地连接:localhost:3306可以省略不写
//		即:"jdbc:mysql:///mydb" 

String url="jdbc:mysql://localhost:3306/mydb";
String username="root";
String password="123456";
Connection conn=DriverManager.getConnection(url,username,password);
//4、获取操作对象
Statement st=conn.createStatement();
//5、编写SQL语句
String sql="select * from student";	//SQL语句
6、执行SQL语句
st.executeQuery(sql);//执行SQL语句
7、释放资源
conn.close();
st.close();

二、JDBC相关类及常用方法

DriverManager //驱动管理类
	getConnection(); //试图建立到给定数据库 URL 的连接
Connection//接口:与特定数据库的连接(会话)
    createStatement(String sql);//创建一个 Statement 对象来将 SQL 语句发送到数据库
	prepareStatement(String sql);
	//创建一个 PreparedStatement 对象来将参数化的 SQL 语句发送到数据库
	prepareStatement(String sql, int autoGeneratedKeys)
    //创建一个默认 PreparedStatement 对象,该对象能获取自动生成的键。
Statement//接口:用于执行静态 SQL 语句并返回它所生成结果的对象
    executeUpdate();//执行DML语句,返回值是影响行数
	executeQuery();//执行DQL语句,返回值是查询的结果集
	execute();//执行任何语句
	addBatch(String sql)//将给定的 SQL 命令添加到此 Statement 对象的当前命令列表中
    executeBatch()//将一批命令提交给数据库来执行,如果全部命令执行成功,则返回更新计数组成的数组    
ResultSet//接口
/*表示数据库结果集的数据表,通常通过执行查询数据库的语句生成;
ResultSet 对象具有指向其当前数据行的光标。最初,光标被置于第一行之前。next 方法将光标移动到下一行;
因为该方法在 ResultSet 对象没有下一行时返回 false,所以可以在 while 循环中使用它来迭代结果集。
beforeFirst() //将光标移动到此 ResultSet 对象的开头,正好位于第一行之前。*/
        
//遍历取出结果集中对象
ResultSet re=st.executeQuery(sql);    
while(re.next()){
    int id=re.getInt("id");//参数为表头序号或者表头字段名
    String username=re.getString(2);
    /*处理这些零碎数据的方法:
		把查询出来的数据封装到类里
		再把对象存到集合里*/
}

演示一:模拟登录

import java.sql.*;
import java.util.Scanner;

public class Example03 {
    public static void main(String[] args) throws Exception{
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String username = sc.nextLine();
        System.out.println("请输入密码:");
        String password=sc.nextLine();
        Class.forName("com.mysql.jdbc.Driver");
        String url="jdbc:mysql:///mydb";
        Connection conn = DriverManager.getConnection(url, "root", "123456");
        String sql="select * from users where username=? and password=?";
        PreparedStatement ps = conn.prepareStatement(sql);
        ps.setString(1,username);
        ps.setString(2,password);
        ResultSet resultSet = ps.executeQuery();
        //如果查到结果,说明登录成功
        if(resultSet.next()){
            System.out.println("登录成功!");
        }else {
            System.out.println("登录失败!");
        }
        ps.close();
        resultSet.close();
        conn.close();
    }
}

演示二:批量操作

//部分代码省略
String sql="insert into demo values(?)";
        PreparedStatement ps = connection.prepareStatement(sql);
        for (int i = 1; i < 10000; i++) {
            ps.setInt(1,i);
            ps.addBatch();
        }
        ps.executeBatch();

演示三:获取自增长键的值

//部分代码省略
//要获取自增长键的值,需要在获取操作对象时声明一个参数 Statement.RETURN_GENERATED_KEYS
PreparedStatement preparedStatement = conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
//获取自增长键的结果集
ResultSet generatedKeys = preparedStatement.getGeneratedKeys();
while (generatedKeys.next()){
    keyValue = generatedKeys.getInt(1);
}

三、安全问题

SQL注入:通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。

1、SQL注入案例:使用拼串的形式写SQL语句

//部分代码省略
Statement st=conn.createStatement();
String name="1'or'1'='1";
"select * from student name='"+name+"'";
st.executeQuery(sql);//执行SQL语句,此语句可以查出student表中所有信息

2、防止SQL注入:使用PrepareStatement 预编译操作对象

//部分代码省略
//SQL语句中的参数先用 ? 占位
String name="张三";
String sql="select * from student where name=?"
PrepareStatement ps=conn.prepareStatement(sql);
//给 ? 赋值
ps.setString(1,name);//参一为 ? 的索引,注意索引从1开始计算,参二是 ? 的值
ps.executeQuery();	//执行SQL语句,注意不再传入SQL语句

四、调用存储过程和函数

1、调用存储过程

SQL语句格式 {call <procedure-name>[(<arg1>,<arg2>, ...)]}

Java代码:部分省略

	String sql="{call myPro(?,?)}";
    CallableStatement callableStatement = connection.prepareCall(sql);
    callableStatement.setInt(1,-1);//给第一个问号赋值
	//确定第二个问号的数据类型
    callableStatement.registerOutParameter(1, Types.VARCHAR);
    callableStatement.execute();//执行SQL语句
    String r = callableStatement.getString(2);//获取返回值
    System.out.println(r);

myPro 存储过程代码

DELIMITER $$
CREATE PROCEDURE pro_testIf(IN num INT,OUT str VARCHAR(2))
BEGIN
	IF num>0 THEN
		SET str='正数';		-- 注意要用分号结束
	ELSEIF num<0 THEN        --注意elseif 连写
		SET str='负数';            
	ELSE
		SET str='零';
	END IF;         		 --注意要结束if,要写分号
	END $$
DELIMITER;

2、调用函数

SQL语句格式: {?= call <procedure-name>[(<arg1>,<arg2>, ...)]}

Java代码:部分省略

	String sql="{?=call myFun(?)}";
    CallableStatement callableStatement = connection.prepareCall(sql);
    callableStatement.setInt(2,1);//给第二个 ? 赋值,删除 id=1 的人
    callableStatement.registerOutParameter(1,Types.INTEGER);//确定第一个 ? 数据的类型
    callableStatement.execute();//执行sql语句
    int r = callableStatement.getInt(1);//获取函数返回值
    System.out.println(r);

myFun函数代码

create function myFun(num int)
  returns int
  BEGIN
    declare i int default 0;-- 定义i;相当于java中 //int i=0;
    delete from mydemo where id=num;-- 删除id=1的人
    select count(*) from mydemo into i;-- 统计剩余人数,赋给i
    return i;-- 返回i
    END;

表格

在这里插入图片描述

五、事务

概述:事务是指一组最小逻辑操作单元,里面有多个操作组成。组成事务的每一部分必须要同时提交成功,如果有一个操作失败,整个操作就回滚。

1、事务四大特性(ACID):

原子性(Atomicity):原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。

一致性(Consistency):事务必须使数据库从一个一致性状态变换到另外一个一致性状态。

隔离性(Isolation):
事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间相互隔离。

持久性(Durability):持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。

2、事务的提交

默认情况下,Connection 对象处于自动提交模式,这意味着它在执行每个语句后都会自动提交更改,即它的所有 SQL 语句将被执行并作为单个事务提交。
如果禁用了自动提交模式,那么它的 SQL 语句将聚集到事务中,要提交更改就必须显式调用 commit或rollback 方法,否则无法保存数据库更改。
void setAutoCommit ( boolean autoCommit) //将此连接的自动提交模式设置为给定状态。
rollback()//取消在当前事务中进行的所有更改,并释放此 Connection 对象当前持有的所有数据库锁。
commit()//使所有上一次提交/回滚后进行的更改成为持久更改,并释放此 Connection 对象当前持有的所有数据库锁。
setSavepoint()//在当前事务中创建一个未命名的保存点 (savepoint),并返回表示它的新 Savepoint 对象。

示例:简易模拟转账

import utils.JDBC_Utils;
import java.sql.*;

public class Example00 {
    public static void main(String[] args){
        Connection conn=null;
        PreparedStatement statement1=null;
        PreparedStatement statement2=null;
        PreparedStatement statement3=null;
        PreparedStatement statement4=null;
        Savepoint savepoint=null;
        try {
            conn = JDBC_Utils.getConnection();//获取Connection对象方法,篇幅有限,不作展示
            conn.setAutoCommit(false);//将conn 自动提交设置为手动提交
            //模拟第一次转账
            String sql1 = "update bank set money=money-1000 where username='zhangsan'";
            statement1 = conn.prepareStatement(sql1);
            statement1.executeUpdate();
            //System.out.println(1/0);
            String sql2="update bank set money=money+1000 where username='lisi'";
            statement2 = conn.prepareStatement(sql2);
            statement2.executeUpdate();
            //将以上两个操作看做一个事务,只有这两操作都完成才会提交保存,否则回滚至初始状态
            savepoint = conn.setSavepoint();//创建一个保存点,可以指定回滚至此状态

            //模拟第二次转账,假设在此过程出现异常
            String sql3 = "update bank set money=money-1000 where username='zhangsan'";
            statement3 = conn.prepareStatement(sql3);
            statement3.executeUpdate();
            System.out.println(1/0);//模拟转账过程中的异常
            String sql4="update bank set money=money+1000 where username='lisi'";
            statement4 = conn.prepareStatement(sql4);
            statement4.executeUpdate();
            //将以上两个操作看做第二个事务
        } catch (Exception e) {
            try {
                if (savepoint != null) {
                    //如果savepoint不为null且出现异常,说明第一次转账成功,第二次失败,此时只需回滚至指定保存点即可
                    conn.rollback(savepoint);
                }else {
                    //如果savepoint为null,说明第一次转账异常,则回滚至初始状态,即第一次之前
                    conn.rollback();
                }
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        } finally {
            try {
                conn.commit();//手动提交事务,并释放Conn 对象当前持有的所有数据库锁
                conn.close();
                if (statement1 != null) {
                    statement1.close();
                }
                if (statement2 != null) {
                    statement2.close();
                }
                if (statement3 != null) {
                    statement3.close();
                }
                if (statement4 != null) {
                    statement4.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

3、事务的隔离级别

1、Read uncommitted 读未提交

当隔离级别设置为Read uncommitted时,就可能出现脏读。

2、Read committed 读提交 ==>(Oracle默认级别)

当隔离级别设置为Read committed时,避免了脏读,但是可能会造成不可重复读。

3、Repeatable read 重复读 ==>(MySQL默认级别)

当隔离级别设置为Repeatable read时,可避免脏读、不可重复读的发生,但可能会出现幻读(错误读取)。为此级别。

4、Serializable 串行化

最高级别,可以避免所有问题,但效率很低,一般不使用

四种隔离级别的效率:

​ read uncommitted>read committed>repeatable read>serializable

四种隔离级别的安全性:

​ read uncommitted<read committed<repeatable read<serializable

设置查看事务的隔离级别

将数据库的隔离级别设置成 读未提交
	set session transaction isolation level read uncommitted;
查看数据库的隔离级别
	select @@tx_isolation;
java中控制隔离级别: Connection
	void setTransactionIsolation(int level) //level是常量
发布了55 篇原创文章 · 获赞 23 · 访问量 4338

猜你喜欢

转载自blog.csdn.net/y_Engineer/article/details/100189908