SQLiteDataBase数据库

news2025/1/4 5:10:45
XML界面设计  
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="姓名:" />

        <EditText
            android:id="@+id/etc_name"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="50px" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="班级:" />

        <EditText
            android:id="@+id/etc_class"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="50px" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="学号:" />

        <EditText
            android:id="@+id/etc_id"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="50px" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <Button
            android:id="@+id/btn_add"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="添加数据"
            android:textAlignment="center" />

        <Button
            android:id="@+id/btn_show"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="全部显示"
            android:textAlignment="center" />

        <Button
            android:id="@+id/btn_clr"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="清除数据"
            android:textAlignment="center" />

        <Button
            android:id="@+id/btn_del"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="全部删除"
            android:textAlignment="center" />
    </LinearLayout>


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="ID:" />

        <EditText
            android:id="@+id/etc_id2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="2" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <Button
            android:id="@+id/btn_del_id"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="ID删除"
            android:textAlignment="center" />
        <Button
            android:id="@+id/btn_show_id"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="ID查询"
            android:textAlignment="center" />
        <Button
            android:id="@+id/btn_update_id"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="ID更新"
            android:textAlignment="center" />
    </LinearLayout>

    <TextView
        android:id="@+id/txt_end"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="" />

    <ListView
        android:id="@+id/list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

</LinearLayout>
People.java
package com.example.exp6;

public class People {
    public int ID = -1;
    public String Name;
    public String Class;
    public String Number;

    //重载toString方法  返回值String类型
    @Override
    public String toString(){
        String result = "";
        result += "ID:" + this.ID + ",";
        result += "姓名:" + this.Name + ",";
        result += "班级:" + this.Class + ", ";
        result += "学号:" + this.Number;
        return result;
    }
}
 DBAdapter.java
package com.example.exp6;
import android.annotation.SuppressLint;
import android.content.*;
import android.database.Cursor;
import android.database.DatabaseErrorHandler;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;

import androidx.annotation.Nullable;

//对数据库进行操作的类
public class DBAdapter {
    private static final String DB_NAME = "student.db";   //数据库名称
    private static final String DB_TABLE = "peopleinfo";  //表名
    private static final int DB_VERSION = 1;              //数据库版本
    public static final String KEY_ID = "_id";            //数据库表的属性名称
    public static final String KEY_NAME = "name";
    public static final String KEY_CLASS = "class";
    public static final String KEY_NUMBER = "number";

    private SQLiteDatabase db;              //数据库的实例db
    private final Context context;          //Context的实例化
    private DBOpenHelper dbOpenHelper;      //帮助类的实例化 dbOpenHelper

    //内部类:对帮助类 构建
    //继承SQLiteOpenHelper
    //必须重载onCreate和onUpgrade方法
    private static class DBOpenHelper extends SQLiteOpenHelper {
        //帮助类的构造函数 -- 4个参数
        //Code -> SQLiteOpenHelper 即可成功插入该构造函数
        public DBOpenHelper(@Nullable Context context, @Nullable String name,
                            SQLiteDatabase.CursorFactory factory, int version) {
            super(context, name, factory, version);
        }

        //static常量字符串--创建表的 sql 命令
        //create table peopleinfo("id integer primary key autoincrement,name text not null,
        // class text not null,number text not null")
        private static final String DB_CREATE = "create table " +
                DB_TABLE + " (" + KEY_ID + " integer primary key autoincrement, " +
                KEY_NAME + " text not null, " + KEY_CLASS + " text not null," +
                KEY_NUMBER + " text not null);";
        //重载帮助类onCreate方法
        //1个参数SQLiteDatabase类型
        @Override
        public void onCreate(SQLiteDatabase sqLiteDatabase) {
            //execSQL()方法 1个String类型的 sql 建表命令
            sqLiteDatabase.execSQL(DB_CREATE);
        }

        //重载帮助类onUpdate方法
        //一般在升级的时候被调用
        //删除旧的数据库表 并将数据转移到新版本的数据库表
        //3个参数SQLiteDatabase类型、int i-旧版本号、int i1-新版本号
        @Override
        public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
            //仅删除原表后建立新表
            sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + DB_TABLE);
            onCreate(sqLiteDatabase);
        }
    }

    //对数据库进行操作的类DBAdapter的构造
    //参数1个 Context类型
    public DBAdapter(Context _context) {
        context = _context;
    }
    //DBAdapter类的open方法
    //抛出异常!!!!
    public void open() throws SQLiteException {
        //帮助类实例化dbOpenHelper需要4个参数
        dbOpenHelper = new DBOpenHelper(context, DB_NAME,
                null, DB_VERSION);

        //调用getWritableDatabase方法
        try {
            db = dbOpenHelper.getWritableDatabase();
        } catch (SQLiteException ex) {
            db = dbOpenHelper.getReadableDatabase();
        }
    }
    //DBAdapter类的close方法
    public void close() {
        if (db != null) {
            db.close();
            db = null;
        }
    }
    //DBAdapter类的insert方法
    //参数1个:数据库储存的类型(本例是People
    public long insert(People people) {
        //用ContentValues类型
        ContentValues newValues = new ContentValues();
        //在put的时候不需要put Key_ID!!!
        newValues.put(KEY_NAME, people.Name);
        newValues.put(KEY_CLASS, people.Class);
        newValues.put(KEY_NUMBER, people.Number);
        //返回值是新数据插入的位置 ID值
        //数据库对象.insert方法
        //参数3个:表名 在null时的替换数据 ContentValues类型需要添加的数据
        return db.insert(DB_TABLE, null, newValues);
    }
    //DBAdapter类的deleteAllData方法
    //无参数
    public long deleteAllData() {
        //返回值是被删除的数据的数量
        //参数3个:表名 删除条件(删除全部数据条件是null)
        return db.delete(DB_TABLE, null, null);
    }
    //DBAdapter类的deleteOneData方法
    //参数1个:long类型的id值
    public long deleteOneData(long id) {
        //返回值是被删除的数据的数量
        //参数3个:表名 删除条件(删除形参给的id)
        return db.delete(DB_TABLE,  KEY_ID + "=" + id, null);
    }
    //DBAdapter类的UpdataOneData方法
    //参数2个:long类型的id值 和 People类型
    public long updateOneData(long id , People people){
        //更新和插入一样用到了ContentValues类型
        ContentValues updateValues = new ContentValues();
        //同样不需要放Key_ID
        updateValues.put(KEY_NAME, people.Name);
        updateValues.put(KEY_CLASS, people.Class);
        updateValues.put(KEY_NUMBER, people.Number);
        //返回值是被更新的数据数量
        //参数4个 表明 ContentValues 更新条件string类型 null
        return db.update(DB_TABLE, updateValues,  KEY_ID + "=" + id, null);
    }
    //private类型
    //DBAdapter类的ConvertToPeople方法
    //参数1个 Cursor类型
    //返回People数组
    //查询需要基于该 ConvertToPeople 方法(应该是自定义方法?)
    @SuppressLint("Range")
    private People[] ConvertToPeople(Cursor cursor){
        //getCount方法返回查询结果总行数
        int resultCounts = cursor.getCount();
        //行数为0||moveToFirst方法返回false返回结果为空
        if (resultCounts == 0 || !cursor.moveToFirst()){
            //该方法返回null
            return null;
        }
        //新建resultCounts个People对象
        People[] peoples = new People[resultCounts];
        //循环 resultCounts次
        for (int i = 0 ; i<resultCounts; i++){
            peoples[i] = new People();
            peoples[i].ID = cursor.getInt(0);
            //根据 列属性索引 得到 属性值
            peoples[i].Name = cursor.getString(cursor.getColumnIndex(KEY_NAME));
            peoples[i].Class = cursor.getString(cursor.getColumnIndex(KEY_CLASS));
            peoples[i].Number = cursor.getString(cursor.getColumnIndex(KEY_NUMBER));
            //游标/指针下移
            cursor.moveToNext();
        }
        //返回People[]
        return peoples;
    }

    //查询操作:
    //DBAdapter类的getOneData方法
    //参数1个:long类型的id值
    public People[] getOneData(long id) {
        //query方法
        //参数7个(6String 1String[]):表名称 属性列-String[] 查询条件
        //查询条件是否使用通配符 分组条件 分组过滤条件 排序方式  后4个全是null
        //返回Cursor类型
        Cursor results =  db.query(DB_TABLE, new String[] { KEY_ID, KEY_NAME, KEY_CLASS, KEY_NUMBER},
                KEY_ID + "=" + id, null, null, null, null);
        //返回People[]
        return ConvertToPeople(results);
    }

    //DBAdapter类的getAllData方法
    public People[] getAllData() {
        //参数查询条件为null  存在5个null
        Cursor results = db.query(DB_TABLE, new String[] { KEY_ID, KEY_NAME, KEY_CLASS, KEY_NUMBER},
                null, null, null, null, null);
        return ConvertToPeople(results);
    }

}
MainActivity.java 
package com.example.exp6;

import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import java.util.ArrayList;
import java.util.List;
import android.widget.ArrayAdapter;

public class MainActivity extends AppCompatActivity {
    EditText etc_name,etc_class,etc_id,etc_id2;
    TextView txt_end;
    ListView listView;
    Button btn_add,btn_show,btn_clr,btn_del,btn_del_id,btn_show_id,btn_update_id;

    DBAdapter dbAdapter;
    SQLiteDatabase db;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        listView=findViewById(R.id.list);
        ArrayList<String> data=new ArrayList<String>();
        //数组适配器
        //实例化 参数3个
        ArrayAdapter<String> adapter=new ArrayAdapter<String>(this,
                android.R.layout.simple_list_item_1,data);

        etc_name=findViewById(R.id.etc_name);
        etc_class=findViewById(R.id.etc_class);
        etc_id=findViewById(R.id.etc_id);

        btn_add=findViewById(R.id.btn_add);
        btn_show=findViewById(R.id.btn_show);
        btn_clr=findViewById(R.id.btn_clr);
        btn_del=findViewById(R.id.btn_del);

        btn_del_id=findViewById(R.id.btn_del_id);
        btn_show_id=findViewById(R.id.btn_show_id);
        btn_update_id= findViewById(R.id.btn_update_id);

        etc_id2=findViewById(R.id.etc_id2);
        txt_end=findViewById(R.id.txt_end);

        //处理数据库的类的实例对象
        //参数1个 Context类型
        dbAdapter=new DBAdapter(this);
        //调用DBAdapter对象的 open 方法
        dbAdapter.open();

        btn_add.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //将文本框中的内容用来创建对象People
                People t=new People();
                t.Name=etc_name.getText().toString();
                t.Class=etc_class.getText().toString();
                t.Number=etc_id.getText().toString();
                //插入一个对象People
                //返回插入的位置id
                long colunm=dbAdapter.insert(t);
                if (colunm == -1 ){
                    txt_end.setText("添加过程错误!");
                } else {
                    txt_end.setText("ID:"+String.valueOf(colunm)+"   姓名:"+
                            t.Name+"   班级:"+t.Class+"   学号:"+t.Number);
                }
            }
        });

        btn_show.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //调用得到数据库中全部的信息
                People [] peoples =dbAdapter.getAllData();
                if (peoples == null){
                    txt_end.setText("数据库中没有数据");
                    return;
                }
                String t="数据库:\n";
                for(int i=0;i<peoples.length;++i){
                    t += peoples[i].toString()+"\n";
                }
                txt_end.setText(t);
            }
        });

        btn_clr.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                txt_end.setText("");
            }
        });

        btn_del.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                dbAdapter.deleteAllData();
                txt_end.setText("已删除所有数据!");
            }
        });

        btn_del_id.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                int id=Integer.parseInt(etc_id2.getText().toString());
                //返回删除的数据的数量
                //参数要求int类型的
                long result=dbAdapter.deleteOneData(id);
                String msg = "删除ID为"+etc_id2.getText().toString()+"的数据" + (result>0?"成功":"失败");
                txt_end.setText(msg);
            }
        });

        btn_show_id.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                int id=Integer.parseInt(etc_id2.getText().toString());
                //查找方法 参数id-int
                //返回值是People[]
                People people[]=dbAdapter.getOneData(id);
                if(people==null){
                    txt_end.setText("Id为"+id+"的记录不存在!");
                }
                else{
                    //因为仅只查找了一条信息 所以是people[0]
                    //并且在People类型中已经重载了函数toString()
                    txt_end.setText("查询成功:\n"+people[0].toString());
                }
            }
        });

        btn_update_id.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                int id=Integer.parseInt(etc_id2.getText().toString());
                People t=new People();
                t.Name=etc_name.getText().toString();
                t.Class=etc_class.getText().toString();
                t.Number=etc_id.getText().toString();
                //更新方法
                //参数2个 id-int people类型
                //返回值 被更新的数据数量
                long n=dbAdapter.updateOneData(id,t);
                if (n<0){
                    txt_end.setText("更新过程错误!");
                }else{
                    txt_end.setText("成功更新数据,"+String.valueOf(n)+"条");
                }
            }
        });
    }
    @Override
    protected void onStop() {
        super.onStop();
        dbAdapter.close();
    }
}
结果

数据添加(朱迪&尼克狐尼克)

全部显示 

ID删除 (25-朱迪)

 ID查询 (26)

ID更新 (26) 

全部删除

全部显示 

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

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

相关文章

cesium小知识: 处理动画的5种方式

在 Cesium 中处理动画可以通过多种方式实现,具体取决于你想要创建的动画类型。Cesium 提供了丰富的API来支持不同种类的动画,包括但不限于物体的移动、旋转、缩放、属性变化等。以下是几种常见的动画处理方法: 1. 使用 Entity 和 SampledProperty 对于动态数据或随时间变化…

【鸿蒙NEXT】鸿蒙里面类似iOS的Keychain——关键资产(@ohos.security.asset)实现设备唯一标识

前言 在iOS开发中Keychain 是一个非常安全的存储系统&#xff0c;用于保存敏感信息&#xff0c;如密码、证书、密钥等。与 NSUserDefaults 或文件系统不同&#xff0c;Keychain 提供了更高的安全性&#xff0c;因为它对数据进行了加密&#xff0c;并且只有经过授权的应用程序才…

visual studio连接sql server数据库

目录 1、为什么要建立连接2、在sql server中建立数据库3、visual studio连接sql server数据库4、学生信息管理系统页面布局5、添加事件逻辑 5.1 页面跳转5.2 读取学生信息5.3 查询学生信息5.4 修改学生信息5.5 删除学生信息5.6 添加学生信息 bilibili演示视频 github源码 1、…

STM32-笔记23-超声波传感器HC-SR04

一、简介 HC-SR04 工作参数&#xff1a; • 探测距离&#xff1a;2~600cm • 探测精度&#xff1a;0.1cm1% • 感应角度&#xff1a;<15 • 输出方式&#xff1a;GPIO • 工作电压&#xff1a;DC 3~5.5V • 工作电流&#xff1a;5.3mA • 工作温度&#xff1a;-40~85℃ 怎么…

vuex - 第一天

思维逻辑 解决问题 代码能力2 vue2的项目 北京前端鸿蒙6期 语雀 vuex 在组件中使用 插件支持v2和v3 宏任务 和 微任务 多问问自己为什么 new的四步 查找数组里是否包含某个元素 同步任务、异步任务、微任务、宏任务

三大行业案例:AI大模型+Agent实践全景

本文将从AI Agent和大模型的发展背景切入&#xff0c;结合51Talk、哈啰出行以及B站三个各具特色的行业案例&#xff0c;带你一窥事件驱动架构、RAG技术、人机协作流程&#xff0c;以及一整套行之有效的实操方法。具体包含内容有&#xff1a;51Talk如何让智能客服“主动进攻”&a…

STM32G0B1 can Error_Handler 解决方法

问题现象 MCU上电&#xff0c;发送0x13帧数据固定进入 Error_Handler 硬件介绍 MCU :STM32G0B1 can:NSI1042 tx 接TX RX 接RX 折腾了一下午&#xff0c;无解&#xff0c;问题依旧&#xff1b; 对比测试 STM32G431 手头有块G431 官方评估版CAN 模块&#xff1b; 同样的…

【服务器】上传文件到服务器并训练深度学习模型下载服务器文件到本地

前言&#xff1a;本文教程为&#xff0c;上传文件到服务器并训练深度学习模型&#xff0c;与下载服务器文件到本地。演示指令输入&#xff0c;完整的上传文件到服务器&#xff0c;并训练模型过程&#xff1b;并演示完整的下载服务器文件到本地的过程。 本文使用的服务器为云服…

基于微博热搜评论的情感分析与热点主题挖掘研究

目录 1、绪论 1.1 研究背景与研究意义 1.2 数据来源 1.3 技术路线 2、数据预处理 2.1 数据清洗与准备 2.2 导入必要库与加载数据 2.3 加载停用词表与分词处理 2.4 统计词频与高频词分析 3、情感分析与主题建模 3.1 情感分析 3.2 主题建模 3.3 热点主题识别 4、数据可视…

“进制转换”公式大集合

咱们都知道十进制是“逢10进1 ”&#xff0c;同理&#xff0c;N进制就是 “逢N进1”。进制其实就这么简单。它的麻烦之处在于各种进制之间的转换。 一、十进制整数转N进制 1&#xff0e;十进制转二进制 除2取余法&#xff1a;连续除以2&#xff0c;直到商为0&#xff0c;逆序…

【React】- 跨域PDF预览、下载(改文件名)、打印

我们经常会碰到跨域来方位PDF&#xff0c;同时需要下载、打印的需求&#xff0c;通常由于浏览器的安全策略&#xff0c;可以预览&#xff0c;但是下载和打印可能会受限&#xff0c;这时候怎么办呢&#xff1f; 1.创建一个隐藏的标签 要下载 iframe 中的 PDF 文件&#xff0c;…

echarts 柱形图重叠柱形图legend,双y轴

echarts 图表组件&#xff1a; <template><div :style"{ height: 100% }"><div class"foldLine" ref"foldLine" :style"{ width: widths, height: heights }"></div></div> </template> <scr…

MySQL5.7主从同步配置

环境&#xff1a; 使用2台虚拟机&#xff0c;如图-1所示。其中192.168.4.51是主服务器,另一台192.168.4.52作为从服务器&#xff0c;通过调取主服务器上的binlog日志&#xff0c;在本地重做对应的库、表&#xff0c;实现与主服务器的数据同步。 主服务器、从服务器都已安装好m…

方正畅享全媒体新闻采编系统 imageProxy.do 任意文件读取漏洞复现

0x01 产品简介 方正畅享全媒体新闻生产系统是以内容资产为核心的智能化融合媒体业务平台,融合了报、网、端、微、自媒体分发平台等全渠道内容。该平台由协调指挥调度、数据资源聚合、融合生产、全渠道发布、智能传播分析、融合考核等多个平台组成,贯穿新闻生产策、采、编、发…

Ubuntu安装Apache Airflow详细指南

本文我们介绍如何在Ubuntu上安装Apache Airflow。Apache Airflow旨在通过编程方式编写、调度和监控工作流。随着数据编排在现代数据工程中变得越来越重要&#xff0c;掌握Apache Airflow等工具可以显著提高您的生产力和效率。 学习Apache Airflow的首要任务是安装单机版本进行测…

生物信息学软件开发综述学习

目录 ①编程语言和开源工具和库 ②轻量级 R 包开发 ③大规模组学软件开发 ④示例 1.轻量级 R 包开发示例及数据 2.大规模组学软件开发 文献&#xff1a;Bioinformatics software development: Principles and future directions ①编程语言和开源工具和库 在生物信息学…

哈夫曼编码(Huffman Coding)与哈夫曼树(Huffman Tree)

已知字符集{a,b,c,d,e,f}&#xff0c;若各字符出现的次数分别为6&#xff0c;3&#xff0c;8&#xff0c;2&#xff0c;10&#xff0c;4&#xff0c;则对应字符集中各字符的哈夫曼编码可能是&#xff08; &#xff09;。 A.00&#xff0c;1011&#xff0c;01&#xff0…

Eureka 介绍与原理详解

在微服务架构中&#xff0c;服务发现&#xff08;Service Discovery&#xff09;是一个至关重要的组件。随着服务数量的增加&#xff0c;手动管理服务的地址和端口变得不切实际。Eureka 是 Netflix 开源的一款服务发现工具&#xff0c;旨在解决微服务架构中的服务注册与发现问题…

C++和OpenGL实现3D游戏编程【连载19】——着色器光照初步(平行光和光照贴图)(附源码)

1、本节要实现的内容 我们在前期的教程中,讨论了在即时渲染模式下的光照内容。但在我们后期使用着色器的核心模式下,会经常在着色器中使光照,我们这里就讨论一下着色器光照效果,以及光照贴图效果,同时这里知识会为后期的更多光照效果做一些铺垫。本节我们首先讨论冯氏光照…

RedisDesktopManager新版本不再支持SSH连接远程redis后

背景 RedisDesktopManager(又名RDM)是一个用于Windows、Linux和MacOS的快速开源Redis数据库管理应用程序。这几天从新下载RedisDesktopManager最新版本&#xff0c;结果发现新版本开始不支持SSH连接远程redis了。 解决方案 第一种 根据网上有效的信息&#xff0c;可以回退版…