Android入门第53天-在Android手机里使用SQLite内嵌式数据库

news2025/1/20 3:51:52

介绍

Android内带SQLite内嵌式数据库了。这对于我们存储一些更复杂的结构化数据带来了极大的便利。比如说我们要存储应用内的常用联系人,购物车暂存信息,常量。必竟从xml或者是json里取数据都没有一条Select语句来得简单。

SQLite常用有五种数据类型:

  1. NULL
  2. INTEGER
  3. REAL(浮点数)
  4. TEXT(字符串文本)
  5. BLOB(二进制对象)

虽然只有五种,但是对于varchar,char等其他数据类型都是可以保存的,如下create table语句依然是可以生效的:

create table user(name varchar(20))

SQLite常用的三个类介绍

  • SQLiteOpenHelper:抽象类,我们通过继承该类,然后重写数据库创建以及更新的方法, 我们还可以通过该类的对象获得数据库实例,或者关闭数据库;
  • SQLiteDatabase:数据库访问类:我们可以通过该类的对象来对数据库做一些增删改查的操作;
  • Cursor:游标,有点类似于JDBC里的resultset,结果集!可以简单理解为指向数据库中某 一个记录的指针;

这三个类我们直接来看下面的Sample代码各位就知道是什么样的组合应用了。

private SQLiteDatabase db;
private Context context = null;
private DBOpenHelper dbOpenHelper;
private String DB_NAME="user.db";
private static final String DB_TABLE = "t_user_login";

dbOpenHelper = new DBOpenHelper(context, DB_NAME, null, DB_VERSION);
try {
     db = dbOpenHelper.getWritableDatabase();
     db.open();
     StringBuilder sqlStr = new StringBuilder();
     sqlStr.append("select loginId,password,user_name from ").append(DB_TABLE);
     Cursor cur = db.rawQuery(sqlStr.toString(), null);
     while (cur.moveToNext()) {
            String loginId = cur.getString(cur.getColumnIndexOrThrow("loginId"));
            String password = cur.getString(cur.getColumnIndexOrThrow("password"));
            String userName = cur.getString(cur.getColumnIndexOrThrow("user_name"));
            UserBean user = new UserBean();
            user.setLoginId(loginId);
            user.setPassword(password);
            user.setUserName(userName);
            userList.add(user);
    }
} catch (SQLiteException ex) {
     Log.e(TAG, ">>>>>>open db error: " + ex.getMessage(), ex);

}finally {
         try{
           db.close();
         }catch(Exception e){}
}

使用上和JDBC几乎一样,此处的Cursor相当于JDBC里的ResultSet。

敲黑板重要提醒-Android中如何正确使用db.open()/db.close()

在我们的例子里我们在每一个业务方法操作都会使用db.open一下,在finally块里db.close一下。

同时要在Activity的onStop方法中去dbclose()掉它。

记得,它就和水龙头一样,用完就要关。

SQLiteOpenHelper类的使用

这个类通常我们都要新建一个其它的类来extends这个类才能使用,主要的是这个类里有这么两个方法是很有用的。

public void onCreate

方法全签名:public void onCreate(SQLiteDatabase db)

这个类的作用就是当SQLiteOpenHelper被实例化时,第一次用来做“初始化数据库”用的。比如说我们有一个类如下

private static class DBOpenHelper extends SQLiteOpenHelper {

然后当你实例化这个类时

dbOpenHelper = new DBOpenHelper(context, DB_NAME, null, DB_VERSION);

它会自动触发这个onCreate方法,在onCreate方法中我们可以做表创建以及数据初始化操作如:

        private static final String DB_CREATE = "create table " +
                DB_TABLE + " (" + KEY_ID + " VARCHAR(20) primary key , " +
                KEY_PASSWORD + " text not null, " + KEY_NAME + " text not null);";

        @Override
        public void onCreate(SQLiteDatabase db) {
            Log.i(TAG, ">>>>>>execute create table->" + DB_CREATE);
            db.execSQL(DB_CREATE);
            StringBuilder initDataSql = new StringBuilder();
            initDataSql.append("INSERT INTO ").append(DB_TABLE).
                    append("(").append(KEY_ID).append(",").append(KEY_PASSWORD).
                    append(",").append(KEY_NAME).append(")").append("values(?,?,?)");
            Log.i(TAG, ">>>>>>execute initDataSql->" + initDataSql.toString());
            db.execSQL(initDataSql.toString(), new String[]{"root", "111111", "root"});
            Log.i(TAG, ">>>>>>db init successfully");
        }

public void onUpgrade

方法全签名:public void onUpgrade(SQLiteDatabase db, int _oldVersion, int _newVersion)

它有一个_newVersion这么一个参数,这个参数也很有意思。当你在实例化SQLiteOpenHelper类传入的DB_VERSION参数>就近一次实例化这个类传入的DB_VERSION参数时,它就会触发这个onUpgrade()方法,在这个方法里我们一般是根据业务场景的需要,没有一概而论该怎么办,通用的做法有:

  1. 重新执行一遍onCreate();此时所有的数据会被清空并初始化;
  2. 有些Android发版时不需要做全数据库清除,往往太过危险,而是会执行alter更改表结构、新建表、新插入-insert一些数据或者是稍带着update一些数据;

因此我才说,不一概而论而是需要依赖于你的实际业务场景来做操作。在我们的例子里我们会在onUpgrade里再执行一下onCreate()。

使用SQLite操作数据库方法介绍

这一块在使用上分两块。它类似Hibernate一样,也有API和原生SQL之分。

API的使用

即不需要书写SQL,如下样例:

    public long addItem(UserBean user) throws Exception {
        try {
            ContentValues newValues = new ContentValues();
            newValues.put(KEY_ID, user.getLoginId());
            newValues.put(KEY_PASSWORD, user.getPassword());
            newValues.put(KEY_NAME, user.getUserName());
            Log.i(TAG, "addItem successfully with loginId->" + user.getLoginId() + " password->" + user.getPassword());
            return db.insert(DB_TABLE, null, newValues);
        } catch (Exception e) {
            Log.e(TAG, "addItem error: " + e.getMessage(), e);
            throw new Exception("addItem error: " + e.getMessage(), e);
        }
    }

我们可以看到全程没有使用SQL。

原生SQL的使用

    public List<UserBean> queryAll() {
        List<UserBean> userList = new ArrayList<UserBean>();
        try {
            StringBuilder sqlStr = new StringBuilder();
            sqlStr.append("select loginId,password,user_name from ").append(DB_TABLE);
            Cursor cur = db.rawQuery(sqlStr.toString(), null);
            while (cur.moveToNext()) {
                String loginId = cur.getString(cur.getColumnIndexOrThrow("loginId"));
                String password = cur.getString(cur.getColumnIndexOrThrow("password"));
                String userName = cur.getString(cur.getColumnIndexOrThrow("user_name"));
                UserBean user = new UserBean();
                user.setLoginId(loginId);
                user.setPassword(password);
                user.setUserName(userName);
                userList.add(user);
            }
        } catch (Exception e) {
            Log.e(TAG, ">>>>>>queryAll error: " + e.getMessage(), e);
        }
        return userList;
    }

如何区分何时使用API何时使用原生SQL

如何区分呢?

答案很简单,以下是最佳实践 :

  1. 单表操作,就使用API好了;
  2. 跨表(>1个表)的操作或者是较复杂的逻辑如果使用API会产生循环套来套去不如写一条长点的SQL那么请直接使用原生SQL;

在样例前我们最后要了解一下,SQLite数据库管理工具。

SQLite数据库管理

Android里的SQLite一旦生成后它必须导出到AndroidStudio外部才能管理。按照如下步骤来导出SQLite数据库

先使用Device File Explorer打开data->data->你的包全路径下/databases,这里面有一个.db文件就是SQLite数据库文件。另一个.db-journal是Transaction日志,我们后面讲SQLite的Transaction时会讲到。

选择这个user.db右键选->save as,就可以导出到本地磁盘了。

然后你可以使用如下工具去管理SQLite数据库文件。

Windows下的SQLite管理工具

请使用SQLite Expert Professional,我使用的是5.4.4。不过这个是收费的,它长这个样但相当专业(收费的当然专业)。

 

MAC下的SQLite管理工具

请使用SQLiteManager,完全免费并且和Windows的SQLite Expert Professional收费的一样功能全。为什么呢?难道因为MAC贵。。。所以。。。有预收费之说:)?

好了,原理介绍完毕,我们要进入实践课程了。

课程目标

  1. 该APP启动会创建user.db数据库,新建一张t_user_login表并初始化一条root/111111数据进入内嵌SQLite数据库;
  2. 用户名密码输入root/111111点【登录】按钮可以得到登录成功,如果输入其它的不存在的帐户信息会以Toast显示“登录失败请校验你的用户名和密码”;
  3. 用户名密码输入任意值,点击【增加一条记录】按钮,可以插入一条数据到内嵌SQLite;
  4. 点击【查询所有记录】会以Log和Toast显示目前所有的相对应的表内的数据即:select *操作;
  5. 例子中对于单表的插入操作我们使用的是Android自带的SQLiteOpenHelper的原生API;
  6. 校验登录和查询所有记录我们用的是原生SQL+Cursor;

下面就进入代码部分吧。

前端代码

<?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"
    tools:context=".MainActivity">

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

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="用户登陆" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:text="请输入用户名" />

        <EditText
            android:id="@+id/editLoginid"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="用户名" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="请输入密码" />

        <EditText
            android:id="@+id/editPassword"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="密码"
            android:inputType="textPassword" />

        <Button
            android:id="@+id/buttonLogin"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="登录" />

        <View
            android:layout_width="match_parent"
            android:layout_height="1dp"
            android:background="#dfdfdf" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <Button
            android:id="@+id/buttonAddItem"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginRight="10dp"
            android:text="增加一条记录"/>
        <Button
            android:id="@+id/buttonQueryAll"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:text="查询所有记录"/>
    </LinearLayout>
</LinearLayout>

后端代码

 UserBean.java

package org.mk.android.demo.demosimplesqlite;

import java.io.Serializable;

public class UserBean implements Serializable {

    public String getLoginId() {
        return loginId;
    }

    public void setLoginId(String loginId) {
        this.loginId = loginId;
    }

    private String loginId = "";

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    private String password = "";
    private String userName = "";
}

 SQLite的核心操作-DBAdapter.java

package org.mk.android.demo.demosimplesqlite;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

import java.util.ArrayList;
import java.util.List;

public class DBAdapter {
    private static final String TAG = "DemoSQLite";
    private static final String DB_NAME = "user.db";
    private static final String DB_TABLE = "t_user_login";
    private static final int DB_VERSION = 2;

    public static final String KEY_ID = "loginId";
    public static final String KEY_PASSWORD = "password";
    public static final String KEY_NAME = "user_name";
    private SQLiteDatabase db;
    private Context context = null;
    private DBOpenHelper dbOpenHelper;

    public DBAdapter(Context ctx) {
        context = ctx;
    }

    public void close() {
        try {
            if (db != null) {
                db.close();
                db = null;
            }
        } catch (Exception e) {
        }
    }

    public void open()  {
        dbOpenHelper = new DBOpenHelper(context, DB_NAME, null, DB_VERSION);
        try {
            db = dbOpenHelper.getWritableDatabase();
        } catch (SQLiteException ex) {
            Log.e(TAG, ">>>>>>open db error: " + ex.getMessage(), ex);

        }
    }

    public long addItem(UserBean user) throws Exception {
        try {
            ContentValues newValues = new ContentValues();
            newValues.put(KEY_ID, user.getLoginId());
            newValues.put(KEY_PASSWORD, user.getPassword());
            newValues.put(KEY_NAME, user.getUserName());
            Log.i(TAG, "addItem successfully with loginId->" + user.getLoginId() + " password->" + user.getPassword());
            return db.insert(DB_TABLE, null, newValues);
        } catch (Exception e) {
            Log.e(TAG, "addItem error: " + e.getMessage(), e);
            throw new Exception("addItem error: " + e.getMessage(), e);
        }
    }

    public List<UserBean> queryAll() {
        List<UserBean> userList = new ArrayList<UserBean>();
        try {
            StringBuilder sqlStr = new StringBuilder();
            sqlStr.append("select loginId,password,user_name from ").append(DB_TABLE);
            Cursor cur = db.rawQuery(sqlStr.toString(), null);
            while (cur.moveToNext()) {
                String loginId = cur.getString(cur.getColumnIndexOrThrow("loginId"));
                String password = cur.getString(cur.getColumnIndexOrThrow("password"));
                String userName = cur.getString(cur.getColumnIndexOrThrow("user_name"));
                UserBean user = new UserBean();
                user.setLoginId(loginId);
                user.setPassword(password);
                user.setUserName(userName);
                userList.add(user);
            }
        } catch (Exception e) {
            Log.e(TAG, ">>>>>>queryAll error: " + e.getMessage(), e);
        }
        return userList;
    }

    public int checkLogin(String loginId, String pwd) {
        int result = 0;
        StringBuilder sqlStr = new StringBuilder();
        sqlStr.append("SELECT COUNT(*) FROM ").append(DB_TABLE).append(" WHERE ").append(KEY_ID).append("=? AND ").append(KEY_PASSWORD).append("=?");
        Log.i(TAG, ">>>>>>execute checkLogin SQL: " + sqlStr.toString());
        Log.i(TAG, ">>>>>>LoginId->" + loginId + " password->" + pwd);
        try {
            Cursor cur = db.rawQuery(sqlStr.toString(), new String[]{loginId, pwd});
            if (cur != null) {
                cur.moveToFirst();
                result = cur.getInt(0);
            }
        } catch (Exception e) {
            Log.e(TAG, "checkLogin dao error: " + e.getMessage(), e);
        }
        return result;
    }

    private static class DBOpenHelper extends SQLiteOpenHelper {

        public DBOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
            super(context, name, factory, version);
        }

        private static final String DB_CREATE = "create table " +
                DB_TABLE + " (" + KEY_ID + " VARCHAR(20) primary key , " +
                KEY_PASSWORD + " text not null, " + KEY_NAME + " text not null);";

        @Override
        public void onCreate(SQLiteDatabase db) {
            Log.i(TAG, ">>>>>>execute create table->" + DB_CREATE);
            db.execSQL(DB_CREATE);
            StringBuilder initDataSql = new StringBuilder();
            initDataSql.append("INSERT INTO ").append(DB_TABLE).
                    append("(").append(KEY_ID).append(",").append(KEY_PASSWORD).
                    append(",").append(KEY_NAME).append(")").append("values(?,?,?)");
            Log.i(TAG, ">>>>>>execute initDataSql->" + initDataSql.toString());
            db.execSQL(initDataSql.toString(), new String[]{"root", "111111", "root"});
            Log.i(TAG, ">>>>>>db init successfully");
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int _oldVersion, int _newVersion) {
            //db.execSQL("DROP TABLE IF EXISTS " + DB_TABLE);
            //onCreate(_db);
            db.execSQL("DROP TABLE IF EXISTS " + DB_TABLE);
            onCreate(db);

        }
    }
}

MainActivity.java

这个就来得相对简单了。

package org.mk.android.demo.demosimplesqlite;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;


public class MainActivity extends AppCompatActivity {
    private static final String TAG = "DemoSQLite";

    private SQLiteDatabase db;
    private Context context;
    private DBAdapter dbAdapter;

    private EditText editLoginId;
    private EditText editPassword;
    private Button buttonLogin;
    private Button buttonAddItem;
    private Button buttonQueryAll;
    private String strLoginId;
    private String strPassword;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        context = getApplicationContext();
        buttonLogin = (Button) findViewById(R.id.buttonLogin);
        buttonAddItem = (Button) findViewById(R.id.buttonAddItem);
        buttonQueryAll = (Button) findViewById(R.id.buttonQueryAll);
        editLoginId = (EditText) findViewById(R.id.editLoginid);
        editPassword = (EditText) findViewById(R.id.editPassword);
        dbAdapter = new DBAdapter(context);

        buttonLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                try {
                    dbAdapter.open();
                    strLoginId = editLoginId.getText().toString();
                    strPassword = editPassword.getText().toString();
                    int answer = dbAdapter.checkLogin(strLoginId, strPassword);
                    Log.i(TAG, ">>>>>>checkLogin is->" + answer);
                    if (answer != 1) {
                        Toast.makeText(context, "登录失败请校验你的用户名和密码", Toast.LENGTH_LONG).show();
                    } else {
                        Toast.makeText(context, "登录成功", Toast.LENGTH_LONG).show();
                    }
                } catch (Exception e) {
                    Log.e(TAG, ">>>>>>checkLogin error: " + e.getMessage(), e);
                } finally {
                    dbAdapter.close();
                }
            }
        });
        buttonAddItem.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                try {
                    dbAdapter.open();
                    strLoginId = editLoginId.getText().toString();
                    strPassword = editPassword.getText().toString();
                    UserBean user = new UserBean();
                    user.setLoginId(strLoginId);
                    user.setPassword(strPassword);
                    user.setUserName(strLoginId);

                    long result = dbAdapter.addItem(user);
                    Toast.makeText(context, "增加一条数据成功", Toast.LENGTH_LONG).show();

                } catch (Exception e) {
                    Log.e("TAG", ">>>>>>addItem error: " + e.getMessage(), e);
                } finally {
                    dbAdapter.close();
                }
            }
        });
        buttonQueryAll.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                try {
                    dbAdapter.open();
                    StringBuilder sb = new StringBuilder();
                    List<UserBean> userList = new ArrayList<UserBean>();
                    userList = dbAdapter.queryAll();
                    if (userList != null && userList.size() > 0) {
                        userList.forEach(v -> {
                            sb.append("loginId->").append(v.getLoginId())
                                    .append("  password->").append(v.getPassword())
                                    .append(" userName->").append(v.getUserName())
                                    .append("\n");
                        });
                        Log.i(TAG,sb.toString());
                        Toast.makeText(context, "查询所有数据\n" + sb.toString(), Toast.LENGTH_LONG).show();

                    }
                } catch (Exception e) {
                    Log.e(TAG, ">>>>>>queryAll error: " + e.getMessage(), e);
                } finally {
                    dbAdapter.close();
                }
            }
        });
    }

    @Override
    protected void onStart(){
        super.onStart();
        dbAdapter.open();
    }
    @Override
    protected void onStop() {
        super.onStop();
        dbAdapter.close();
    }
}

运行效果

 自己动一下手试试吧。

 

 

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

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

相关文章

第04讲:Redis消息的发布和订阅

一、什么是消息的发布和订阅 Redis 发布订阅 (pub/sub) 是一种消息通信模式&#xff1a;发送者 (pub) 发送消息&#xff0c;订阅者 (sub) 接收消息。 Tip&#xff1a;Redis 客户端&#xff08;redis-cli&#xff09;可以订阅任意数量的频道。 二、Redis的发布和订阅的原理 客…

ssh端口转发

ssh端口转发ssh端口转发Centos7关闭IPV6sshd服务端sshd_config配置调整(非必需)优化sshd_config配置(非必需)调整ssh客户端配置~/.ssh/config ---必须ssh终端转发socks5端口ssh终端命令行转发到socks5ssh_config配置端口转发SOCKS5使用ssh将后端的服务端口转发到localssh端口转…

selenium网络爬虫去哪儿机票利用performance获取日志截获加载的xhr,ajax,js等数据

这次练习获取的网站使用了许多反爬技术&#xff1a; 1.html页面使用了css字体偏移 2.xhr加载有webdriver反爬检测 3.请求接口使用了多项加密参数以及cookie验证 4.部分js代码用了ob混淆 一开始只是想学习练手一下css偏移学习后是解决了&#xff0c;但想获取页面源代码时候遇…

Apache Traffic Server 存在拒绝服务漏洞

漏洞描述 Apache Traffic Serve&#xff08;ATS&#xff09; 是一个开源的 HTTP/1.1 和 HTTP/2 缓存代理服务器&#xff0c;uri_signing 是 ATS 的一款 URI 签名插件&#xff0c;用来阻止所有不具有有效 JWT 的请求。 Apache Traffic Server 的受影响版本中存在拒绝服务漏洞&…

人工神经网络 ANN 基础概念

目录 一&#xff1a;简介 二&#xff1a;感知器 三&#xff1a;权重和阈值 四&#xff1a;多层前馈网络 五&#xff1a;BP神经网络 一&#xff1a;简介 机器学习是实现人工智能的方法&#xff0c;深度学习是实现机器学习的技术。在实现人工智能时需要人工辅助(半自动)&…

图像分类:Pytorch图像分类之--LetNet模型

文章目录前言LetNet简介程序的实现model.py的实现LetNet模型操作流程经过Conv卷积后输出尺寸的计算公式如下Conv2d()函数介绍MaxPool2d&#xff08;&#xff09;函数介绍Tensor的展平&#xff1a;view()train.py导入数据集加载数据集参数设置训练数据保存模型train_tool.pypred…

前端基础(十四)_隐藏元素的方法

隐藏元素的方法 1、display 通过display:none来控制元素隐藏 不使用&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge">…

以数据驱动的自动驾驶时代,还有多远?丨曼孚科技

自动驾驶即将来到数据驱动时代。 在经历了硬件(激光雷达)、软件(多传感器融合)等技术革新后&#xff0c;自动驾驶迎来了第三波浪潮。 去年&#xff0c;除造车新势力外&#xff0c;国内一批主机厂吉利、上汽、广汽等&#xff0c;纷纷加大了自动驾驶数据标注业务投入。到今年&a…

参加 Spartacus 开源项目开发时需要注意的一些编程规范

我们使用 NgRx 存储来管理 Spartacus 功能中的全局应用程序状态。 使用 NgRx 在性能、更好的可测试性和易于故障排除、方面具有明显的优势。 除非有令人信服的理由不这样做&#xff0c;否则在某项 feature 的开发里&#xff0c;请总是使用 Rgrx 来管理状态。 使用 Store 并不…

PS CS6视频剪辑基本技巧(二)视频剪接和添加图片

上一讲&#xff0c;介绍了PS CS6可以实现视频剪接、添加图片、添加声音、添加字幕、添加logo、添加动画等6种功能&#xff0c;今天这讲介绍一下视频剪接和添加图片这两个功能。 目录 一、基本操作 1、打开时间轴窗口 2、创建时间轴 二、视频剪接 1、打开已存在的视频文件…

c++visualStudio学习笔记

文章目录5 c如何工作5.1include5.2main5.3 <<5.3 linker链接器7 C链接器linker是如何工作的7.1案例10 头函数10.1案例10.2头文件10.3pragma once34const34.1 常数34.2指针34.3类和方法34.4引用34.5mutable5 c如何工作 源文件就是文本文件转化到可执行的二进制文件或者程…

MySQL自增主键一定是连续的吗

测试环境&#xff1a; MySQL版本&#xff1a;8.0 数据库表&#xff1a;T &#xff08;主键id&#xff0c;唯一索引c&#xff0c;普通字段d&#xff09; 如果你的业务设计依赖于自增主键的连续性&#xff0c;这个设计假设自增主键是连续的。但实际上&#xff0c;这样的假设是错…

一个大二计算机学生的学期总结(2022末年)

学期总结前言一、本学期的自我反思&#xff08;1&#xff09;返校之前在家期间练习题黑马头条教学管理平台Ajax练习仿写JD&#xff08;2&#xff09;返校之后在学校期间练习题本学期课程hbase、hive、Hadoop一直奉行的话前言 ☀️东流逝水&#xff0c;叶落纷纷&#xff0c;荏苒…

初级程序员 中级程序员 高级程序员级别划分

网络上众说纷纭&#xff0c;大都站立在对某一技术的知识掌握量上来界定&#xff0c;或者有些人说着说着&#xff0c;发现无法自圆其说&#xff0c;然后就说这三种人就是一种叫法&#xff0c;你觉得你是高级你就高级了&#xff0c;从概念上讲&#xff0c;这三者之间必然有质的区…

20221221查看RK3588开发板的LCD屏幕的分辨率(1536x2048)

20221221查看RK3588开发板的LCD屏幕的分辨率 2022/12/21 18:46 方法一&#xff1a;adb/串口 console:/ $ console:/ $ [ 37.217258][ T57] vbus5v0_typec: disabling [ 37.217707][ T57] vcc3v3_pcie20: disabling console:/ $ console:/ $ console:/ $ dumpsys wi…

我国输配电及控制设备行业现状及竞争分析 营收及企业规模整体增长

根据观研报告网发布的《中国输配电设备行业发展趋势研究与未来投资分析报告&#xff08;2022-2029年&#xff09;》显示&#xff0c;输配电及控制设备主要应用于电力系统和下游用电企业的电能传输和电能控制等&#xff0c;直接影响电网的建设、安全与可靠运行&#xff0c;输配电…

Go:使用 go-micro 构建微服务(二)

1. 什么是go micro# 1.1 go micro作用 它是一个可插入的RPC框架&#xff0c;用于在Go中编写微服务。开箱即用&#xff0c;您将收到&#xff1a; 服务发现: 应用程序自动注册到服务发现系统负载平衡: 客户端负载平衡&#xff0c;用于平衡服务实例之间的请求同步通信: 提供请求…

12月21日:fastadmin顶部搜索栏selectpage事件绑定以及实现添加表单和查询数据在同一个页面

fastadmin将添加表单从弹窗转为页面 如图所示&#xff0c;实现这种两个标签页&#xff0c;各自执行自己的操作 添加页从弹窗转为页面实现步骤&#xff1a; 首先&#xff1a;从前端页面来看&#xff0c;需要将添加的功能移动到index页面&#xff0c;即可初步实现 将Index页面如…

l2实时接口在手机APP上的使用方法介绍

考虑到很多人白天只能在手机上看行情&#xff0c;因此也重点介绍一下l2实时接口在手机APP上的使用方法&#xff0c;但是功能上PC才是最直观的。除了本文介绍的重点功能&#xff0c;Level2还有其他功能&#xff0c;具体使用方法可参考同花顺官方介绍。 利用“BBD”看当天大盘走…

R语言中的风险价值模型度量指标TVaR与VaR

99&#xff05;的预期缺口[…]与99.6&#xff05;的[…]风险值非常接近 受到“ 瑞士经验”报告中一句话的启发&#xff0c; 在99&#xff05;置信水平[...]上的预期缺口[…]对应于大约99.6&#xff05;至99.8&#xff05;的风险价值 最近我们被客户要求撰写关于VaR的研究报告…