Java网络开发(Tomcat)——登陆和注册功能 的 迭代升级 从Jsp到JavaScript + axios + vue 诸多bug 同步到异步

news2025/2/1 17:47:07

目录

  • 引出
  • 前置工作vue+axios+resp
    • 0.vue版本的jsp模板
    • 1.导包--Json:pom.xml文件:
    • 2.新建一个专门用来处理响应的实体类ResData
    • 3.在axios中,所有响应必须是 resp.getWriter().write() 的方式,核心代码如下
    • 4.在jsp前端代码中导包:
    • 5.删除form表单,把页面都放在div框里,并且加上id=“app”
    • 6.在script中写逻辑
  • 登陆功能+保存登陆的用户名
    • 1.前端代码,loginVue.jsp文件 & 保存登陆用户名
    • 2.后端代码如下,LoginServlet.java文件:
    • 3.然后在index页面显示登陆的用户名
  • 注册功能
    • 1.前端代码registerVue.jsp
    • 2.后端代码RegisterServletVue.java
  • 附录:Vue+Axios遇到的bug汇总
      • 1.导包失败net:ERR ABORTED 404
      • 2.未定义错误msg is not defined,data写成date
      • 3.之前的form表单没有删
      • 4.没有加this,报未定义异常imgbtn is not defined
      • 5.post请求没加上下文,POST请求404,Uncaught (in promise)
      • 6.let app = new Vue没有加new
      • 7.一个没有报错信息的bug--应该写在created里
      • 8.一个报错定位不清楚的bug
  • 总结

引出

在之前的jsp版本的登陆和注册中,如下面博客:
Java网络开发(Tomcat)—— 登陆 和 注册功能 的实现 & 从html 到 jsp 迭代升级 & session保存登陆信息

在这里插入图片描述

本质是用同步请求实现登陆和注册功能,在学习Js+axios+vue之后,就可以用异步的方式来实现登陆,主要博客如下:

Java网络开发(Asynchronous)—— 从 Jsp 到 Ajax 的 axios 到 vue & 同步请求 到 异步请求

前端基础(JavaScript)——基础语法(变量,分支…)& Json对象【重要】& 函数定义 & 事件

前端基础(Vue)——基础语法({{}}, v-model, :src=“imagSrc“, v-for)& 事件@click & 属性和方法(this.add() + this.name)


前置工作vue+axios+resp

0.vue版本的jsp模板

要点:head导包;div框+id;script里写逻辑

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="js/vue.min-v2.5.16.js"></script>
</head>
<body>
<div id="app">

</div>

<script>
    let app = new Vue({
        // 选择操作的div区域
        el:"#app",
        // 数据区
        data:{},
        // 方法区
        methods:{},
        // 文档加载之后就执行
        created(){},
        // 整个页面全部加载完成后再执行
        mounted(){},
    })
</script>

</body>
</html>

带有axios请求的vue框架:

<script>
    let app = new Vue({
        el:"#app",
        data:{
            bookTypeList:[]
        },
        methods(){},
        // 文档加载之后就执行
        created(){
            axios.get("/day06/types/list/vue")
                .then(response=>{
                    console.log(response)
                    let resp = response.data;
                    console.log(resp);
                    this.bookTypeList=resp.data;

                },error=>{
                    console.log(error)
                })
        },
    })
</script>

1.导包–Json:pom.xml文件:

<!--    fastjson包-->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.76</version>
    </dependency>

2.新建一个专门用来处理响应的实体类ResData

package com.tianju.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 响应的实体类
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ResData {
    // 返回码,200正常,10001参数错误。。。
    private Integer code;
    // 正常标识,success,ok
    private String msg;
    // 具体的信息,可以是对象
    private Object data;
}

3.在axios中,所有响应必须是 resp.getWriter().write() 的方式,核心代码如下

resp.getWriter().write(JSON.toJSONString(new ResData(1001, "输入为空", null)));

4.在jsp前端代码中导包:

<head>
    <title>用户登陆页面e</title>
<%--    如果没有加day06--%>
<%--    bug--%>
    <link rel="stylesheet" href="/day06/bootstrap/css/bootstrap.css">
    <script src="/day06/js/axios.min.js"></script>
    <script src="/day06/js/jquery-3.5.1.js"></script>
    <script src="/day06/bootstrap/js/bootstrap.js"></script>
    <script src="/day06/js/vue.min-v2.5.16.js"></script>

</head>

5.删除form表单,把页面都放在div框里,并且加上id=“app”

<div id="app">
        用户名:<input type="text" v-model="username"><br>&nbsp;&nbsp;码:<input type="password" v-model="password"><br>
        <span style="color: darkred">{{msg}}</span><br>
        <button @click="login">登陆</button>
        <button @click="reset">重置</button>
</div>

6.在script中写逻辑

<script>
    let app = new Vue({
        el:"#app",
        data:{},
        methods:{},
    })
</script>

登陆功能+保存登陆的用户名

登陆功的代码如下:

1.前端代码,loginVue.jsp文件 & 保存登陆用户名

  • localStorage.setItem(“username”,resp.data)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>用户登陆页面e</title>
<%--    如果没有加day06--%>
<%--    bug--%>
    <link rel="stylesheet" href="/day06/bootstrap/css/bootstrap.css">
    <script src="/day06/js/axios.min.js"></script>
    <script src="/day06/js/jquery-3.5.1.js"></script>
    <script src="/day06/bootstrap/js/bootstrap.js"></script>
    <script src="/day06/js/vue.min-v2.5.16.js"></script>

</head>
<body>
<%--如果忘记删除form表单--%>
<%--bug--%>
<div id="app">
        用户名:<input type="text" v-model="username"><br>
        密&nbsp;&nbsp;码:<input type="password" v-model="password"><br>
        <span style="color: darkred">{{msg}}</span><br>
        <button @click="login">登陆</button>
        <button @click="reset">重置</button>
</div>
<script>
    let app = new Vue({
        el:"#app",
        data:{
            username:"",
            password:"",
            msg:""
        },
        methods:{
            login(){
                let params = new URLSearchParams();
                params.append("username",this.username);
                params.append("password",this.password)
                axios.post("/day06/comUser/login/vue",params)
                    .then(response=>{
                        console.log(response);
                        console.log(response.data);
                        let resp=response.data;
                        if (resp.code==200)
                        {
                            alert(resp.msg);
                            localStorage.setItem("username",resp.data)
                            location.href = "/day06/company/messList"
                        }
                        else
                        {
                            this.msg=resp.msg;
                        }
                    })
            },
            reset(){
                this.username="";
                this.password="";
                this.msg=""
            }

        }
    })
</script>

</body>
</html>

2.后端代码如下,LoginServlet.java文件:

package com.tianju.servlet.comUser;

import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson.JSON;
import com.tianju.entity.ComUser;
import com.tianju.entity.ResData;
import com.tianju.service.IComUserService;
import com.tianju.service.impl.ComUserServiceImpl;
import com.tianju.util.StringUtils;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 用户登陆的servletVue
 */
@WebServlet("/comUser/login/vue")
public class LoginServletVue extends HttpServlet {
    private IComUserService comUserService = new ComUserServiceImpl();
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 输入不为空
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        if (StringUtils.isBlank(username) || StringUtils.isBlank(password)){
            resp.getWriter().write(JSON.toJSONString(
                    new ResData(1001, "输入为空", null)));
            return;
        }

        // 进行验证
        ComUser comUser = comUserService.queryByUsername(username);
        if (comUser==null || !comUser.getPassword().equals(SecureUtil.md5(password))){
            resp.getWriter().write(JSON.toJSONString(new ResData(1002, "用户名 | 密码错误", null)));
            return;
        }

        // ++++++登陆成功的用户对象存储到session中=====用户确定后续的操作是谁做的
        req.getSession().setAttribute("user", comUser);

        resp.getWriter().write(JSON.toJSONString(new ResData(200, "ok", comUser.getUsername())));

    }
}

3.然后在index页面显示登陆的用户名

欢迎您:
<%--这个改成vue的 --%>
<%--${sessionScope.user.username}--%>
{{username}}

核心代码如下:

<script>
    let app = new Vue({
        el:"#app",
        data:{
            username:"",
        },
        methods:{},
        created(){
            this.username=localStorage.getItem("username")
        }

    })
</script>

注册功能

在这里插入图片描述

1.前端代码registerVue.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>用户注册页面</title>
    <link rel="stylesheet" href="/day06/bootstrap/css/bootstrap.css">
    <script src="/day06/js/axios.min.js"></script>
    <script src="/day06/js/jquery-3.5.1.js"></script>
    <script src="/day06/bootstrap/js/bootstrap.js"></script>
    <script src="/day06/js/vue.min-v2.5.16.js"></script>
</head>
<body>
<div id="app">
    用户名:<input type="text" v-model="username"><br>&nbsp;&nbsp;码:<input type="text" v-model="password"><br>
    确认密码:<input type="text" v-model="rePassword"><br>&nbsp;&nbsp;&nbsp;别:
    男<input type="radio" v-model="gender" value="男"><input type="radio" v-model="gender" value="女"><br>
    出生日期:<input type="date" v-model="birthday"><br>
    <%--    如果要让验证码在点击时自动更新--%>
    验证码:<input type="text" v-model="imgCode">

    <img :src="imgsrc" @click="imgbtn">

    <br>

    <span style="color: darkred">{{msg}}</span><br>
    <button @click="register">注册</button>
    <button @click="reset">重置</button>
    <a href="/index.jsp">退出</a>

</div>

<script>
    let app = new Vue({
        el:"#app",
        data:{
            username:"",
            password:"",
            rePassword:"",
            gender:"",
            birthday:"",
            imgCode:"",
            msg:"",
            imgsrc:"/day06/register/image/get",

        },
        methods:{
            register(){
                let params = new URLSearchParams();
                params.append("username",this.username);
                params.append("password",this.password);
                params.append("rePassword",this.rePassword);
                params.append("gender",this.gender);
                params.append("birthday",this.birthday);
                params.append("imgCode",this.imgCode);
                console.log(params);
                axios.post("/day06/comUser/register/vue",params)
                    .then(response=>{
                        console.log(response);
                        console.log(response.data);
                        let resp = response.data;
                        if (resp.code==200)
                        {
                            alert("登陆成功");
                            // 保存登陆到local
                            localStorage.setItem("username",this.username);
                            // 跳转到index
                            location.href = "/day06/compMess/list.jsp";
                        }
                        else if (resp.code==1002){
                            // 更新一下验证码
                            this.imgbtn();
                            this.msg=resp.msg;
                        }
                        else
                        {
                            this.msg=resp.msg;
                        }
                    })
            },
            reset() {
                this.username="";
                this.password="";
                this.rePassword="";
                this.gender="";
                this.birthday="";
                this.imgCode="";
                this.msg="";
            },
            imgbtn(){
                // 切换图片
                this.imgsrc = '/day06/register/image/get?'+new Date().getMilliseconds();
            }
        }
    })
</script>

</body>
</html>

2.后端代码RegisterServletVue.java

package com.tianju.servlet.comUser;

import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson.JSON;
import com.tianju.entity.ComUser;
import com.tianju.entity.ResData;
import com.tianju.service.IComUserService;
import com.tianju.service.impl.ComUserServiceImpl;
import com.tianju.util.StringUtils;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;

/**
 * 用vue版本的注册的servlet
 */
@WebServlet("/comUser/register/vue")
public class RegisterServletVue extends HttpServlet {
    private IComUserService comUserService = new ComUserServiceImpl();
    private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        // 用户注册的业务逻辑
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        String rePassword = req.getParameter("rePassword");
        String sex = req.getParameter("gender");
        String birthday = req.getParameter("birthday");
        System.out.println(username+password+rePassword+sex+birthday);
        // +++加入+验证码输入
        String imgCode = req.getParameter("imgCode");

        // 1.输入不为空;
        if (StringUtils.isBlank(username)
        || StringUtils.isBlank(password)
        || StringUtils.isBlank(rePassword)
        || StringUtils.isBlank(sex)
        || StringUtils.isBlank(birthday)
        || StringUtils.isBlank(imgCode)){
            resp.getWriter().write(JSON.toJSONString(
                    new ResData(1001, "输入为空", null)));

            return;
        }

        // +++++判断验证码是否正确
        HttpSession session = req.getSession();
        // 从session中获取验证码
        String yzm = (String)session.getAttribute("yzm");
        // 忽略大小写 .equalsIgnoreCase
        if (!yzm.equalsIgnoreCase(imgCode)){
            resp.getWriter().write(JSON.toJSONString(
                    new ResData(1002, "验证码不正确", null)));
            return;
        }


        // 2.用户名不重复;
        ComUser comUserDb = comUserService.queryByUsername(username);
        if (comUserDb!=null){
            resp.getWriter().write(JSON.toJSONString(
                    new ResData(1003, "用户名重复", null)));
            return;
        }

        // 3.两次密码输入一致;
        if (!password.equals(rePassword)){
            // 两次密码不一致
            resp.getWriter().write(JSON.toJSONString(
                    new ResData(1004, "两次密码输入不一致,请检查", null)));
            return;
        }


        // 进行密码加密存储
        ComUser comUser = new ComUser();
        comUser.setPassword(SecureUtil.md5(password));
        comUser.setUsername(username);
        try {
            comUser.setBirthday(sdf.parse(birthday));
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
        comUser.setSex(sex);

        System.out.println(comUser);


        // 4.保存信息到数据库
        comUserService.add(comUser);

        // 5.注册成功跳转到登陆页面
        resp.getWriter().write(JSON.toJSONString(
                new ResData(200, "注册成功", null)));

    }
}

附录:Vue+Axios遇到的bug汇总

1.导包失败net:ERR ABORTED 404

报错信息:

GET http://localhost:8080/bootstrap/css/bootstrap.css net:ERR ABORTED 404

在这里插入图片描述

2.未定义错误msg is not defined,data写成date

报错信息:ReferenceError:msg is not defined

在这里插入图片描述

另一种情况,不太好找:

在这里插入图片描述

3.之前的form表单没有删

导致发送两次请求
在这里插入图片描述

在这里插入图片描述

4.没有加this,报未定义异常imgbtn is not defined

Uncaught (in promise) ReferenceError: imgbtn is not defined

在这里插入图片描述

5.post请求没加上下文,POST请求404,Uncaught (in promise)

报错信息:

http://localhost:8080/comUser/register/vue 404

Uncaught (in promise)
{message:‘Request failed with status code 404’,name:‘AxiosError’,

在这里插入图片描述

6.let app = new Vue没有加new

报错信息:

vue.min-v2.5.16.js:6 Uncaught TypeError: Cannot read properties of undefined (reading ‘_init’)

在这里插入图片描述

7.一个没有报错信息的bug–应该写在created里

在这里插入图片描述

8.一个报错定位不清楚的bug

报错信息:

Uncaught TypeError: e.bind is not a function

在这里插入图片描述


总结

1.vue+axios前端固定格式,后端必须用resp.getWriter().write() ;
2.localStorage的应用,保存登陆成功的用户名;
3.登陆和注册功能的异步实现,JavaScript + axios + vue + resp;
4.遇到的诸多bug汇总,

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

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

相关文章

华为OD机试真题B卷 JavaScript 实现【查字典】,附详细解题思路

一、题目描述 输入一个单词前缀和一个字典&#xff0c;输出包含该前缀的单词。 二、输入描述 单词前缀字典长度字典。 字典是一个有序单词数组。 输入输出都是小写。 三、输出描述 所有包含该前缀的单词&#xff0c;多个单词换行输出。 若没有则返回-1。 四、解题思路…

SLAM十四讲——ch3实践

ch3的实践及避坑 一、ch3的总体步骤二、文件的执行1. 实践&#xff1a;Eigen2. 实践&#xff1a;Eigen几何模块3. 可视化演示 出现的问题 一、ch3的总体步骤 确保已经安装Eigen库&#xff0c;Eigen库是一个C开源线性代数库。 sudo apt-get install libeigen3-dev说明&#xf…

在 Compose 中实现缓存列表数据提升用户体验(Stale-while-revalidate)

前言 最近在利用业余时间使用 Compose 实现一个 Github APP 客户端。 对标的是 GSY 大佬使用多种不同语言框架实现的 Github APP。 在实现过程中发现一些问题&#xff0c;因为这个客户端的数据几乎全部来自于 Github API&#xff0c;所以 UI 渲染也极度依赖于请求到的数据。…

JAVA面向对象(三)

第三章 封装与继承 目录 第三章 封装与继承 1.1.封装 1.2.包 1.3.访问权限控制 1.4.static修饰符 1.4.1.成员变量 1.4.2.成员方法 1.4.3.代码块 总结 内容仅供学习交流&#xff0c;如有问题请留言或私信&#xff01;&#xff01;&#xff01;&#xff01;&#xff0…

java并发编程:ArrayBlockingQueue详解

文章目录 一、简介二、数据结构三、源码分析3.1 属性3.2 构造方法3.3 方法3.3.1 入队3.3.2 出队3.3.3 获取元素3.3.4 删除元素 四、总结 一、简介 ArrayBlockingQueue 顾名思义&#xff1a;基于数组的阻塞队列。数组是要指定长度的&#xff0c;所以使用 ArrayBlockingQueue 时…

在知乎逮到一个腾讯10年老测试,聊过之后收益良多...

老话说的好&#xff0c;这人呐&#xff0c;一单在某个领域鲜有敌手了&#xff0c;就会闲得蛋疼。前几天我在上班摸鱼刷知乎的时候认识了一位腾讯测试大佬&#xff0c;在腾讯工作了10年&#xff0c;因为本人天赋比较高&#xff0c;平时工作也兢兢业业&#xff0c;现在企业内有一…

Python基础知识讲解——main方法

前言 嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 估计很多人跟我一样初学python看代码的时候先找一下main()方法&#xff0c;从main往下看。 但事实上python中是没有你理解中的“main()”方法的。 言归正传 if name "main":可以看成是python程序的入口&a…

数据结构与算法系列之习题练习

&#x1f497; &#x1f497; 博客:小怡同学 &#x1f497; &#x1f497; 个人简介:编程小萌新 &#x1f497; &#x1f497; 如果博客对大家有用的话&#xff0c;请点赞关注再收藏 &#x1f31e; 力扣习题 括号匹配问题。用队列实现栈。用栈实现队列。设计循环队列。 有效的括…

【数据分析案例】深度分析超市零售商店数据--Python数据分析实战

前言 咳咳&#xff0c;又是好久不见~这不高考已经结束了 对python感兴趣的准大学生们&#xff0c;是打算好好玩几个月还是&#xff0c;继续研究学习python呢~ &#x1f928; 我呢 还是建议大家劳逸结合哈哈 先玩再学习~ 当然啦 最重要的还是看你们自己呀 不过我以上这些都不能…

[NOI2007] 调兵遣将

题目描述 我军截获的情报显示&#xff0c;敌军正在集结兵力试图向我军重要的军械研究所发起进攻。由于我军正处于多线作战的状态&#xff0c;无法抽调大批兵力前去支援&#xff0c;指挥部决定通过有效的战前部署来提高胜率&#xff0c;减少伤亡和损失。 该军械研究所的平面图…

网络安全自学笔记+学习路线+就业规划(超详细)

每天都有新闻报道描述着新技术对人们的生活和工作方式带来的巨大乃至压倒性影响。与此同时有关网络攻击和数据泄露的头条新闻也是日益频繁。 攻击者可谓无处不在&#xff1a;企业外部充斥着黑客、有组织的犯罪团体以及民族国家网络间谍&#xff0c;他们的能力和蛮横程度正日渐…

如何使虚拟机自动生成ip地址

一. 打开虚拟机并登录账号进入命令行界面输入指令&#xff1a; vi /etc/sysconfig/network-scripts/ifcfg-ens33 通过指令进入到下面的界面当中 点击键盘输入 "i" 进入编辑模式将文件修改为 文件当中的 BOOTPROTO可以将ip地址定义为自动生成类型或者静态指定类型其中…

力扣 213. 打家劫舍 II

一、题目描述 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋&#xff0c;每间房内都藏有一定的现金。这个地方所有的房屋都围成一圈&#xff0c;这意味着第一个房屋和最后一个房屋是紧挨着的。同时&#xff0c;相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻…

高考后计算机人工智能大类专业的选择建议

随着GPT的出现&#xff0c;很多人开始质疑是否还需要学计算机专业&#xff0c;计算机专业是否会消失。 先给结论&#xff0c;不会&#xff01; 只是会产生分层&#xff0c;大体上是这样的&#xff1a; 核心代码部分还是需要人来写的&#xff0c;只要是代码方式出现的结果&#…

了解ASEMI代理英飞凌TLE6208-6G其功能和应用的综合指南

编辑-Z TLE6208-6G是一款高度集成、通用且高效的汽车半桥驱动器&#xff0c;由英飞凌设计。这种功能强大的设备专门设计用于满足汽车应用的苛刻要求&#xff0c;如控制直流电机、螺线管和电阻负载。在本文中&#xff0c;我们将深入研究TLE6208-6G的功能、优点和应用&#xff0…

【Spring MVC】这几种传参方式这么强大,让我爱不释手,赶快与我一起去领略吧 ! ! !

前言: 大家好,我是良辰丫,在上2一篇文章中我们已经初步认识了Spring MVC,并且学习了热部署的配置,今天我们将继续开始我们的Spring MVC的学习! ! !&#x1f48c;&#x1f48c;&#x1f48c; &#x1f9d1;个人主页&#xff1a;良辰针不戳 &#x1f4d6;所属专栏&#xff1a;jav…

JDK9~17+Springboot3 @Resource常见问题和解决方案

一、常见问题描述 因为JDK版本升级的改动&#xff0c;在Jdk9~17环境下&#xff0c;搭建Springboot项目&#xff0c;会出现原有Resource&#xff08;javax.annotation.Resource&#xff09;不存在的问题&#xff0c;导致项目从Jdk8迁移到高版本时遇到的问题 原因 你可能会问&…

TiDB亿级数据亚秒响应查询将MySql数据全量迁移到TiDB

目录 1 下载安装TiDB工具包1.1 检查最新版本1.2 下载tidb-toolkit 2 Dumpling导出数据2.1 Dumpling工具简介2.2 导出需要的权限2.3 创建用户并授权2.4 验证数据库2.5 导出sql文件2.6 查看导出文件 3 TiDB Lightning导入数据3.1 TiDB Lightning简介3.2 TiDB Lightning 整体架构3…

蓝桥杯【第14届国赛】Python B组

本题解仅代表个人观点&#xff0c;仅供参考&#xff0c;欢迎各位指正 A&#xff1a;弹珠堆放 【问题描述】 小蓝有 20230610 颗磁力弹珠&#xff0c;他对金字塔形状尤其感兴趣&#xff0c;如下图所示&#xff1a; 高度为 1 的金字塔需要 1 颗弹珠&#xff1b; 高度为 2 的金字…

【Jetpack】使用 Room 中的 Migration 升级数据库异常处理 ( 多个数据库版本的迁移 | fallbackToDestructiveMigration() 函数处理升级异常 )

文章目录 一、Room#Migration 迁移工具升级数据库二、多个数据库版本的迁移三、数据库异常处理 - RoomDatabase.Builder#fallbackToDestructiveMigration() 函数四、完整代码示例 一、Room#Migration 迁移工具升级数据库 Room Migration 数据库迁移工具 是 Android Jetpack Arc…