JDBC以及事务

news2024/11/16 23:33:20

内容概要

  • 了解JDBC是什么,以及定义,它有什么好处
  • 掌握使用JDBC访问数据库
  • 掌握使用JDBC进行增删改查
  • 掌握数据库注入问题,以及怎么解决数据库注入问题
  • 掌握事务的使用,以及为什么需要事务。
  • 理解事务的四大特性:ACID。能用自己的话讲出来
  • 事务的隔离级别

1 介绍

1.1 数据库的访问过程

在这里插入图片描述

1.2 JDBC是什么

JDBC(Java Database Connectivity)是Java程序与数据库进行交互的一种标准接口。它定义了一组Java API,使得Java程序可以通过这些API来连接和操作各种关系型数据库(如MySQL、Oracle、SQL Server等),执行SQL语句并处理查询结果。JDBC提供了一种跨平台、可移植的方式来访问数据库,使得Java程序可以与不同的数据库进行通信而无需改变代码。JDBC的主要优点包括:可移植性、可靠性、安全性和易于使用。

JDBC具体指的就是 Java的一套标准的连接数据库的接口。

在这里插入图片描述

那么标准的接口具体在哪儿呢?指的是哪些接口呢?(rt.jar内部的)

  • java.sql
  • javax.sql

2 JDBC怎么用

2.1 第一个JDBC程序

  • 新建项目

  • 导包

    导包是指导入其他的人或者是组织写的代码。

    如何导包呢?

    1. 下载包

      下载仓库地址

      MySQL驱动包下载地址

    2. 把包复制到项目中,并且加载进来

      .jar: 这个格式是一种压缩格式,和 .zip,.rar是类似的,这种类型的文件,可以被Java识别并且运行。

      .jar文件中都是一些 .class文件,是可以直接运行的

      接下来,需要把对应的jar包添加到library里面去。对着jar包右键

      在这里插入图片描述

  • 编写应用程序

      	    // 1. 加载驱动  {@Link java.sql.Driver   impl : com.mysql.jdbc.Driver}
            DriverManager.registerDriver(new Driver());
    
    
      	    // String url = "协议 + ip + 端口 + 路径 + 参数";
            String url = "jdbc:mysql://localhost:3306/40th?useSSL=false&characterEncoding=utf8";
            String username = "root";
            String password = "123456";
    
            // 2. 发送用户名和密码,建立连接
            // 返回的当前是一个Connection接口,但是实际上在运行的时候,返回是Connection接口的实现类的实例
            Connection connection = DriverManager.getConnection(url, username, password);
    
            // 3. 获取statement对象
            Statement statement = connection.createStatement();
    
            // 4. 发送SQL语句
            int affectedRows = statement.executeUpdate("insert into stu values (4,'云飞兄',20,'358班')");
    
            // 5. 解析结果集
            System.out.println("affectedRows:" + affectedRows);
    
            // 6. 断开连接
            statement.close();
            connection.close();
    

MySQL版本5.7:选驱动可以选 5.1.47 48 49都可以; 8的也可以。

MySQL8的版本:只能选8,不要选5的版本。

2.2 使用JDBC进行增删改查

2.2.1 增

// 3. 发送SQL语句
// 返回值是个int,代表影响的行数。 新增的行数
int affectedRows = statement.executeUpdate("insert into stu values (1, 'zhangsan', 22)");

2.2.2 删

增、删、改都是一样的,都是使用 statement.executeUpdate(String sql) 来执行SQL语句,返回的结果也是一样的,都是影响的行数。

// 3. 发送SQL语句
// 返回值是个int,代表影响的行数。 删除的行数
int affectedRows = statement.executeUpdate("delete from stu where id = 3");

2.2.3 改

int affectedRows = statement.executeUpdate("update stu set name = '孔二愣子',class='五班' where id = 4"

2.2.4 查

// 3. 发送SQL语句
// resultSet指结果集对象,具体指代查询返回的临时表对象
ResultSet resultSet = statement.executeQuery("select * from stu");


//  解析结果集
while (resultSet.next()) {

	int id = resultSet.getInt("id");
	String name = resultSet.getString("name");
 	int age = resultSet.getInt("age");
	String className = resultSet.getString("class");

	System.out.println("id:" + id);
	System.out.println("name:" + name);
	System.out.println("age:" + age);
	System.out.println("className:" + className);

}

2.3 API

查看类中所有方法的快捷键:ctrl + F12

2.3.1 DriverManager

驱动管理器。可以帮助我们管理驱动,获取连接

// 注册驱动
DriverManager.registerDriver(new Driver);

// 获取连接
// 在代码实际运行的时候,一定不可能光是一个接口,一定是一个实现类。(是MySQL提供的一个实现类。)
// 获取到的连接对象实际上是 JDBC4Connection 对象
Connection conn = DriverManager.getConnection(String url,String username,String password);

2.3.2 Connection

指代连接对象。在JDBC中是一个接口,在我们使用JDBC的时候,实际上实现类是 com.mysql.jdbc.JDBC4Connection 对象。

// 获取statement
// 通过statement对象来执行SQL
Statement stat = connection.createStatement();

// 关闭连接
connection.close();

// 事务相关的API
connection.commit();
connection.rollback();
connection.setAutoCommit(false);

2.3.3 Statement

statement对象其实就是用来去执行SQL语句,并且返回这个SQL语句产生的结果集。实际上我们在使用的时候,其实是用的Statement接口的实现类 com.mysql.jdbc.StatementImpl

// 执行增删改的方法。新增数据的SQL,删除数据的SQL,修改数据的SQL
int affectedRows = statement.executeUpdate(String updateSql);

// 执行查询的方法
ResultSet rs  = statement.executeQuery(String querySql);

// 拿到一个ResultSet,怎么从里面获取数据。 当一个迭代器的方法使用。

// 关闭
statement.close();

// 执行sql语句
Boolean ret = statement.execute(String sql);

// 如果 ret == true,那么说明执行的是查询语句  statement.getResultSet();
// 如果 ret == false,那么说明执行的是增删改语句 statement.getUpdateCount();
// 获取影响的行数: statement.getUpdateCount();
// 获取返回的结果集:statement.getResultSet();

2.3.4 ResultSet

这个对象表示查询的结果集。

在这里插入图片描述

在查询的结果集中,有一个游标,游标可以移动,移动的时候会扫描一些行,那么对于这些扫描到的行,我们就可以获取对应的列的值。

// 移动游标方法

// 向下移动
Boolean ret = resultSet.next();

// 向上移动
Boolean ret = resultSet.previous();

// 定位到第一行之前
resultSet.beforeFirst();

// 定义到最后一行之后
resultSet.afterLast();


// 获取值的方法
resultSet.getInt(String columnName);
resultSet.getString(String columnName);
resultSet.getDate(String columnName);

在这里插入图片描述

2.4 JDBC的优化

  • 提取工具类
  • 连接配置放入到配置文件中
  • 注册驱动利用反射,解耦
  • 关闭资源(提取公共方法)
public class JDBCUtils {

    static String url;
    static String username;
    static String password;
    static String driver;

    static {

        Properties properties = new Properties();
        try {
            properties.load(new FileInputStream("jdbc.properties"));
        } catch (IOException e) {
            e.printStackTrace();
        }


        url = properties.getProperty("url");
        username = properties.getProperty("username");
        password = properties.getProperty("password");
        driver = properties.getProperty("driverClassName");
    }


    // 获取连接
    public static Connection getConnection(){

        Connection connection = null;
        try {
            Class.forName(driver);
            connection = DriverManager.getConnection(url, username, password);
        }catch (Exception ex) {
            ex.printStackTrace();
        }

        return connection;
    }



    // 关闭资源
    public static void  close(Connection connection, Statement statement, ResultSet resultSet){

        try {

            if (resultSet != null) resultSet.close();
            if (statement!= null) statement.close();
            if (connection != null) connection.close();

        }catch (Exception ex) {
            ex.printStackTrace();
        }


    }
}

2.5 数据库注入问题

数据库注入是一种常见的网络安全漏洞,攻击者利用这种漏洞向网站或应用程序的数据库中插入恶意代码,从而获取敏感信息、执行非法操作等。例如:

假设有一个登录页面,用户需要输入用户名和密码才能登录。该页面的后端代码使用SQL查询语句来验证用户的身份,例如:

SELECT * FROM user WHERE username = 'input_username' AND password = 'input_password';

其中,'input_username’和’input_password’是用户在登录页面上输入的值。如果用户输入的值与数据库中的值匹配,则允许用户登录,否则拒绝登录。

然而,攻击者可以在输入框中输入一些恶意代码,例如:

input_username: root
input_password: xxx' or ' 1=1

这个输入会更改SQL查询语句,变成:

-- 下面这条SQL的含义,是这样的   1=1是恒等的,所以where条件相当于会没有任何条件
-- SELECT * FROM users WHERE (username = 'admin' AND password = 'xxx') or '1=1';
SELECT * FROM users WHERE username = 'admin' AND password = 'xxx' or ' 1=1';

SELECT * FROM users WHERE username = 'admin' AND password = 'xxx' ;drop database test1';

这个SQL语句的含义是“从users表中选择任何一个行,其中用户名为空或1等于1,并且密码为空”。由于1等于1始终为真,因此这个SQL语句将返回users表中的所有行,从而绕过了身份验证,攻击者可以以任何用户的身份登录系统。这就是一个典型的SQL注入攻击。

为了避免SQL注入攻击,必须对用户输入的值进行过滤和转义,或使用预处理语句等安全措施来防范这种攻击。

主要的原因是字符串拼接,把用户的一些输入当做了关键字。

 public static void main(String[] args) throws SQLException {

//        Boolean ret = login("天明", "upan");

        // select * from user where name = 'xxx' and password = 'xxx';
        // select * from user where name = 'xxx' and password = 'xxx' or '1=1';

        Boolean ret = login("xxx", "xxx' or '1=1");


        if (ret) {
            System.out.println("登录成功!");

        }else {
            System.out.println("登录失败");
        }
    }

    // 登录方法
    public static Boolean login(String username,String password) throws SQLException {

        // 传入用户名和密码。根据用户名和密码查询用户,假如查询到了,说明登录成功;如果没查到,登录失败
        Connection connection = JDBCUtils.getConnection();

        Statement statement = connection.createStatement();

        String sql = "select * from user where name = '"+username+"' and password = '"+password+"'";

        System.out.println(sql);

        ResultSet resultSet = statement.executeQuery(sql);

        if (resultSet.next()) {

            return true;
        }else {
            return  false;
        }

    }

数据库注入问题产生的原因:因为SQL语句是通过字符串拼接的,这个时候用户可能输入一些字符,这些字符中包含有SQL语句中的关键字,那么通过字符串拼接SQL语句之后,可能会改变SQL语句的格式,进而引发安全性的问题。

根本的原因:MySQL把用户输入的参数当做关键字来解析了

如何解决数据库注入问题呢?

  • PrepareStatement(预编译的Statement)
// 登录方法2
    public static Boolean login2(String username,String password) throws SQLException {

        // 1. 获取连接
        Connection connection = JDBCUtils.getConnection();

        // 2. 获取PreparedStatement
        // 这一步,在创建PreparedStatement的时候,PreparedStatement会把当前这个没有参数的SQL语句,发送给MySQL服务器,执行预编译
        // 预编译:其实就是去解析这个SQL语句中的关键字,变成MySQL可以执行的命令
        // 在预编译之后,后续输入的字符串,就只会被MySQL当成纯文本来解析
        PreparedStatement preparedStatement = connection.prepareStatement("select * from user where name = ? and password = ?");


        // 3. 设置参数
        // 序号从 1 开始
        preparedStatement.setString(1,username);
        preparedStatement.setString(2,password);


        // 4. 传递参数,执行SQL语句
        ResultSet resultSet = preparedStatement.executeQuery();

        if (resultSet.next()) {
            return true;
        }else {
            return false;
        }
    }

总结:

  1. 在安全性方法,PreparedStatement比Statement要好很多,没有数据库的注入问题

  2. 在效率方面,执行单次SQL语句的时候,Statement的效率比PreparedStatement要好一些

    因为Statement在执行一条SQL语句的时候,只会与数据库通信一次,而PreparedStatement要通信两次

    prepareStatement使用的比statement多很多。statement几乎不用。

在这里插入图片描述
在这里插入图片描述

3 批处理

其实就是批量的处理SQL语句,典型的业务场景就是一次插入大量的数据。在今后,如果需要大家使用JDBC批量插入数据,可以使用这些方法。

3.1 for循环逐条插入

// for循环来做
public static void batchUseForEach() throws SQLException {

    Statement statement = connection.createStatement();

    for (int i = 0; i < 10000; i++) {

        String sql = "insert into user values ("+i+",'foreach',null,null)";

        statement.executeUpdate(sql);

    }
}

向MySQL服务器发送了SQL语句 1w次,SQL语句被编译了1w次,SQL语句也被执行了1w次

3.2 statement批处理

// Statement 来处理
public static void batchUseStatement() throws SQLException {

    Statement statement = connection.createStatement();

    for (int i = 10000; i < 20000; i++) {
        String sql = "insert into user values ("+i+",'batchUseStatement',null,null)";
        // 相当于在内部有一个容器。 
        statement.addBatch(sql);
    }

    // 发送SQL语句,执行
    statement.executeBatch();

}

向MySQL服务器发送了SQL语句 1次,这一次中包含1w条SQL语句信息,SQL语句被编译了1w次,SQL语句也被执行了1w次

3.3 PreparedStatement批处理

需要在数据库的url后面加上配置:rewriteBatchedStatements=true ,表示开启批处理

// PreparedStatement来处理
public static void batchUsePrepapreStatement() throws SQLException {

    // 获取PreparedStatement
    PreparedStatement preparedStatement = connection.prepareStatement("insert into user values (?,?,null,null)");


    // 循环,设置参数
    for (int i = 20000; i < 30000; i++) {

        preparedStatement.setInt(1,i);
        preparedStatement.setString(2,"PrepapreStatement");

        preparedStatement.addBatch();

    }

    // 把参数发送给MySQL服务器,执行SQL语句
    preparedStatement.executeBatch();

    // insert into user values (),(),(),(),();

}

与MySQL通信了2次,SQL语句被编译了一次,SQL语句被执行了一次

插入n条数据

for循环,通讯n次,编译n次,执行n次

statement,通讯1次,编译n次,执行n次

prepareStatement,通讯2次,编译1次,执行1次

假如需要批处理n条SQL语句,开启了rewriteBatchedStatements之后

通信次数编译次数执行次数时间
for循环nnn最长
Statement1nn次之
PreparedStatement211最短

4 事务

4.1 介绍

数据库事务( transaction)是访问并可能操作各种数据项的一个数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。事务由事务开始与事务结束之间执行的全部数据库操作组成。

事务就是要保证一组数据库操作,要么全部成功,要么全部失败。

比如转账操作,涉及到几个方面。

账户余额表。 zs 1000 | ls 5000

现在zs要给ls转账,转500。现在在数据库里面,我们要进行两步操作。

  1. 扣zs的钱,扣500
  2. 给ls增加钱,增加500

A给B转账。涉及到两个操作,需要给A账户扣钱,然后给B账户增加钱。

如果在这个操作的过程中,出现了异常。可能会导致A账户的钱扣了,B账户的钱没有增加。

4.2 使用事务

开启事务

提交事务

回滚事务

  • API

    //  开启事务
    connection.setAutoCommit(false);
    
    // 提交事务
    connection.commit();
    
    // 回滚事务
    connection.rollback();
    
  • 命令

    # 开始事务
    start transaction;
    
    # 提交事务
    commit;
    
    # 回滚事务
    rollback;
    

代码示例:

    private static boolean transfer(String fromName, String toName, int money) throws SQLException, ClassNotFoundException {

        // 1.获取连接
        Connection connection = JdbcUtils.getConnection();

        // 2.开启事务
        connection.setAutoCommit(false);

        try {
            // 3.1 扣A的钱
            // update account set money = money - ? where name = ? and money > ?
            PreparedStatement preparedStatementA = connection.prepareStatement("update account set money = money - ? where name = ? and " + "money > ?");
            preparedStatementA.setInt(1, money);
            preparedStatementA.setString(2, fromName);
            preparedStatementA.setInt(3, money);

            int affectedRowsA = preparedStatementA.executeUpdate();
            System.out.println(affectedRowsA);

            if (affectedRowsA != 1) {
                throw new RuntimeException("A账户信息不对" + affectedRowsA);
            }

            //int i = 1 / 0;


            // 3.2 增加B账户的钱
            // update account set money = money + ? where name = ?
            PreparedStatement preparedStatementB = connection.prepareStatement("update account set money = money + ? where name = ? ");
            preparedStatementB.setInt(1, money);
            preparedStatementB.setString(2, toName);

            int affectedRowsB = preparedStatementB.executeUpdate();
            System.out.println(affectedRowsB);

            if (affectedRowsB != 1) {
                throw new RuntimeException("B账户信息不对" + affectedRowsB);
            }

            connection.commit();

        } catch (Exception e) {
            e.printStackTrace();
            connection.rollback();
            return false;
        }


        return true;
    }

4.3 特性

事务通常具有四个标准特性(ACID):

  • 原子性(Atomicity)

    事务是一个不可分割的操作单元(数据库的操作),事务中的操作要么就都成功,要么就都不成功。

  • 一致性(Consistency)

    事务必须使数据库从一个一致性状态到另外一个一致性状态。

    在转账案例中,一致性是指 在转账前和转账后,(无论怎么转账),钱的总金额是前后一致的,不变的

  • 隔离性(Isolation)

    事务与事务之间是互相隔离的,互不影响的。

    数据库有为隔离性设置不同的隔离级别。不同的隔离级别对于隔离性的影响是不一样的

  • 持久性(Durability)

    一个事务一旦生效,那么对数据库的改变是永久的,不可逆转的。意思就是提交了事务之后,就已经对数据库产生的变化,那么后续再回滚就回滚不了了

4.4 隔离级别

当数据库有多个事务同时执行的时候,可能会出现问题。

脏读(dirty read)、不可重复读(non-repeatable read)、幻读(phantom read)的问题。

  • 脏读

    一个事务读取到了另外一个事务没有提交的数据。(这个比较严重)

  • 不可重复读

    在同一个事务中,读取同一个数据,前后读取的数据不一致。

    通常指的是,在一个事务中,读取到了另外一个事务已经提交的数据。

  • 幻读

    指在同一个事务中,读取同一个表数据,前后读取的数量不一致。

    通常指的是,在一个事务,读取到了另外一个事务插入或者删除的数据。

事务的隔离级别:

一个参数,可以用来控制事务和事务之间的隔离性。

  • 读未提交(read uncommitted)

    读未提交是指,一个事务还没提交时,它做的变更就能被别的事务看到。

    可能会产生脏读、不可重复读、幻读

  • 读已提交 (read committed)

    读提交是指,一个事务提交之后,它做的变更才会被其他事务看到。
    不会产生脏读,可能会产生不可重复读、幻读

  • 可重复读 (repeatable read)

    这个是MySQL默认的隔离级别

    可重复读是指,一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的。
    不会产生脏读、不可重复读,可能会产生幻读。

  • 串行化 (serializable)

    串行化,顾名思义是对于同一行记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。

    脏读、不可重复读、幻读均不可发生。
    实际工作中很少采用该级别。

    注意:你隔离得越严实,效率就会越低。因此很多时候,我们都要在二者之间寻找一个平衡点。

4.5 演示

准备条件:有一张表,表里只有一条数据。

create table t(
	value int
);
insert into t values (1);

-- test_52th3 代表直接进入这个库
-- 代表我进入之后不用调用 use test_52th3 
mysql -uroot -p123456 test_55th3
# 获取当前数据库的隔离级别
select @@transaction_isolation;
select @@tx_isolation;

# 设置隔离级别
set global transaction isolation level 隔离级别;

mysql -uroot -p123456 test_55th3

set global transaction isolation level repeatable read;

update t set value=1;

# 读未提交
read uncommitted;
# 读已提交
read committed;

# 可重复读
repeatable read

# 串行化
serializable;

# 注意。设置了隔离级别。必须要重新启动一下客户端,才能生效。

MySQL的可重复,可以解决部分幻读问题,不能完全解决。

在这里插入图片描述

  • 读未提交:V1、V2、V3均为2。
  • 读已提交: V1为1,V2为2,V3为2
  • 可重复读: V1,V2为1, V3为2
  • 串行化: 事务2修改的过程中。会一直等待,直到事务1提交

在MySQL中的可重复读,可以部分解决幻读的问题。你去修改了这个数据,又能看到这个数据了。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

InternLM模型部署教程

一、模型介绍 interlm是一系列多语言基础模型和聊天模型。 InternLM2.5 系列&#xff0c;具有以下特点&#xff1a; 出色的推理能力 &#xff1a;数学推理性能达到世界先进水平&#xff0c;超越 Llama3、Gemma2-9B 等模型。1M 上下文窗口 &#xff1a;在 1M 长上下文中几乎完…

【Qt】Qt 网络 | HTTP

文章目录 HTTP Client核心APIQNetworkAccessManagerQNetworkRequestQNetworkReply 代码示例 本文不涉及 HTTP 的相关前置知识&#xff0c;前置知识可参看 URL概念及组成 HTTP请求 HTTP响应及Cookie原理 HTTP Client 进行 Qt 开发时&#xff0c;和服务器之间的通信很多时候也会…

解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界!

随着编程语言的不断演进&#xff0c;Python已经成为开发者们心目中的“瑞士军刀”。它的简洁易用、强大的库支持、广泛的应用领域&#xff0c;让它在人工智能、数据分析、网络爬虫、自动化办公等领域展现了无与伦比的优势。那么&#xff0c;如何深入掌握Python这门语言并用它解…

Stable Diffusion【提示词】【居家设计】:AI绘画给你的客厅带来前所未有的视觉盛宴!

前言 参数设置大模型&#xff1a;RealVisXL V4.0 Lightning采样器&#xff1a;DPM SDE Karras采样迭代步数&#xff1a;5CFG&#xff1a;2图片宽高&#xff1a;1024*1024反向提示词&#xff1a;(octane render, render, drawing, anime, bad photo, bad photography:1.3),(wor…

c++编程(24)——map的模拟实现

欢迎来到博主的专栏&#xff1a;c编程 博主ID&#xff1a;代码小号 文章目录 map的底层红黑树的节点 map的模拟实现map的查找与插入map的迭代器 map的底层 map的底层是一个红黑树&#xff0c;关于红黑树的章节博主写在了数据结构专栏当中&#xff0c;因此不再赘述。 templat…

网络安全服务基础Windows--第8节-DHCP部署与安全

DHCP协议理解 定义&#xff1a;DHCP&#xff1a;Dynamic Host Configuration Protocol&#xff0c;动态主机配置协议&#xff0c;是⼀个应⽤在局域⽹中的⽹络协议&#xff0c;它使⽤UDP协议⼯作。 67&#xff08;DHCP服务器&#xff09;和68&#xff08;DHCP客户端&#xff0…

C语言:常用技巧及误用

一、字符串存储在数组中 int main() {char* arr[7] {"xiaoming","zhangsan","李四"};printf("%s\n", arr[0]);printf("%s\n", arr[2]);return 0; } 二、scanf()函数用法 2.1 scanf()输入字符串 int main() {char arr[10…

raksmart香港大带宽服务器地址

RAKsmart香港大带宽服务器的地址是由RAKsmart公司提供的香港机房所在地&#xff0c;具体地址未在公开资料中披露&#xff0c;但其主要特点是提供高带宽且不限制流量的服务。 RAKsmart是一家成立于2012年的美国公司&#xff0c;其香港机房以提供大带宽、直连内地的优化线路和丰富…

wincc 远程和PLC通讯方案

有 5个污水厂 的数据需要集中监控到1个组态软件上,软件是WINCC。每个污水厂监控系统都是独立的&#xff0c;已经投入运行了&#xff0c; 分站也是WINCC 和西门子PLC 。采用巨控远程模块的话&#xff0c;有两种方式&#xff1a;一种是从现场的PLC取数据&#xff0c;一种是从分站…

HubliderX将Vue3离线包打包生成App,以及解决打包后的APP出现白屏的问题(简单示例)

一、准备 HBuilderX官网&#xff0c;先去官网下载需要的工具到vue项目中把rooter的模式由“history”改为“hash”&#xff0c;否则在本地真机调试时会出现白屏 更改 vue.config.js文件&#xff0c;不修改的话&#xff0c;同样会出现白屏&#xff08;原因&#xff1a;app打开需…

java对接斑马打印机打印标签

JAVA对接斑马打印机打印RFID标签和普通标签 1、打印RFID标签 在打印RFID标签时&#xff0c;如果机器在没有校准的情况下进行打印标签&#xff0c;此时如果还需要获取到RFID的epc值&#xff0c;那么打印机返回的EPC值&#xff0c;有可能不是当前标签的epc值。考虑到此种情形&a…

技能 | next.js服务端渲染技术

哈喽小伙伴们大家好,我是程序媛小李,今天为大家分享一项前端开发中比较主流的服务端渲染技术:next.js 首先,next.js是什么? 通俗来讲,它就是一个React框架, 它能干啥?它能实现服务端渲染. 什么是服务端渲染? 一句话它就是在服务端生成整个页面的内容,用户在客户端只需要…

VS-E5PH3006L-N3 600V 30A 高效低损耗整流器 二极管 电动 / 混动汽车电池充电的可靠之选

VS-E5PH3006L-N3参数特性&#xff1a; 反向电压&#xff08;VR&#xff09;&#xff1a;600V&#xff0c;这表示该整流器在电路中能承受的最大反向电压为 600 伏特&#xff0c;超过此电压可能会导致器件损坏。平均整流电流&#xff08;IF (AV)&#xff09;&#xff1a;30A&…

测试流程及注意事项,包括jemter和postman

一、接口测试需要考虑的地方有哪些&#xff1f; 1、考虑输入参数和输出参数的合法性&#xff0c;参数必填&#xff0c;默认值&#xff0c;参数长度和格式校验&#xff0c;边界等&#xff0c;图片长传考虑图片大小和格式。查询考虑数据排序&#xff0c;分页考虑分页显示等。 2…

linux 下一跳缓存,early demux(‌早期解复用)‌介绍

3.6版本以后的下一跳缓存 3.6版本移除了FIB查找前的路由缓存。这意味着每一个接收发送的skb现在都必须要进行FIB查找了。这样的好处是现在查找路由的代价变得稳定(consistent)了。3.6版本实际上是将FIB查找缓存到了下一跳(fib_nh)结构上&#xff0c;也就是下一跳缓存下一跳缓存…

【算法】蒙特卡洛模拟

一、引言 蒙特卡洛模拟算法是一种基于概率和统计理论的数值计算方法&#xff0c;通过随机抽样来近似复杂系统的概率问题。它以摩纳哥著名的赌场蒙特卡洛命名&#xff0c;象征着其基于随机性的特点。 二、算法原理 蒙特卡洛模拟算法的核心思想是利用随机抽样来估计一个函数的期望…

【SQL】删除表中重复数据的方法

很久之前我写入一张sql的数据表&#xff0c;它里面有很多重复的内容。然后我想只保留一条原始数据&#xff1a; 例如上面的时间&#xff0c;出现了很多重复值。 我最初用的是这种方法&#xff1a; SELECT * FROM table_name WHERE primary_key IN (SELECT max(primary_key)F…

2.4 SQL注入之高权限注入下

SQL注入之高权限注入 1.注入流程与上节实例相同 查询所有数据库名称 http://localhost/sqli-labs-master/Less-2/?id-2%20union%20select%201,group_concat(schema_name),3%20from%20information_schema.schemata查询数据库对应的表名 http://localhost/sqli-labs-master/Le…

JMeter之接口测试

在做接口测试之前&#xff0c;我们起码需要了解&#xff1a; 1、接口涉及的业务 2、接口的基本信息&#xff1a;访问地址、传值方式&#xff08;Post 或 Get&#xff09;、协议类型、域名或IP、端口、参数 3、接口参数是否加密或者有其他处理加工 很多时候&#xff0c;可能…

U8+ 提示子票区间开始输入不合法处理

手工做是否分包流转为是的商业汇票&#xff0c;提示如下&#xff1a; 处理方法&#xff1a; 第一步&#xff1a; 第二步 数据类型为数字&#xff0c;保存即可&#xff0c;填写值为1