数据库(三)——事物、视图、DCL(用户权限操作)、JDBC(操作数据库、API、方法介绍、ResultSet结果集、PreparedStatement以及相关案例)、工具类的封装(练习)

8 篇文章 1 订阅
本文探讨了如何通过事务机制确保银行转账的原子性,避免数据库故障导致的资金问题。同时,介绍了视图在数据保护和查询优化中的作用,包括创建、修改和删除视图的语法。此外,涵盖了数据库权限管理和JDBC技术的基础应用,如用户管理、JDBC连接及PreparedStatement的使用,提升代码安全性和效率。
摘要由CSDN通过智能技术生成

数据库(三)

一、事务

1、例子引入

例子:

现在有这么个情况:
在这里插入图片描述

有两个人需要转账,一方转给另一方,按照之前的操作是,甲的账户扣500,乙的账户再加500。问题是如果甲发起了转账操作,钱扣了,这个时候数据库宕机了,再启动数据库,就会发现甲的钱扣了,但是这钱并没有到乙这边来。

所以一个解决方案就是,这两个sql语句为一组,同时执行,要么大家都成功,要么大家都失败。这种执行方式j就是通过事务来管理。

2、事务的概念:

在这里插入图片描述

3、事务执行的指令

在这里插入图片描述

4、事务实现

素材:
在这里插入图片描述

通过事务实现转账功能:

#查询状态
SELECT @@autocommit
#设置为手动提交
SET autocommit=0;
#开启事务
START TRANSACTION;
#实现转账
UPDATE account SET amoney=500 WHERE aname ="华子";
#出错了;fffff
UPDATE account SET amoney=2500 WHERE aname ="刘健";
#回滚
ROLLBACK;
#提交事务
COMMIT;

注意:数据库默认自动提交,我们需要自己设置手动提交;想要使用事务,需要先开启事务;接着就是写 sql 语句;然后有个回滚功能,在提交事务之前,无论执行了多少操作,都可以通过回滚功能返回到未执行操作之前的状态;当操作确定无误后,使用提交事务功能,确认最终状态。切记,提交事务之后就不能再回滚。

5、事务执行的原理

在这里插入图片描述

6、事务的特点(ACID)

()

二、视图

1、视图的概念

在这里插入图片描述

2、视图的作用

在这里插入图片描述
说人话就是:
1、查询出来的表,是一个虚拟表,即便这个虚拟表被更改了,也不会动到数据库里面的那份原表,是对数据库里面的表的一种保护。

2、有些数据经常会被查看,比如首页的数据,或者搜索出来的第一页的数据,如果经常加载这些数据,会加大数据库的负担,可以通过视图加载这些数据形成一个虚拟表,这样每次查看只会看到这张虚拟表,而不用每次都搜索数据库的数据,减少了数据库的负担。

3、创建视图的语法

1.语法:
	create view 视图的名称 as 查询语句
	
2.例子:
	CREATE VIEW v_s AS SELECT ename,eage FROM emp

4、查询视图

1.语法:
	select * from 视图名称
	 
2.例子:
	SELECT * FROM v_s

5、修改视图

1.语法:
	alter view 视图名称 as 查询语句
	 
2.例子:
	ALTER VIEW v_s AS SELECT ename FROM emp

6、删除视图

1.语法: 
	drop view 视图的名称
	
2.例子
	DROP VIEW v_s

三、DCL(对用户以及权限的操作)

1、查询用户

查询数据库的用户,需要先切入到某数据库中,然后再输入查询语句:
在这里插入图片描述

在这里插入图片描述

2、创建用户

1.语法:
	create user '用户名'@'主机名' IDENTIFIED by '密码'
	
2.例子:创建一个huazi用户
	CREATE USER 'huazi'@'localhost' IDENTIFIED BY '123'

3、删除用户

1.语法:
	 drop user '用户名'@'主机名'
	 
2.例子:
	 DROP USER 'huazi'@'localhost'

4、修改用户密码

1.语法: 
	UPDATE USER SET PASSWORD =PASSWORD('新密码') WHERE USER="用户名"
	
2.例子:
	UPDATE USER SET PASSWORD =PASSWORD('abc') WHERE USER="liujian"
	
3.说明:
	msyql中的密码使用的是 md5 加密。

5、查询权限

1.语法:
	show grants for '用户名'@'主机名'
	
2.例子:
	SHOW GRANTS FOR 'root'@'%'

6、设置权限

1.语法:
	GRANT 权限的类型 on 库名.表名 to ‘用户名’@ '主机名'
	
2.例子:
	GRANT SELECT DELETE UPDATE INSERT ON *.* TO 'hua'@'localhost'
		# 权限的类型可以通过 ALL 代表全部权限

7、撤销权限

1.语法:
	revoke 权限的类型 on 库名.表名 from ‘用户名’@ '主机名'
2.说明:
	需要重启服务之后才能生效

四、JDBC

1、JDBC的引入

之前都是通过客户端连接数据库:
在这里插入图片描述

在实际的开发中,更多的是通过代码来操作数据库

2、jdbc概念以及导入和基础搭建

a、jdbc是什么?

在这里插入图片描述

b、jdbc连接的原理

在这里插入图片描述

c、驱动包

如果要使用 jdbc ,首先要准备跟数据库版本相匹配的驱动包。

d、基础搭建

在这里插入图片描述

创建 lib 文件夹:
在这里插入图片描述

粘贴驱动包进来:
在这里插入图片描述

然后右键,添加项目依赖:
在这里插入图片描述

就完成了基础的搭建

3、jdbc操作数据库

a、操作步骤

在这里插入图片描述

b、开始代码输入

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

接着就是通过字符串保存 sql 语句,然后执行这个 sql 语句(这里的是增 / 删 / 改):

在这里插入图片描述

会返回一个 int 值,如果修改成功则是返回 1,否则返回 0。

最后自然是关闭资源:
在这里插入图片描述

那么完整代码:

public static void main(String[] args) {
        try {
            // 通过反射加载驱动类
            Class.forName("com.mysql.jdbc.Driver");

            // 获取链接对象:url地址,数据库用户名和密码
            Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?characterEncoding=utf-8", "root", "root");

            // 得到 sql 执行的对象
            Statement sta = con.createStatement();

            // 存放 sql 语句
            String sql = "insert into account values(6,'测试',2222) ";

            // 执行 sql 语句,并获得结果集
            int num = sta.executeUpdate(sql);

            System.out.println(num);    //  输出这个结果集内容
            
            // 关闭资源
            sta.close();
            con.close();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

4、jdbc 的 API 介绍

在这里插入图片描述

5、方法的介绍

在这里插入图片描述

6、ResultSet 结果集

在这里插入图片描述

注意:上面方法中 ,getString 并不是唯一,还有其他很多方法,比如,getDouble 方法等等,这个方法主要是取数据库中的列名(字段名)。

next()方法的使用类似于迭代器的next()

使用方式:
在这里插入图片描述

上图中,一般都不这么取,一般是根据列名来取:
在这里插入图片描述

7、小案例——登录功能的实现

public static void main(String[] args) throws SQLException {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入用户名");
        String username = sc.next();
        System.out.println("请输入密码");
        int pwd = sc.nextInt();
        System.out.println(checkLogin(username, pwd));
    }

    public static boolean checkLogin(String username,int pwd) throws SQLException {
        Connection con = null;
        Statement sta = null;
        ResultSet rs = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
//        获取链接对象:url地址,数据库用户名和密码
            con = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?characterEncoding=utf-8", "root", "root");
            sta = con.createStatement();

            // 这里的字符串拼接要注意,数据库中的对应字段都有单引号 ' ,所以这块拼接会复杂一点。
            String sql = "select * from admin where username='"+username+"'"+"and pwd='"+pwd+"'";
            System.out.println(sql);
            rs = sta.executeQuery(sql);     //  向数据库发送查询指令,并接收返回的结果

            if (rs.next()){     //  迭代。如果查找到了数据,则能执行 next 语句,意味着用户名跟密码都输入正确
                return true;
            }
            return false;       //  没找到数据,返回失败

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        finally {       //  关闭资源
            if (rs!=null){
                rs.close();
            }
            if (sta!=null){
                sta.close();
            }
            if (con!=null){
                con.close();
            }
        }
        return false;       
    }

8、PreparedStatement

a、登录功能中的 sql 代码缺陷——sql语句注入问题

在上面得登录功能代码中,sql 语句有问题,可以看到这个执行语句最终会判断是否返回 true。如果外部某人通过某手段,向这个sql语句中再加入一些语句,比如 or 1=1,因为 or 语句有一个为真则为真,且 1=1,这个语句恒为真,这时候就会出现重大问题:
在这里插入图片描述

b、防止 sql 注入

在这里插入图片描述

c、PreparedStatement 特点和方法

在这里插入图片描述

PreparedStatement 不会直接扔进数据库,而是先检查语句是否正确,没问题才放到数据库,这种方式效率更高。

在这里插入图片描述

d、PreparedStatement 的使用

原先代码中接收用户输入的方式是这样:
在这里插入图片描述
通过 PreparedStatement 对象的使用,则不用这种方法;而是需要填数据的地方先通过占位符代替这个位置,然后判断是否合理合法,如果合理合法,则继续执行,再通过 setXXX方法去填充这个值,然后自后再提交语句。

e、通过 PreparedStatement 优化登录功能

所以通过这种方式修改的登录代码为:

public static void main(String[] args) throws SQLException {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入用户名");
        String username = sc.next();
        System.out.println("请输入密码");
        int pwd = sc.nextInt();
        System.out.println(checkLogin(username, pwd));
    }

    public static boolean checkLogin(String username,int pwd) throws SQLException {
        Connection con = null;
        Statement sta = null;
        ResultSet rs = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
//        获取链接对象:url地址,数据库用户名和密码
            con = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?characterEncoding=utf-8", "root", "root");
//            sta = con.createStatement();

            // 这里的字符串拼接要注意,数据库中的对应字段都有单引号 ' ,所以这块拼接会复杂一点。
//            String sql = "select * from admin where username='"+username+"'"+"and pwd='"+pwd+"'";
//            rs = sta.executeQuery(sql);     //  向数据库发送查询指令,并接收返回的结果

            String sql = "select * from admin where username=? and pwd=? ";     //  占位符:?
            PreparedStatement ps = con.prepareStatement(sql);   //  判断该sql语句是否有问题,然后返回相应的对象

            ps.setString(1,username);   //  把数据放到第一个占位符
            ps.setInt(2,pwd);   //  把数据放到第二个占位符

            System.out.println(sql);      // 看看 sql 语句

            rs = ps.executeQuery();     //  向数据库发送查询指令,并接收返回的结果

            if (rs.next()){     //  迭代。如果查找到了数据,则能执行 next 语句,意味着用户名跟密码都输入正确
                return true;
            }
            return false;       //  没找到数据,返回失败

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        finally {       //  关闭资源
            if (rs!=null){
                rs.close();
            }
            if (sta!=null){
                sta.close();
            }
            if (con!=null){
                con.close();
            }
        }
        return false;
    }

9、工具类的封装(练习)

可以发现,上面的代码太多重复了,每次需要用的时候都一大串代码。可以把他们封装起来,只需要输入 sql 语句,再输入相应的数据,即可完成想要的功能。

工具类:

public class BaseDao {
    // 把常用的不变的设置为 静态常量
    public static final String url = "jdbc:mysql:///test";      //  链接本机
    public static final String driver = "com.mysql.jdbc.Driver";    //  驱动路径
    public static final String username = "root";       //  数据库用户名
    public static final String password = "root";       //  数据库密码


    public static Connection getCon(){		//	链接数据库
        Connection con = null;
        try {
            Class.forName(driver);  //  加载驱动

            con = DriverManager.getConnection(url,username,password);   //链接数据库

            return con;     //  返回链接的对象
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static int update(String sql,Object obj[]){      //  增删改功能。executeUpdate方法会返回 int 类型数值,这里返回这个数值判断是否成功
        int num = 0;
        Connection con = null;
        PreparedStatement ps = null;
        try {
            con = getCon();     //  取得链接对象
            ps = con.prepareStatement(sql);     //  用 prepareStatement 校验 sql 语句
            if (obj != null && obj.length>0){   //  数组非空验证
                for (int i = 0; i < obj.length; i++) {
                    ps.setObject(i+1,obj[i]);   //  将对应的数据一一跟占位符对应。因为下标从 1 开始,所以这里要 +1。
                }
            }
            num = ps.executeUpdate();   //  执行 sql 语句,并获得结果集
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            closeAll(con,ps,null);
        }
        return num;     //  返回结果集
    }

    public static ResultSet getResultSet(String sql,Object obj[]){      //  查询功能。返回 ResultSet 结果集
        Connection con = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            con = getCon();             //  取得链接对象
            ps = con.prepareStatement(sql);      //  用 prepareStatement 校验 sql 语句
            if (obj != null && obj.length>0){
                for (int i = 0; i < obj.length; i++) {
                    ps.setObject(i+1,obj[i]);
                }
            }
            rs = ps.executeQuery();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return rs;
    }

    public static void closeAll(Connection con,PreparedStatement ps,ResultSet rs){  //  关闭资源
        try {
            if (con != null){
                con.close();
            }
            if (ps != null){
                con.close();
            }
            if (con != null){
                rs.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

测试类:

public static void main(String[] args) throws SQLException {
        //  增加数据
//        String sql = "insert into admin(username,pwd)values(?,?)";
//        Object obj[] ={"admin",1234};
//        int num = BaseDao.update(sql,obj);
//        System.out.println(num);
        
        //  查询数据
        String sql = "select * from admin where username=?";
        Object obj[] ={"admin"};
        ResultSet rs = BaseDao.getResultSet(sql,obj);
        while (rs.next()){
            System.out.println(rs.getString("username"));
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值