GreenDao3.2使用详解(增,删,改,查,升级)
首先看一下效果图:
项目结构如下图所示:
第一步:在build中添加配置如下:
projet 目录下的build.gradle
dependencies {
     
        classpath 'org.greenrobot:greendao-gradle-plugin:3.2.0'
    }
moudle 下的build.gradle添加如下内容:
apply plugin: 'org.greenrobot.greendao'
 
 dependencies {
    .....................
	
    compile 'org.greenrobot:greendao:3.2.0'
}
添加完成之后点击sync一下工程
第二步:
首先在moudle下 build.gradle的根目录下添加如下配置
greendao {
    //数据库的schema版本,也可以理解为数据库版本号
    schemaVersion 1
    //设置DaoMaster、DaoSession、Dao包名,也就是要放置这些类的包的全路径。
    daoPackage 'com.koimy.greedaotest.dao'
    //设置DaoMaster、DaoSession、Dao目录
    targetGenDir 'src/main/java'
}
在项目里面添加实体类Student如下所示:
@Entity
public class Student {
    @Id
    private Long id;
    private String name;
    private int age;
    private String num;
}
此时项目目录如下所示:
然后点击 android studio 的 build-->make project
此时项目结构如下:
其中dao目录下的文件是由greendao自动生成的,Student 类里面的内容发生了编号如下:
/**
 * Created by hp on 2018/1/23.
 */
@Entity
public class Student {
    @Id
    private Long id;
    private String name;
    private int age;
    private String num;
    @Generated(hash = 1538557423)
    public Student(Long id, String name, int age, String num) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.num = num;
    }
    @Generated(hash = 1556870573)
    public Student() {
    }
    public Long getId() {
        return this.id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getName() {
        return this.name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return this.age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getNum() {
        return this.num;
    }
    public void setNum(String num) {
        this.num = num;
    }
GreenDao帮助我们自动生成了一些get和set方法,以及构造方法
第三步:为了方便使用我们添加了一个 db包
DbManager类如下:
package com.koimy.greedaotest.db;
 
/**
 * Created by hp on 2018/1/23.
 */
 
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
 
import com.koimy.greedaotest.dao.DaoMaster;
import com.koimy.greedaotest.dao.DaoSession;
 
 
public class DbManager {
 
    // 是否加密
    public static final boolean ENCRYPTED = true;
 
    private static final String DB_NAME = "test.db";
    private static DbManager mDbManager;
    private static DaoMaster.DevOpenHelper mDevOpenHelper;
    private static DaoMaster mDaoMaster;
    private static DaoSession mDaoSession;
 
    private Context mContext;
 
    private DbManager(Context context) {
        this.mContext = context;
        // 初始化数据库信息
        mDevOpenHelper = new DaoMaster.DevOpenHelper(context, DB_NAME);
        getDaoMaster(context);
        getDaoSession(context);
    }
 
    public static DbManager getInstance(Context context) {
        if (null == mDbManager) {
            synchronized (DbManager.class) {
                if (null == mDbManager) {
                    mDbManager = new DbManager(context);
                }
            }
        }
        return mDbManager;
    }
 
    /**
     * 获取可读数据库
     *
     * @param context
     * @return
     */
    public static SQLiteDatabase getReadableDatabase(Context context) {
        if (null == mDevOpenHelper) {
            getInstance(context);
        }
        return mDevOpenHelper.getReadableDatabase();
    }
 
    /**
     * 获取可写数据库
     *
     * @param context
     * @return
     */
    public static SQLiteDatabase getWritableDatabase(Context context) {
        if (null == mDevOpenHelper) {
            getInstance(context);
        }
 
        return mDevOpenHelper.getWritableDatabase();
    }
 
    /**
     * 获取DaoMaster
     *
     * @param context
     * @return
     */
    public static DaoMaster getDaoMaster(Context context) {
        if (null == mDaoMaster) {
            synchronized (DbManager.class) {
                if (null == mDaoMaster) {
                    mDaoMaster = new DaoMaster(getWritableDatabase(context));
                }
            }
        }
        return mDaoMaster;
    }
 
//    /**
//     * 获取DaoMaster
//     *
//     * 判断是否存在数据库,如果没有则创建数据库
//     * @param context
//     * @return
//     */
//    public static DaoMaster getDaoMaster(Context context) {
//        if (null == mDaoMaster) {
//            synchronized (DbManager.class) {
//                if (null == mDaoMaster) {
//                    MyOpenHelper helper = new MyOpenHelper(context,DB_NAME,null);
//                    mDaoMaster = new DaoMaster(helper.getWritableDatabase());
//                }
//            }
//        }
//        return mDaoMaster;
//    }
 
    /**
     * 获取DaoSession
     *
     * @param context
     * @return
     */
    public static DaoSession getDaoSession(Context context) {
        if (null == mDaoSession) {
            synchronized (DbManager.class) {
                mDaoSession = getDaoMaster(context).newSession();
            }
        }
        return mDaoSession;
    }
}
SutdentDaoOpe类如下:
package com.koimy.greedaotest.db;
 
/**
 * Created by hp on 2018/1/23.
 */
 
import android.content.Context;
 
 
import com.koimy.greedaotest.dao.StudentDao;
import com.koimy.greedaotest.entity.Student;
 
import org.greenrobot.greendao.query.QueryBuilder;
 
import java.util.List;
 
 
public class StudentDaoOpe {
 
    /**
     * 添加数据至数据库
     *
     * @param context
     * @param stu
     */
    public static void insertData(Context context, Student stu) {
        DbManager.getDaoSession(context).getStudentDao().insert(stu);
    }
 
 
    /**
     * 将数据实体通过事务添加至数据库
     *
     * @param context
     * @param list
     */
    public static void insertData(Context context, List<Student> list) {
        if (null == list || list.size() <= 0) {
            return;
        }
        DbManager.getDaoSession(context).getStudentDao().insertInTx(list);
    }
 
    /**
     * 添加数据至数据库,如果存在,将原来的数据覆盖
     * 内部代码判断了如果存在就update(entity);不存在就insert(entity);
     *
     * @param context
     * @param student
     */
    public static void saveData(Context context, Student student) {
        DbManager.getDaoSession(context).getStudentDao().save(student);
    }
 
    /**
     * 删除数据至数据库
     *
     * @param context
     * @param student 删除具体内容
     */
    public static void deleteData(Context context, Student student) {
        DbManager.getDaoSession(context).getStudentDao().delete(student);
    }
 
    /**
     * 根据id删除数据至数据库
     *
     * @param context
     * @param id      删除具体内容
     */
    public static void deleteByKeyData(Context context, long id) {
        DbManager.getDaoSession(context).getStudentDao().deleteByKey(id);
    }
 
    /**
     * 删除全部数据
     *
     * @param context
     */
    public static void deleteAllData(Context context) {
        DbManager.getDaoSession(context).getStudentDao().deleteAll();
    }
 
    /**
     * 更新数据库
     *
     * @param context
     * @param student
     */
    public static void updateData(Context context, Student student) {
        DbManager.getDaoSession(context).getStudentDao().update(student);
    }
 
 
    /**
     * 查询所有数据
     *
     * @param context
     * @return
     */
    public static List<Student> queryAll(Context context) {
        QueryBuilder<Student> builder = DbManager.getDaoSession(context).getStudentDao().queryBuilder();
 
        return builder.build().list();
    }
 
 
 
    /**
     *  分页加载
     * @param context
     * @param pageSize 当前第几页(程序中动态修改pageSize的值即可)
     * @param pageNum  每页显示多少个
     * @return
     */
    public static List<Student> queryPaging( int pageSize, int pageNum,Context context){
         StudentDao studentDao = DbManager.getDaoSession(context).getStudentDao();
        List<Student> listMsg = studentDao.queryBuilder()
                .offset(pageSize * pageNum).limit(pageNum).list();
        return listMsg;
    }
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.koimy.demo2.MainActivity">
 
 
    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="增" />
 
    <Button
        android:id="@+id/button2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="删" />
 
    <Button
        android:id="@+id/button3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="改" />
 
    <Button
        android:id="@+id/button4"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="查" />
 
    <LinearLayout
        android:layout_marginTop="20dp"
        android:gravity="center"
        android:layout_gravity="center_vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
 
        <Button
            android:id="@+id/btn_query_all"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="根据ID查询(分页查询)" />
 
        <Button
            android:id="@+id/button5"
            android:layout_width="107dp"
            android:layout_height="wrap_content"
            android:text="删除全部" />
    </LinearLayout>
 
    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
 
        <TextView
            android:id="@+id/tv_content"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </ScrollView>
 
</LinearLayout>
MainActivithy.java
package com.koimy.greedaotest;
 
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
 
import com.koimy.greedaotest.db.StudentDaoOpe;
import com.koimy.greedaotest.entity.Student;
 
import java.util.ArrayList;
import java.util.List;
 
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
 
public class MainActivity extends AppCompatActivity {
 
    @BindView(R.id.button)
    Button button;
    @BindView(R.id.button2)
    Button button2;
    @BindView(R.id.button3)
    Button button3;
    @BindView(R.id.button4)
    Button button4;
    @BindView(R.id.btn_query_all)
    Button btnQueryAll;
    @BindView(R.id.button5)
    Button button5;
    @BindView(R.id.tv_content)
    TextView tvContent;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        initData();
    }
 
    private List<Student> studentList = new ArrayList<>();
 
    /**
     * 初始化数据
     */
    private void initData() {
        for (int i = 0; i < 100; i++) {
            Student student = new Student((long) i, "huang" + i, 25,"666"+i);
            studentList.add(student);
        }
 
    }
 
    int page;
 
    @OnClick({R.id.button, R.id.button2, R.id.button3, R.id.button4, R.id.button5, R.id.btn_query_all})
    public void onViewClicked(View view) {
        switch (view.getId()) {
            case R.id.button:
                StudentDaoOpe.insertData(this, studentList);
                break;
            case R.id.button2:
                Student student = new Student((long) 5, "haung" + 5, 25,"123456");
                /**
                 * 根据特定的对象删除
                 */
                StudentDaoOpe.deleteData(this, student);
                /**
                 * 根据主键删除
                 */
                StudentDaoOpe.deleteByKeyData(this, 7);
                StudentDaoOpe.deleteAllData(this);
                break;
            case R.id.button3:
                student = new Student((long) 2, "caojin", 1314,"888888");
                StudentDaoOpe.updateData(this, student);
                break;
            case R.id.button4:
                List<Student> students = StudentDaoOpe.queryAll(this);
                tvContent.setText(students.toString());
                for (int i = 0; i < students.size(); i++) {
                    Log.i("Log", students.get(i).getName());
                }
 
                break;
            case R.id.button5:
                StudentDaoOpe.deleteAllData(this);
                break;
            case R.id.btn_query_all:
                List<Student> students2 = StudentDaoOpe.queryPaging(page, 20, this);
 
                if (students2.size() == 0) {
                    Toast.makeText(this, "没有更多数据了", Toast.LENGTH_SHORT).show();
                }
                for (Student st : students2) {
                    Log.e("TAG", "onViewClicked: ==" + st);
                    Log.e("TAG", "onViewClicked: == num = " + st.getNum());
                }
                page++;
                tvContent.setText(students2.toString());
                break;
        }
    }
 
 
}
最终在/data/data/包名/databases/数据库名/表名 生成的表结构如下所示:
下面讲一下数据库的升级:
首先新建一个helper包引入两个类,内容如下:
MigrationHelper类:
package com.koimy.greedaotest.helper;
 
/**
 * 数据库更新辅助类
 *
 * 原理:创建临时表-->删除原表-->创建新表-->复制临时表数据到新表并删除临时表;这样数据库表的更新就完成了
 */
 
import android.database.Cursor;
import android.text.TextUtils;
import android.util.Log;
 
 
import com.koimy.greedaotest.dao.DaoMaster;
 
import org.greenrobot.greendao.AbstractDao;
import org.greenrobot.greendao.database.Database;
import org.greenrobot.greendao.internal.DaoConfig;
 
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
 
 
public class MigrationHelper {
 
    private static final String CONVERSION_CLASS_NOT_FOUND_EXCEPTION = "MIGRATION HELPER - CLASS DOESN'T MATCH WITH THE CURRENT PARAMETERS";
    private static MigrationHelper instance;
 
    public static MigrationHelper getInstance() {
        if (instance == null) {
            instance = new MigrationHelper();
        }
        return instance;
    }
 
    public void migrate(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
 
        generateTempTables(db, daoClasses);
        DaoMaster.dropAllTables(db, true);
        DaoMaster.createAllTables(db, false);
        restoreData(db, daoClasses);
    }
 
    /**
     * 生成临时列表
     *
     * @param db
     * @param daoClasses
     */
    private void generateTempTables(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
        for (int i = 0; i < daoClasses.length; i++) {
            DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]);
 
            String divider = "";
            String tableName = daoConfig.tablename;
            String tempTableName = daoConfig.tablename.concat("_TEMP");
            ArrayList<String> properties = new ArrayList<>();
 
            StringBuilder createTableStringBuilder = new StringBuilder();
 
            createTableStringBuilder.append("CREATE TABLE ").append(tempTableName).append(" (");
 
            for (int j = 0; j < daoConfig.properties.length; j++) {
                String columnName = daoConfig.properties[j].columnName;
 
                if (getColumns(db, tableName).contains(columnName)) {
                    properties.add(columnName);
 
                    String type = null;
 
                    try {
                        type = getTypeByClass(daoConfig.properties[j].type);
                    } catch (Exception exception) {
                        exception.printStackTrace();
                    }
 
                    createTableStringBuilder.append(divider).append(columnName).append(" ").append(type);
 
                    if (daoConfig.properties[j].primaryKey) {
                        createTableStringBuilder.append(" PRIMARY KEY");
                    }
 
                    divider = ",";
                }
            }
            createTableStringBuilder.append(");");
 
            db.execSQL(createTableStringBuilder.toString());
 
            StringBuilder insertTableStringBuilder = new StringBuilder();
 
            insertTableStringBuilder.append("INSERT INTO ").append(tempTableName).append(" (");
            insertTableStringBuilder.append(TextUtils.join(",", properties));
            insertTableStringBuilder.append(") SELECT ");
            insertTableStringBuilder.append(TextUtils.join(",", properties));
            insertTableStringBuilder.append(" FROM ").append(tableName).append(";");
 
            db.execSQL(insertTableStringBuilder.toString());
 
        }
    }
 
    /**
     * 存储新的数据库表 以及数据
     *
     * @param db
     * @param daoClasses
     */
    private void restoreData(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
        for (int i = 0; i < daoClasses.length; i++) {
            DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]);
            String tableName = daoConfig.tablename;
            String tempTableName = daoConfig.tablename.concat("_TEMP");
            ArrayList<String> properties = new ArrayList();
 
            for (int j = 0; j < daoConfig.properties.length; j++) {
                String columnName = daoConfig.properties[j].columnName;
 
                if (getColumns(db, tempTableName).contains(columnName)) {
                    properties.add(columnName);
                }
            }
 
            StringBuilder insertTableStringBuilder = new StringBuilder();
 
            insertTableStringBuilder.append("INSERT INTO ").append(tableName).append(" (");
            insertTableStringBuilder.append(TextUtils.join(",", properties));
            insertTableStringBuilder.append(") SELECT ");
            insertTableStringBuilder.append(TextUtils.join(",", properties));
            insertTableStringBuilder.append(" FROM ").append(tempTableName).append(";");
 
            StringBuilder dropTableStringBuilder = new StringBuilder();
            dropTableStringBuilder.append("DROP TABLE ").append(tempTableName);
            db.execSQL(insertTableStringBuilder.toString());
            db.execSQL(dropTableStringBuilder.toString());
        }
    }
 
    private String getTypeByClass(Class<?> type) throws Exception {
        if (type.equals(String.class)) {
            return "TEXT";
        }
        if (type.equals(Long.class) || type.equals(Integer.class) || type.equals(long.class)) {
            return "INTEGER";
        }
        if (type.equals(Boolean.class)) {
            return "BOOLEAN";
        }
 
        Exception exception = new Exception(CONVERSION_CLASS_NOT_FOUND_EXCEPTION.concat(" - Class: ").concat(type.toString()));
        exception.printStackTrace();
        throw exception;
    }
 
    private List<String> getColumns(Database db, String tableName) {
        List<String> columns = new ArrayList<>();
        Cursor cursor = null;
        try {
            cursor = db.rawQuery("SELECT * FROM " + tableName + " limit 1", null);
            if (cursor != null) {
                columns = new ArrayList<>(Arrays.asList(cursor.getColumnNames()));
            }
        } catch (Exception e) {
            Log.v(tableName, e.getMessage(), e);
            e.printStackTrace();
        } finally {
            if (cursor != null)
                cursor.close();
        }
        return columns;
    }
}
MyOpenHelper类:
package com.koimy.greedaotest.helper;
 
/**
 * Created by hp on 2018/1/23.
 */
 
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
 
 
import com.koimy.greedaotest.dao.DaoMaster;
import com.koimy.greedaotest.dao.StudentDao;
 
import org.greenrobot.greendao.database.Database;
 
 
public class MyOpenHelper extends DaoMaster.OpenHelper {
 
    public MyOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) {
        super(context, name, factory);
    }
 
    /**
     * 数据库升级
     * @param db
     * @param oldVersion
     * @param newVersion
     */
    @Override
    public void onUpgrade(Database db, int oldVersion, int newVersion) {
        //操作数据库的更新 有几个表升级都可以传入到下面
 
        Log.i("version", oldVersion + "---先前和更新之后的版本---" + newVersion);
        if (oldVersion < newVersion) {
            Log.i("version", oldVersion + "---先前和更新之后的版本---" + newVersion);
            MigrationHelper.getInstance().migrate(db, StudentDao.class);
            //更改过的实体类(新增的不用加)   更新UserDao文件 可以添加多个  XXDao.class 文件
//             MigrationHelper.getInstance().migrate(db, UserDao.class,XXDao.class);
        }
 
 
 
        //MigrationHelper.getInstance().migrate(db,StudentDao.class);
    }
 
}
然后将
    /**
     * 获取DaoMaster
     *
     * @param context
     * @return
     */
    public static DaoMaster getDaoMaster(Context context) {
        if (null == mDaoMaster) {
            synchronized (DbManager.class) {
                if (null == mDaoMaster) {
                    mDaoMaster = new DaoMaster(getWritableDatabase(context));
                }
            }
        }
        return mDaoMaster;
    }
换成如下所示:
/**
     * 获取DaoMaster
     *
     * 判断是否存在数据库,如果没有则创建数据库
     * @param context
     * @return
     */
    public static DaoMaster getDaoMaster(Context context) {
        if (null == mDaoMaster) {
            synchronized (DbManager.class) {
                if (null == mDaoMaster) {
                    MyOpenHelper helper = new MyOpenHelper(context,DB_NAME,null);
                    mDaoMaster = new DaoMaster(helper.getWritableDatabase());
                }
            }
        }
        return mDaoMaster;
    }
接下来:我们在Student类里面添加一个 属性 grade属性,然后将build.gradle里面schemaVersion改为 2
然后编译,运行一下程序,然后我们再来看升级后的数据库,如下图所示成功添加了我么新增的字段,还保留了以前的数据
GreenDao3.2使用详解(增,删,改,查,升级)的更多相关文章
- 好用的SQL TVP~~独家赠送[增-删-改-查]的例子
		
以前总是追求新东西,发现基础才是最重要的,今年主要的目标是精通SQL查询和SQL性能优化. 本系列主要是针对T-SQL的总结. [T-SQL基础]01.单表查询-几道sql查询题 [T-SQL基础] ...
 - iOS FMDB的使用(增,删,改,查,sqlite存取图片)
		
iOS FMDB的使用(增,删,改,查,sqlite存取图片) 在上一篇博客我对sqlite的基本使用进行了详细介绍... 但是在实际开发中原生使用的频率是很少的... 这篇博客我将会较全面的介绍FM ...
 - iOS sqlite3 的基本使用(增 删 改 查)
		
iOS sqlite3 的基本使用(增 删 改 查) 这篇博客不会讲述太多sql语言,目的重在实现sqlite3的一些基本操作. 例:增 删 改 查 如果想了解更多的sql语言可以利用强大的互联网. ...
 - django ajax增 删 改 查
		
具于django ajax实现增 删 改 查功能 代码示例: 代码: urls.py from django.conf.urls import url from django.contrib impo ...
 - ADO.NET 增 删 改 查
		
ADO.NET:(数据访问技术)就是将C#和MSSQL连接起来的一个纽带 可以通过ADO.NET将内存中的临时数据写入到数据库中 也可以将数据库中的数据提取到内存中供程序调用 ADO.NET所有数据访 ...
 - MVC EF 增 删 改 查
		
using System;using System.Collections.Generic;using System.Linq;using System.Web;//using System.Data ...
 - python基础中的四大天王-增-删-改-查
		
列表-list-[] 输入内存储存容器 发生改变通常直接变化,让我们看看下面列子 增---默认在最后添加 #append()--括号中可以是数字,可以是字符串,可以是元祖,可以是集合,可以是字典 #l ...
 - 简单的php数据库操作类代码(增,删,改,查)
		
这几天准备重新学习,梳理一下知识体系,同时按照功能模块划分做一些东西.所以.mysql的操作成为第一个要点.我写了一个简单的mysql操作类,实现数据的简单的增删改查功能. 数据库操纵基本流程为: 1 ...
 - MongoDB增 删 改 查
		
增 增加单篇文档 > db.stu.insert({sn:'001', name:'lisi'}) WriteResult({ "nInserted" : 1 }) > ...
 - Go语言之进阶篇mysql增 删 改 查
		
一.mysql操作基本语法 1.创建名称nulige的数据库 CREATE DATABASE nulige DEFAULT CHARSET utf8 COLLATE utf8_general_ci; ...
 
随机推荐
- HDU - 5115 Dire Wolf (非原创)
			
Dire wolves, also known as Dark wolves, are extraordinarily large and powerful wolves. Many, if not ...
 - 使用nodejs爬取图片
			
在运行代码前,请确保本机是否有nodejs环境 1 D:\ > node -v 2 v12.1.0 //版本号 需要用到的包 axios //请求页面 cheerio // 把get请求的页面 ...
 - vuepress config favicon
			
vuepress config favicon .vuepress/public favicons https://vuepress.vuejs.org/guide/assets.html#publi ...
 - React useEffect in depth
			
React useEffect in depth useEffect class DogInfo extends React.Component { controller = null state = ...
 - W3Schools Quizzes
			
W3Schools Quizzes Test your skills https://www.w3schools.com/quiztest/default.asp Quiz HOME Quiz HTM ...
 - Apache HTTP Server & WS (websockets)
			
Apache HTTP Server & WS (websockets) Apache HTTP Server Version 2.4 https://httpd.apache.org/doc ...
 - css整理之-----------布局相关
			
文档流 文档流指的是元素排版布局过程中,元素会默认自动从左往右,从上往下的流式排列方式布局,文档流可以分为定位流.浮动流.普通流三种 普通流(Normal flow) 在常规流中,盒一个接着一个排列, ...
 - 字节码增强技术-Byte Buddy
			
本文转载自字节码增强技术-Byte Buddy 为什么需要在运行时生成代码? Java 是一个强类型语言系统,要求变量和对象都有一个确定的类型,不兼容类型赋值都会造成转换异常,通常情况下这种错误都会被 ...
 - 进阶高阶IoT架构-教你如何简单实现一个消息队列
			
前言 消息队列是软件系统领域用来实现系统间通信最广泛的中间件.基于消息队列的方式是指由应用中的某个系统负责发送消息,由关心这条消息的相关系统负责接收消息,并在收到消息后进行各自系统内的业务处理.消息可 ...
 - Tawk.to工具使用攻略--如何让访客通过tawk.to联系时先填写邮箱、联系方式等信息
			
Tawk.to工具使用攻略--如何让访客通过tawk.to联系时先填写邮箱.联系方式等信息 您在使用tawk.to的时候,是否遇到当访客联系您时,您未能及时查看到tawk.to的消息,后续想要再联系访 ...