JDBC【封装工具类、SQL注入问题】

day54

JDBC

封装工具类01

创建配置文件

DBConfig.properties

driverName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/qnz01?characterEncoding=utf8&serverTimezone=UTC
username=root
password=root

新建配置文件,不用写后缀名
配置文件,不用写后缀

创建工具类

  1. 将变化的配置信息搬到配置文件中
  2. 工具类提供获取连接的方法
  3. 工具类提供关闭资源的方法
package com.qf.utils;

/**
 * 数据库工具类
 */
public class DBUtil {

    private static String url;
    private static String username;
    private static String password;

    static{
        //负责加载配置文件,只加载一次
        Properties properties = new Properties();
        try {
            properties.load(DBUtil.class.getClassLoader().getResourceAsStream("DBConfig.properties"));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        String driverName = properties.getProperty("driverName");
        url = properties.getProperty("url");
        username = properties.getProperty("username");
        password = properties.getProperty("password");

        try {
            //加载驱动只加载一次
            Class.forName(driverName);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取连接对象
     */
    public static Connection getConnection() throws SQLException {
        Connection connection = DriverManager.getConnection(url,username,password);
        return connection;
    }

    /**
     * 关闭资源
     */
    public static void close(Connection connection, Statement statement, ResultSet resultSet){
        if(resultSet != null){
            try {
                resultSet.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
        if(statement != null){
            try {
                statement.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
        if(connection != null){
            try {
                connection.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }

}

理解

(1)新建配置文件
把导入驱动包和获取连接对象的参数url、root、password写入配置文件
(2)将获取连接对象方法封装到工具类DBUtil
01:加载配置文件
new 一个配置文件对象,配置文件对象调用加载方法加载进来【报异常先用try-catch】
之后就可以配置文件对象调用方法拿到配置信息【注意要与配置文件的名字相同,否则找不到就是null】
02:导入驱动包

03:在获取连接对象方法里,调用方法获取连接对象,返回连接对象

04:对于关闭资源,不用每次都写,直接在工具类写个关闭资源方法传入可能关的资源,里面非空判断哪些要关就行了

注意:

通常会把加载配置文件和导入驱动包放在静态代码块里,提高效率,不用获取一次连接加载一次,加载一次就行
对于重复使用的属性先静态定义【注意静态代码块只能初始化静态的属性】

基本操作报错try-catch【例如类这里的forName类未找到异常就要么资源文件没导入,要么配置文件对应的名称没写对】,对于需要对应不同业务处理异常就需要抛异常,在外面处理

现在对于先前的增删改查就不用那么麻烦,每个都写导入驱动包和获取连接对象的一大坨代码,当然这里就涉及封装工具类
直接用工具类调用获取连接对象的方法,关闭资源方法

测试类

对day53的增删改查进行进一步优化

package com.qf.jdbc01;

import com.qf.utils.DBUtil;
import org.junit.Test;

import java.sql.*;

public class Test01 {
    /**
     * 知识点:封装DBUtil - v1.0
     */

    //添加数据
    @Test
    public void test01() throws SQLException {
        //获取连接对象
        Connection connection = DBUtil.getConnection();

        //获取发送指令对象
        Statement statement = connection.createStatement();

        //发送SQL指令
        String sql = "INSERT INTO student(name,sex,age,salary,course) VALUES('柳如烟','女',28,6000,'HTML');";
        int num = statement.executeUpdate(sql);
        System.out.println("对于" + num + "行造成了影响");

        //关闭资源
        DBUtil.close(connection,statement,null);
    }


    //删除数据
    @Test
    public void test02() throws SQLException {
        //获取连接对象
        Connection connection = DBUtil.getConnection();

        //获取发送指令对象
        Statement statement = connection.createStatement();

        //发送SQL指令
        String sql = "DELETE FROM student WHERE id>10;";
        int num = statement.executeUpdate(sql);
        System.out.println("对于" + num + "行造成了影响");

        //关闭资源
        DBUtil.close(connection,statement,null);
    }

    //修改数据
    @Test
    public void test03() throws SQLException {

        //获取连接对象
        Connection connection = DBUtil.getConnection();

        //获取发送指令对象
        Statement statement = connection.createStatement();

        //发送SQL指令
        String sql = "UPDATE student SET age=21,salary=50000 WHERE id=3;";
        int num = statement.executeUpdate(sql);
        System.out.println("对于" + num + "行造成了影响");

        //关闭资源
        DBUtil.close(connection,statement,null);
    }

    //查询数据
    @Test
    public void test04() throws SQLException {

        //获取连接对象
        Connection connection = DBUtil.getConnection();

        //获取发送指令对象
        Statement statement = connection.createStatement();

        //发送SQL指令,并获取结果集对象
        String sql = "select * from student";
        ResultSet resultSet = statement.executeQuery(sql);

        //遍历结果集
        while(resultSet.next()){//判断是否有可迭代的数据行

            int id = resultSet.getInt("id");
            String name = resultSet.getString("name");
            String sex = resultSet.getString("sex");
            int age = resultSet.getInt("age");
            float salary = resultSet.getFloat("salary");
            String course = resultSet.getString("course");

            System.out.println(id + " -- " + name + " -- " + sex + " -- " + age + " -- " + salary + " -- " + course);
        }

        //关闭资源
        DBUtil.close(connection,statement,resultSet);
    }


}

SQL注入问题

理解

ps:select * from student where name=‘’ or 1=1 #’ and passwd=‘111111’;

当输入用户名和密码
’ or 1=1 # '【对于它分不清sql命令和数据,就会or 1=1判断为true,#后面理解为注释,就直接登录进去了】
user表

sql注入问题

package com.qf.jdbc02;

import com.qf.utils.DBUtil;

public class Test01 {
    /**
     * 知识点:SQL注入问题
     *
     * 出现原因:数据库分不清哪些是sql命令,哪些是数据
     *
     * 需求:模拟登录功能
     */
    public static void main(String[] args) throws SQLException {

        Connection connection = DBUtil.getConnection();

        Statement statement = connection.createStatement();

        Scanner scan = new Scanner(System.in);
        System.out.println("请输入账号:");
        String usernameVal = scan.nextLine();
        System.out.println("请输入密码:");
        String passwordVal = scan.nextLine();

        //select * from user where username='' or 1=1 #' and password='12312345'
        String sql = "select * from user where username='"+usernameVal+"' and password='"+passwordVal+"'";
        ResultSet resultSet = statement.executeQuery(sql);

        if(resultSet.next()){

            String username = resultSet.getString("username");
            String password = resultSet.getString("password");
            String nikeName = resultSet.getString("nike_name");

            System.out.println("登录成功");
            System.out.println(username);
            System.out.println(password);
            System.out.println(nikeName);
        }else{
            System.out.println("登录失败");
        }

        DBUtil.close(connection,statement,resultSet);
    }
}

解决

采用预编译对象来编程:告诉他,使用prepareStatement

sql命令中用?表示数据
注意:prepareStatement继承于statement
现在用prepareStatement拿到的sql是没有数据的,得到它的对象
再将输入的数据设置给这个对象,注意方法SetString的两个参数(下标,值)【注意下标从1开始】
sql注入问题解决

  1. 安全性,避免了SQL注入
  2. 性能,预编译,语句-编译-执行
package com.qf.jdbc02;

import com.qf.utils.DBUtil;

public class Test02 {
    /**
     * 知识点:SQL注入问题
     *
     * 出现原因:数据库分不清哪些是sql命令,哪些是数据
     * 解决方案:告诉数据库哪些是sql命令,哪些是数据
     *
     * 需求:模拟登录功能
     */
    public static void main(String[] args) throws SQLException {

        Connection connection = DBUtil.getConnection();

        String sql = "select * from user where username=? and password=?";
        PreparedStatement statement = connection.prepareStatement(sql);

        //输入数据后,将数据设置给statement对象
        Scanner scan = new Scanner(System.in);
        System.out.println("请输入账号:");
        String usernameVal = scan.nextLine();
        System.out.println("请输入密码:");
        String passwordVal = scan.nextLine();

        statement.setString(1,usernameVal);
        statement.setString(2,passwordVal);

        ResultSet resultSet = statement.executeQuery();

        if(resultSet.next()){

            String username = resultSet.getString("username");
            String password = resultSet.getString("password");
            String nikeName = resultSet.getString("nike_name");

            System.out.println("登录成功");
            System.out.println(username);
            System.out.println(password);
            System.out.println(nikeName);
        }else{
            System.out.println("登录失败");
        }

        DBUtil.close(connection,statement,resultSet);
    }
}

封装工具类02

更新数据(添加、删除、修改):

01方法传的参数(一个sql命令,另一个数据不确定用可变参数)
02由于不知道下标和值用setObject设置给statement,但有多个参数需要遍历处理就写个方法来专门处理【处理statement对象参数数据的处理器,注意setObject下标从1开始】直接调用处理
03添加操作返回影响行数
04而考虑到关闭资源,处理异常就不会关闭资源,对代码进行try-finally,考虑作用域把定义对象放在之外

添加–主键回填:

就是添加数据后,不再是返回影响的行数,加一个查询返回主键
主键回填理解:
主键回填

查询:

以前查询就打印出来结果,现在查询的数据封装成对象,不同表封装不同的对象
01学生类:有参无参自动生成时,window+alt+回车快速生成,生成时涉及属性选择ctrl+1全选
02方法返回值设置成list集合,类型用泛型;传的参数(哪个类的对象clazz【反射创建】,一个sql,一个可变参数)
03对于拿到的结果集,需要遍历, 一个数据创建一个对象
先泛型创建对象【无数据】,获取数据字段名才可以利用反射去设置对象的属性
怎么获取:先用结果集对象调用方法获取表数据对象,再表数据对象调用方法获取字段个数,再在遍历中通过字段个数循环获取字段名,字段值

反射获取类的属性对象和设置对象的属性【对于反射设置属性回顾以前反射讲到的方法】

04然后将封装的对象添加到对象集合再返回

05而考虑到关闭资源,处理异常就不会关闭资源,对代码进行try-finally,考虑作用域把定义对象放在之外

package com.qf.utils;

/**
 * 数据库工具类
 */
public class DBUtil {

    private static String url;
    private static String username;
    private static String password;

    static{
        //负责加载配置文件,只加载一次
        Properties properties = new Properties();
        try {
            properties.load(DBUtil.class.getClassLoader().getResourceAsStream("DBConfig.properties"));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        String driverName = properties.getProperty("driverName");
        url = properties.getProperty("url");
        username = properties.getProperty("username");
        password = properties.getProperty("password");

        try {
            //加载驱动只加载一次
            Class.forName(driverName);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取连接对象
     */
    public static Connection getConnection() throws SQLException {
        Connection connection = DriverManager.getConnection(url,username,password);
        return connection;
    }

    /**
     * 关闭资源
     */
    public static void close(Connection connection, Statement statement, ResultSet resultSet){
        if(resultSet != null){
            try {
                resultSet.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
        if(statement != null){
            try {
                statement.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
        if(connection != null){
            try {
                connection.close();
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 更新数据(添加、删除、修改)
     */
    public static int commonUpdate(String sql,Object... params) throws SQLException {
        Connection connection = null;
        PreparedStatement statement = null;
        try {
            connection = getConnection();
            statement = connection.prepareStatement(sql);
            paramHandler(statement,params);
            int num = statement.executeUpdate();
            return num;
        }finally {
            close(connection,statement,null);
        }
    }

    /**
     * 添加数据 - 主键回填(主键是int类型可以返回)
     */
    public static int commonInsert(String sql,Object... params) throws SQLException {
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
            connection = getConnection();
            statement = connection.prepareStatement(sql,PreparedStatement.RETURN_GENERATED_KEYS);
            paramHandler(statement,params);
            statement.executeUpdate();

            resultSet = statement.getGeneratedKeys();
            int primaryKey = 0;
            if(resultSet.next()){
                primaryKey = resultSet.getInt(1);
            }
            return primaryKey;
        }finally {
            close(connection,statement,resultSet);
        }
    }

    /**
     * 查询数据
     */
    public static <T> List<T> commonQuery(Class<T> clazz,String sql, Object... params) throws SQLException, InstantiationException, IllegalAccessException {

        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;

        try {
            connection = getConnection();
            statement = connection.prepareStatement(sql);
            paramHandler(statement,params);
            resultSet = statement.executeQuery();

            //获取表数据对象
            ResultSetMetaData metaData = resultSet.getMetaData();
            //获取字段个数
            int count = metaData.getColumnCount();

            List<T> list = new ArrayList<>();

            while(resultSet.next()){

                T t = clazz.newInstance();

                //获取字段名及数据
                for (int i = 1; i <= count; i++) {
                    String fieldName = metaData.getColumnName(i);
                    Object fieldVal = resultSet.getObject(fieldName);
                    setField(t,fieldName,fieldVal);
                }
                list.add(t);
            }
            return list;
        } finally {
            DBUtil.close(connection,statement,resultSet);
        }
    }

    /**
     * 处理statement对象参数数据的处理器
     */
    private static void paramHandler(PreparedStatement statement,Object... params) throws SQLException {
        for (int i = 0; i < params.length; i++) {
            statement.setObject(i+1,params[i]);
        }
    }

    /**
     * 获取当前类及其父类的属性对象
     * @param clazz class对象
     * @param name 属性名
     * @return 属性对象
     */
    private static Field getField(Class<?> clazz,String name){

        for(Class<?> c = clazz;c != null;c = c.getSuperclass()){
            try {
                Field field = c.getDeclaredField(name);
                return field;
            } catch (NoSuchFieldException e) {
            } catch (SecurityException e) {
            }
        }
        return null;
    }

    /**
     * 设置对象中的属性
     * @param obj 对象
     * @param name 属性名
     * @param value 属性值
     */
    private static void setField(Object obj,String name,Object value){

        Field field = getField(obj.getClass(), name);
        if(field != null){
            field.setAccessible(true);
            try {
                field.set(obj, value);
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }
}

调用工具类的方法实现增删改查

package com.qf.jdbc03;

import com.qf.utils.DBUtil;

public class Test01 {
    /**
     * 知识点:封装数据库 - v2.0
     */

    //添加数据
    @Test
    public void test01() throws SQLException {
        String sql = "INSERT INTO student(name,sex,age,salary,course) VALUES(?,?,?,?,?)";
        int num = DBUtil.commonUpdate(sql, "天使萌", "女", 23, 10000, "HTML");
        System.out.println("对于" + num + "行造成了影响");
    }

    //删除数据
    @Test
    public void test02() throws SQLException {
        String sql = "delete from student where id=?";
        int num = DBUtil.commonUpdate(sql, 3);
        System.out.println("对于" + num + "行造成了影响");
    }

    //修改数据
    @Test
    public void test03() throws SQLException {
        String sql = "update student set salary=? where id=?";
        int num = DBUtil.commonUpdate(sql, 15000,1);
        System.out.println("对于" + num + "行造成了影响");
    }

    //添加数据 - 主键回填
    @Test
    public void test04() throws SQLException {
        String sql = "INSERT INTO student(name,sex,age,salary,course) VALUES(?,?,?,?,?)";
        int primaryKey = DBUtil.commonInsert(sql, "铃原爱蜜莉", "女", 23, 10000, "HTML");
        System.out.println("返回的主键是:" + primaryKey);
    }

    //查询数据
    @Test
    public void test05() throws SQLException, InstantiationException, IllegalAccessException {
        String sql = "select * from student where id<?";
        List<Student> list = DBUtil.commonQuery(Student.class, sql, 8);
        for (Student stu : list) {
            System.out.println(stu);
        }
    }
}

学生类

package com.qf.jdbc03;

public class Student {

    private int id;
    private String name;
    private String sex;
    private int age;
    private float salary;
    private String course;

    public Student() {
    }

    public Student(int id, String name, String sex, int age, float salary, String course) {
        this.id = id;
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.salary = salary;
        this.course = course;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public float getSalary() {
        return salary;
    }

    public void setSalary(float salary) {
        this.salary = salary;
    }

    public String getCourse() {
        return course;
    }

    public void setCourse(String course) {
        this.course = course;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", age=" + age +
                ", salary=" + salary +
                ", course='" + course + '\'' +
                '}';
    }
}

小结:

JDBC:封装工具类,对增删改查优化,SQL注入问题

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/766846.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

北斗/GPS模块输出的NMEA语句详解

NMEA协议采用 ASCII 码来传递 GPS 定位信息&#xff0c;我们称之为帧。 帧格式形如&#xff1a;$aaccc,ddd,ddd,…,ddd*hh(CR)(LF) 1、“$”&#xff1a;帧命令起始位 2、aaccc&#xff1a;地址域&#xff0c;前两位为识别符&#xff08;aa&#xff09;&#xff0c;后三位为…

记一次android打包,因路由规则设置不合理而导致pom文件无法访问的错误

一、错误详情 FAILURE: Build failed with an exception.* What went wrong: Could not determine the dependencies of task :mediaplayer:compileReleaseAidl. > Could not resolve all task dependencies for configuration :mediaplayer:releaseCompileClasspath.> C…

nodejs版本升级12->18

1.把老版本删除&#xff0c;没删除升级没成功。 2.在官网下载新版本。 3.在菜单中输入cmd&#xff0c;一定要用管理员身份运行&#xff0c;切记&#xff0c;不然会出现2503/2502错误。 4.安装即可。

SCI丨5分期刊,JCR一区

SCI&#xff0c;5分&#xff0c;JCR Q1&#xff0c;中科大类3小类2区 1 基于复杂网络与xxx能源汽车节能数值分析 2 基于热能损失优化的xxx与性能管理 3 基于xxxLCA技术的绿色制造工艺优化研究 4 基于xxx入侵检测技术的物联网智能制造监控系统设计 6 基于物联网技术xxx电力系…

跨境电商自养号全攻略:TEMU、Shein、速卖通测评技巧揭秘

TEMU、Shein、速卖通等跨境平台都推出了全托管模式&#xff0c;普通平台讲究排名&#xff0c;销量&#xff0c;流量量&#xff0c;转化率等等。那么全托管为什么需要做测评呢&#xff1f;因为全托管平台讲究的是一个动销率&#xff0c;有的新品上架或许很快就出单&#xff0c;而…

邮件通知提醒邮箱设置教程及API代码示例!

邮件通知的警告功能如何配置&#xff1f;详细教程与API代码示例&#xff01; 无论是业务提醒、账户活动警告&#xff0c;还是个人事务&#xff0c;邮件通知已经成为一种重要的沟通工具。AokSend将详细介绍如何设置邮件通知提醒邮箱&#xff0c;并提供相应的API代码示例&#x…

场景管理分析平台介绍

在数字化浪潮的推动下&#xff0c;数据已成为企业决策的重要依据。特别是在智能驾驶、虚拟现实和物联网等领域&#xff0c;场景数据的高效管理和利用至关重要。在智能驾驶领域面对海量的场景数据&#xff0c;如何高效处理、精准分析&#xff0c;并将其转化为有价值的决策支持&a…

[OC]萝卜圈Python手动机器人脚本

这是给机器人设置的端口&#xff0c;对照用 代码 # #作者:溥哥’ ##机器人驱动主程序 #请在main中编写您自己的机器人驱动代码 import msvcrt def main():a"none"while True:key_input msvcrt.getch()akey_inputif abw:print(a)robot_drv.set_motors(1,40,2,40,3,…

(漏洞检查项) | 任意文件包含漏洞 file-include

(漏洞检查项)|任意文件包含漏洞 file-include 漏洞场景 1.含有动态包含语句 2.有类似于文件读取的url 漏洞描述 攻击者可以利用任意文件包含漏洞&#xff0c;读取任意文件&#xff0c;对服务器造成危害。 程序开发人员为了代码的灵活性&#xff0c;常常会将包含文件的路径…

SpringBoot怎么单独关闭某个类打印出来的日志?

application.yml文件增加以下内容&#xff1a; logging:level:org.springframework.amgp.rabbit: OFF 配置logging:level是配置的什么&#xff1f; 在application.yml文件中配置logging.level是用来设置日志级别的。这是Spring Boot应用中的一个常用配置&#xff0c;它允许您…

JeecgFlow错误事件

事件定义 错误事件可以用做一个流程的开始事件或者作为一个任务或者子流程的边界事件&#xff0c;错误事件没有提供作用中间事件的功能&#xff0c;这一点和前面介绍的定时器事件和消息事件还有区别的。在错误事件中提供了错误结束事件。 BPMN错误和Java异常并没有直接关联。BP…

tiktok数据分析应用介绍和tiktok数据分析平台分享

对于创作者、商家&#xff0c;tiktok官方有提供相应的数据分析为精细化运营给予辅助支持。 tiktok官方数据分析功能 TikTok Pro Account&#xff08;专业账户&#xff09;&#xff0c;包括CA账户&#xff08;Creator Account&#xff09;和BA&#xff08;Business Account&am…

ONLYOFFICE8.1版本桌面编辑器简单测评

ONLYOFFICE官网链接&#xff1a;在线PDF查看器和转换器 | ONLYOFFICE ONLYOFFICE介绍&#xff1a;https://www.onlyoffice.com/zh/office-suite.aspx OnlyOffice 是一款免费且开源的 Office 协作办公套件&#xff0c;支持桌面端和移动端等多平台&#xff0c;由一家领先的 IT 公…

Python深度理解系列之【排序算法——冒泡排序】

读者大大们好呀&#xff01;&#xff01;!☀️☀️☀️ &#x1f440;期待大大的关注哦❗️❗️❗️ &#x1f680;欢迎收看我的主页文章➡️木道寻的主页 文章目录 &#x1f525;前言&#x1f680;冒泡排序python实现算法实现图形化算法展示 ⭐️⭐️⭐️总结 &#x1f525;前…

师傅们 ~ 2024HW一手资料

各位师傅们&#xff0c;2024HW来了&#xff01; 从2026年开始&#xff0c;随着我国对网络安全的重视&#xff0c;涉及单位不断增加&#xff0c;越来越多单位和个人都加入到HW当中。 2024HW就在眼前&#xff0c; 那么还有不了解或者还没投简历面试的朋友们&#xff0c;需要注意…

职升网:中级会计师考试难度是怎样的?

中级会计师考试确实被普遍认为是具有一定难度的考试。以下是我对其难度的分析&#xff1a; 一、知识体系的广泛性 中级会计师考试覆盖的内容十分广泛&#xff0c;包括但不限于财务管理、财务会计、成本会计、税法等。这就要求考生具备扎实的基础知识和广泛的知识面&#xff0…

咨询公司在推行TPM管理中有哪些不可替代的作用?

TPM管理作为一种先进的生产维护理念&#xff0c;正逐渐成为企业追求卓越生产性能的不二之选。在这场转型升级的浪潮中&#xff0c;咨询公司扮演着不可替代的角色&#xff0c;它们如何助力企业成功推行TPM管理&#xff0c;成为了我们今天要探讨的焦点。 一、专业引领&#xff0c…

在Ubuntu 22.04 LTS 上安装 MySQL两种方式:在线方式和离线方式

Ubuntu安装MySQL 介绍&#xff1a; Ubuntu 是一款基于Linux操作系统的免费开源发行版&#xff0c;广受欢迎。它以稳定性、安全性和用户友好性而闻名&#xff0c;适用于桌面和服务器环境。Ubuntu提供了大量的软件包和应用程序&#xff0c;拥有庞大的社区支持和活跃的开发者社区…

五、【源码】资源加载器

源码地址&#xff1a;https://github.com/spring-projects/spring-framework 仓库地址&#xff1a;https://gitcode.net/qq_42665745/spring/-/tree/05-resource-loader 资源加载器 流程&#xff1a; 1.初始化BeanFactory 2.创建XmlBeanDefinitionReader用于从 XML 文件中读…

LoadRunner初学篇

我也是初学&#xff0c;写一篇文章记录下过程及心得&#xff0c;有不同建议的大佬可评价&#xff0c;感谢提携 这是什么 LoadRunner&#xff0c;是一种预测系统行为和性能的负载测试工具。通过模拟上千万用户实施并发负载及实时性能监测的方式来确认和查找问题&#xff0c;Loa…