• SQLite原理与运用



    title: SQLite原理与运用
    tags:

    • Android
    • SQLite
    • ORM框架
      categories: 移动开发
      abbrlink: 43941
      date: 2020-12-03 12:10:00

    SQLite是一个开源的关系型数据库,实现自包容、零配置、支持事务的SQL数据库引擎。其特点是高度便携、使用方便、结构紧凑、高效、可靠。并且SQLite是在世界上最广泛部署的 SQL 数据库引擎。SQLite 源代码不受版权限制。 本篇文章书要是记述了SQLite的基本架构以及SQLite的几种操作方式,其中比较重要的就是ADB Shell命令操作与SQL语句,另外在开发中还是Litepal这款开源ORM框架用的比较多一些,使用起来确实非常方便。

    SQLite基本结构

    接口由SQLite C API组成,程序与SQLite交互的基础就是用C语言编写的API,JDBC也只是JNI调用而已。

    在编译器中,词法分析器与语法分析器把SQL翻译为语法树,Code Generator根据语法树生产SQLite的汇编代码,交给虚拟机执行。

    虚拟机,与Java虚拟机执行class中的指令类似,SQLite的汇编代码由SQLite虚拟机来执行,由虚拟机负责SQL到数据存取的交互,关于虚拟机的更多内容可以查看官网《The Virtual Database Engine of SQLite》

    更多关于SQLite架构的内容可以查看官网《 Architecture of SQLite 》, 里面介绍的比较详细。

    SQLite数据类型

    类型 类型说明
    NULL 这个值为空值
    VARCHAR(n) 长度不固定且其最大长度为 n 的字串,n不能超过 4000
    CHAR(n) 长度固定为n的字串,n不能超过 254
    INTEGER 值被标识为整数,依据值的大小可以依次被存储为1,2,3,4,5,6,7,8
    REAL 所有值都是浮动的数值,被存储为8字节的IEEE浮动标记序号
    TEXT 值为文本字符串,使用数据库编码存储(TUTF-8, UTF-16BE or UTF-16-LE)
    BLOB 值是BLOB数据块,以输入的数据格式进行存储。如何输入就如何存储,不改变格式
    DATA 包含了 年份、月份、日期
    TIME 包含了 小时、分钟、秒

    Android中操作SQLite

    SQLite的SQL语句其实和普通SQL没什么特别的不同,Windows下可视化操作SQLite可以使用SQLite Expert Personal 4 - 这款工具,下载地址如下:http://www.sqliteexpert.com/v4/SQLiteExpertPersSetup64.exe。打开后即可通过图形化界面的方式操作SQLite,同样也可以通过SQL语句来操作:

    # 建表
    create table stu_info (
           id integer primary key autoincrement,
           name varchar(30) not null,
           age integer ,
           gender varchar(2) not null
    )
    
    # 插入数据
    insert into stu_info(name, age, gender) values ('Mike', 24, '女');
    insert into stu_info(name, age, gender) values ('Jone', 26, '男');
    insert into stu_info(name, age, gender) values ('Tom', 28, '女');
    
    # 查询数据
    select * from stu_info;
    
    # 删除数据
    delete from stu_info where id = 13;
    
    # 修改数据
    update stu_info set name = 'Jack' where id = 10;
    
    # 按条件查询
    select * from stu_info where age = 24; 
    

    现在主要还是看看在Android平台如何使用吧!SQLiteOpenHelper:Android平台里一个数据库辅助类,用于创建或打开数据库,并且对数据库的创建和版本进行管理。

    SQLiteDatabase:用于管理和操作SQLite数据库,几乎所有的数据库操作,最终都将由这个类完成。

    ADB Shell操作SQLite

    打开CMD窗口,输入adb shell,找到sqlite文件,通过sqlite3 + sqlite文件名就可以进入sqlite shell:

    命令 作用
    .database 显示数据库信息
    .tables 显示表名称
    .schema 命令可以查看创建数据表时的SQL命令
    .schema table_name 查看创建表table_name时的SQL的命令

    SQLiteDatabase + SQL语句

    下面这个例子是通过SQLiteDatabase + SQL语句来操作数据库,即需要自己手动拼接SQL,如果是插入数据、修改数据、删除数据都是用sqLiteDatabase.execSQL(insertSQL/updateSQL/deleteSQL)来完成的,查询数据时使用sqLiteDatabase.rawQuery()方法来完成,由于使用起来需要拼接SQL,稍微麻烦一点。

    main_activity.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"
        android:padding="20dp"
        tools:context=".MainActivity">
    
        <EditText
            android:id="@+id/et_name"
            android:hint="姓名"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
    
        <EditText
            android:id="@+id/et_age"
            android:hint="年龄"
            android:inputType="number"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
    
        <RadioGroup
            android:id="@+id/rg_sex"
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <RadioButton
                android:id="@+id/rb_male"
                android:text="男"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
            <RadioButton
                android:id="@+id/rb_female"
                android:text="女"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
        </RadioGroup>
    
        <LinearLayout
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <Button
                android:id="@+id/btn_add"
                android:text="添加"
                android:onClick="operatorData"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
            <Button
                android:id="@+id/btn_update"
                android:text="修改"
                android:onClick="operatorData"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
            <Button
                android:id="@+id/btn_delete"
                android:onClick="operatorData"
                android:text="删除"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
        </LinearLayout>
        <EditText
            android:id="@+id/et_id"
            android:hint="修改/删除的ID"
            android:inputType="number"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
        <ListView
            android:id="@+id/lv_data"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
    </LinearLayout>
    

    MainActivity.java

    public class MainActivity extends AppCompatActivity {
        private static final String TAG = "MainActivity";
    
        private EditText etId;
        private EditText etName;
        private EditText etAge;
        private String sex;
        private SQLiteDatabase sqLiteDatabase;
        private ListView lvData;
    
        // 申请权限请求码
        private static final int REQUEST_EXTERNAL_STORAGE = 1001;
    
        // 检查权限,这种写法主要是针对比较新的Android6.0及以后的版本
        public static void verifyStoragePermissions(Activity activity) {
            int writePermission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
            int readPermission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE);
    
            if (writePermission != PackageManager.PERMISSION_GRANTED
                    || readPermission != PackageManager.PERMISSION_GRANTED) {
                // 如果没有权限需要动态地去申请权限
                ActivityCompat.requestPermissions(
                        activity,
                        // 权限数组
                        new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE},
                        // 权限请求码
                        REQUEST_EXTERNAL_STORAGE
                );
            }
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            etId = findViewById(R.id.et_id);
            etName = findViewById(R.id.et_name);
            etAge = findViewById(R.id.et_age);
    
            // 单选框组件
            RadioGroup rgSex = findViewById(R.id.rg_sex);
            lvData = findViewById(R.id.lv_data);
            verifyStoragePermissions(this);
            /*
             * 构造参数:
             * 1、上下文
             * 2、数据库名称,默认位置应用的私有目录(内部存储的database文件夹)
             * 3、CursorFactory类型
             * 4、数据库版本
             */
            String path = Environment.getExternalStorageDirectory() + "/sqlite_demo.db";
            SQLiteOpenHelper helper = new SQLiteOpenHelper(this, path, null, 1){
                // 创建数据库
                @Override
                public void onCreate(SQLiteDatabase db) {
                    Toast.makeText(MainActivity.this, "数据库创建", Toast.LENGTH_SHORT).show();
                    // 如果事先没有数据库的话,创建表的操作就可以在这里进行
                    db.execSQL("create table stu_info (id integer primary key autoincrement, name varchar(30) not null, age integer,gender varchar(2) not null)");
                }
                // 版本号变化之后会调用这个方法
                @Override
                public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
                    Toast.makeText(MainActivity.this, "数据库升级", Toast.LENGTH_SHORT).show();
                }
            };
            // 获取数据库对象
            sqLiteDatabase = helper.getWritableDatabase();
            // 设置单选框的监听
            rgSex.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(RadioGroup group, int checkedId) {
                    switch (checkedId){
                        case R.id.rb_female:
                            sex = "女";
                            break;
                        case R.id.rb_male:
                            sex = "男";
                            break;
                    }
                }
            });
            flushStuData();
        }
    
        private void flushStuData() {
            List<StudentInfo> stuList = new ArrayList<>();
            String selectSQL = "select * from stu_info";
            Cursor cursor = sqLiteDatabase.rawQuery(selectSQL, new String[]{});
            cursor.moveToFirst();
            while (!cursor.isAfterLast()){
                int id = cursor.getInt(0);
                String name = cursor.getString(1);
                int age = cursor.getInt(2);
                String sex = cursor.getString(3);
                stuList.add(new StudentInfo(id, name, age, sex));
                cursor.moveToNext();
            }
            cursor.close();
            lvData.setAdapter(new StuInfoAdapter(this, stuList));
        }
    
    
        public void operatorData(View view) {
            int viewId = view.getId();
            switch (viewId) {
                case R.id.btn_add:
                    if(TextUtils.isEmpty(sex)) {
                        Toast.makeText(MainActivity.this, "请选择性别", Toast.LENGTH_SHORT).show();
                        return;
                    }
                    String insertSQL = String.format(Locale.CHINA,"insert into stu_info(name, age, gender) values ('%s', %d, '%s')",
                            etName.getText().toString(),
                            Integer.parseInt(etAge.getText().toString()),
                            sex);
                    Log.i(TAG, "operatorData: insertSQL = " + insertSQL);
                    sqLiteDatabase.execSQL(insertSQL);
                    // 刷新数据展示
                    flushStuData();
                    Toast.makeText(MainActivity.this, "添加成功", Toast.LENGTH_SHORT).show();
                    break;
                case R.id.btn_update:
                    String idStr = etId.getText().toString();
                    if(TextUtils.isEmpty(idStr)){
                        Toast.makeText(MainActivity.this, "请输入ID", Toast.LENGTH_SHORT).show();
                        return;
                    }
                    int id = Integer.parseInt(idStr);
                    String updateSQL = String.format(Locale.CHINA,
                            "update stu_info set name = '%s', age=%d, gender='%s' where id = %d",
                            etName.getText().toString(),
                            Integer.parseInt(etAge.getText().toString()),
                            sex, id);
                    Log.i(TAG, "operatorData: updateSQL = " + updateSQL);
                    sqLiteDatabase.execSQL(updateSQL);
                    // 刷新数据展示
                    flushStuData();
                    Toast.makeText(MainActivity.this, "更新成功", Toast.LENGTH_SHORT).show();
                    break;
                case R.id.btn_delete:
                    String deleteIdStr = etId.getText().toString();
                    if(TextUtils.isEmpty(deleteIdStr)){
                        Toast.makeText(MainActivity.this, "请输入ID", Toast.LENGTH_SHORT).show();
                        return;
                    }
                    String deleteSQL = String.format(Locale.CHINA, "delete from stu_info where id = %d", Integer.parseInt(deleteIdStr));
                    Log.i(TAG, "operatorData: deleteSQL = " + deleteSQL);
                    sqLiteDatabase.execSQL(deleteSQL);
                    // 刷新数据展示
                    flushStuData();
                    Toast.makeText(MainActivity.this, "删除成功", Toast.LENGTH_SHORT).show();
                    break;
            }
        }
    }
    
    class StudentInfo {
        public int id;
        public String name;
        public int age;
        public String sex;
    
        public StudentInfo() {
        }
    
        public StudentInfo(int id, String name, int age, String sex) {
            this.id = id;
            this.name = name;
            this.age = age;
            this.sex = sex;
        }
    }
    

    StuInfoAdapter.java

    public class StuInfoAdapter extends BaseAdapter {
        private List<StudentInfo> stuList;
        private Activity context;
    
        public StuInfoAdapter(Activity context, List<StudentInfo> stuList) {
            this.stuList = stuList;
            this.context = context;
        }
    
        @Override
        public int getCount() {
            return stuList.size();
        }
    
        @Override
        public Object getItem(int position) {
            return stuList.get(position);
        }
    
        @Override
        public long getItemId(int position) {
            return position;
        }
    
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder viewHolder;
            if(convertView == null){
                LayoutInflater inflater = context.getLayoutInflater();
                convertView = inflater.inflate(R.layout.stu_item, null);
                viewHolder = new ViewHolder();
                viewHolder.tvName = convertView.findViewById(R.id.tv_item_name);
                viewHolder.tvId = convertView.findViewById(R.id.tv_item_id);
                viewHolder.tvAge = convertView.findViewById(R.id.tv_item_age);
                viewHolder.tvSex = convertView.findViewById(R.id.tv_item_sex);
                convertView.setTag(viewHolder);
            }else {
                viewHolder = (ViewHolder) convertView.getTag();
            }
            StudentInfo studentInfo = stuList.get(position);
            viewHolder.tvId.setText(String.valueOf(studentInfo.id));
            viewHolder.tvName.setText(studentInfo.name);
            viewHolder.tvSex.setText(studentInfo.sex);
            viewHolder.tvAge.setText(String.valueOf(studentInfo.age));
            return convertView;
        }
    
        // ViewHolder
        static class ViewHolder{
            TextView tvId;
            TextView tvName;
            TextView tvSex;
            TextView tvAge;
        }
    }
    

    最好不要忘记申明读写的权限(以及在Android6.0以后的动态权限申请),最后效果展示如下:

    在Android 中使用SQLiteDatabase的静态方法openOrCreateDatabase(String path,SQLiteDatabae.CursorFactory factory)打开或者创建一个数据库。它会自动去检测是否存在这个数据库,如果存在则打开,不存在则创建一个数据库;创建成功则返回一个SQLiteDatabase对象,否则抛出异常FileNotFoundException。

    还有需要注意的就是,查询数据返回的结果是Cursor,当我们使用SQLiteDatabase.query()方法时,会得到一个Cursor对象,Cursor指向的就是每一条数据。它提供了很多有关查询的方法,具体方法如下:

    方法名称 方法描述
    getCount() 获得总的数据项数
    isFirst() 判断是否第一条记录
    isLast() 判断是否最后一条记录
    moveToFirst() 移动到第一条记录
    moveToLast() 移动到最后一条记录
    move(int offset) 移动到指定记录
    moveToNext() 移动到下一条记录
    moveToPrevious() 移动到上一条记录
    getColumnIndexOrThrow(String columnName) 根据列名称获得列索引
    getInt(int columnIndex) 获得指定列索引的int类型值
    getString(int columnIndex) 获得指定列缩影的String类型值

    这个可以参考在代码示例中flushStuData()方法中的使用:

    private void flushStuData() {
        List<StudentInfo> stuList = new ArrayList<>();
        // 参数解释:表名、要查询的字段、列条件、列条件参数、GroupBy、having、orderBy
        Cursor cursor = sqLiteDatabase.query("stu_info", null, null, null, null, null, null);
        if(cursor.moveToFirst()){
            do{
                int id = cursor.getInt(0);
                String name = cursor.getString(1);
                int age = cursor.getInt(2);
                String sex = cursor.getString(3);
                stuList.add(new StudentInfo(id, name, age, sex));
            } while (cursor.moveToNext());
        }
        cursor.close();
        lvData.setAdapter(new StuInfoAdapter(this, stuList));
    }
    

    通过API来操作数据库

    SQLiteDatabase也提供了insert()、delete()、update()、query()方法专门用于插入、删除、更新和查询,通过这种API的操作方式就需要编写SQL语句了,只需要传入对应的参数,即可完成CRUD操作。还是通过上面的例子,改动的地方无非就是操作数据的部分而已:

    // ...
    
    // 查询
    private void flushStuData() {
        List<StudentInfo> stuList = new ArrayList<>();
        // 参数解释:表名、要查询的字段、列条件、列条件参数、GroupBy、having、orderBy
        Cursor cursor = sqLiteDatabase.query("stu_info", null, null, null, null, null, null);
        if(cursor.moveToFirst()){
            do{
                int id = cursor.getInt(0);
                String name = cursor.getString(1);
                int age = cursor.getInt(2);
                String sex = cursor.getString(3);
                stuList.add(new StudentInfo(id, name, age, sex));
            } while (cursor.moveToNext());
        }
        cursor.close();
        lvData.setAdapter(new StuInfoAdapter(this, stuList));
    }
    
    
    public void operatorData(View view) {
        int viewId = view.getId();
        switch (viewId) {
            // 添加
            case R.id.btn_add:
                // 参数解释:操作表的名称、可以为空的列、参数
                ContentValues values = new ContentValues();
                // Key - value
                values.put("name", etName.getText().toString());
                values.put("age", Integer.parseInt(etAge.getText().toString()));
                values.put("gender", sex);
                // 插入成功,返回数据的ID
                long infoId = sqLiteDatabase.insert("stu_info", null, values);
                // 刷新数据展示
                flushStuData();
                Toast.makeText(MainActivity.this, "添加成功,Id = " + infoId, Toast.LENGTH_SHORT).show();
                break;
            // 更新
            case R.id.btn_update:
                String idStr = etId.getText().toString();
                ContentValues updateValues = new ContentValues();
                // Key - value
                updateValues.put("name", etName.getText().toString());
                updateValues.put("age", Integer.parseInt(etAge.getText().toString()));
                updateValues.put("gender", sex);
                int info = sqLiteDatabase.update("stu_info", updateValues, "id=?", new String[]{idStr});
                // 刷新数据展示
                flushStuData();
                Toast.makeText(MainActivity.this, "更新成功,影响行数:" + info, Toast.LENGTH_SHORT).show();
                break;
            // 删除
            case R.id.btn_delete:
                String deleteIdStr = etId.getText().toString();
                int delete = sqLiteDatabase.delete("stu_info", "id=?", new String[]{deleteIdStr});
                // 刷新数据展示
                flushStuData();
                Toast.makeText(MainActivity.this, "删除成功,影响行数:" + delete, Toast.LENGTH_SHORT).show();
                break;
        }
    }
    
    // ...
    

    使用LitePal操作SQLite

    上面使用SQLiteDatabase来操作SQLite数据库的方法,使用起来真的很不方便,像我这种习惯使用ORM框架的人来说,SQLiteDatabase的操作方式简直太过于复杂,所以现在来看看Litepal这款开源框架吧,使用完过后自己也来尝试造一轮子,可以参考:https://github.com/huyongli/TigerDB

    环境搭建

    首先引入依赖:

    implementation 'org.litepal.android:core:1.4.1'
    

    接下来需要配置 litepal.xml 文件。右击 app/src/main 目录 —>New—>Directory,创建一个 assets 目录,在assets目录下新建litepal.xml文件:

    <?xml version="1.0" encoding="utf-8" ?>
    <litepal>
        <!-- 指定数据库名称 -->
        <dbname value="BookStore"/>
        <!-- 指定数据库版本号 -->
        <version value="1"/>
        <!-- 指定映射模型 -->
        <list></list>
    </litepal>
    

    最后还需要修改一下 AndroidManifest.xml 中的代码:

    <!-- 关键就这一句:android:name="org.litepal.LitePalApplication" -->
    <application
    	android:name="org.litepal.LitePalApplication"
    	android:allowBackup="true"
    	android:icon="@mipmap/ic_launcher"
    	android:label="@string/app_name"
    	android:roundIcon="@mipmap/ic_launcher_round"
    	android:supportsRtl="true"
    	android:theme="@style/AppTheme">
    	<activity android:name=".MainActivity">
    		<intent-filter>
    			<action android:name="android.intent.action.MAIN" />
    			<category android:name="android.intent.category.LAUNCHER" />
    		</intent-filter>
    	</activity>
    </application>
    

    现在 Litepal 的配置工作已经做完了,让我们开始正式使用它吧!

    建库建表

    现在开始声明一个JavaBean,也就是我们要存储的数据:

    public class Book {
        private int id;
        private String name;
        private String author;
    
        public Book(){ }
    
        public Book(int id, String author, String name) {
            this.id = id;
            this.author = author;
            this.name = name;
        }
        // Getter and Setter ...
    }
    

    并且在配置文件中配置它的映射模型:

    <?xml version="1.0" encoding="utf-8" ?>
    <litepal>
        <!-- 指定数据库名称 -->
        <dbname value="BookStore"/>
        <!-- 指定数据库版本号 -->
        <version value="1"/>
        <!-- 指定映射模型 -->
        <list>
            <mapping class="cn.tim.litepal_demo.Book"/>
        </list>
    </litepal>
    

    在Activity启动时创建数据库:

    // 创建数据库
    SQLiteDatabase database = LitePal.getDatabase();
    

    虽然有三张表,其中android_matedata表仍然不用管,table_schema表是litepal内部使用的,也可以直接忽视,Book表就是根据配置的Book类自动生成的表,是不是很方便?

    数据库升级

    而且Litepal很好的解决了数据库升级问题,使用SQLiteOpenHelper来升级数据库的方式:升级数据库的时候我们需要先把之前的表drop掉,然后再重新创建才行。这其实是一个非常严重的问题,因为这样会造成数据丢失,每当升级一次数据库,之前表中的数据就全没了。虽然可以通过复杂的逻辑控制来避免这种情况,但是维护成本很高。而有了LitePal,这些就都不再是问题了,使用LitePal来升级数据库非常非常简单,你完全不用思考任何的逻辑,只需要改你想改的任何内容,然后将版本号加1就行了。

    比如,将图书表的中再添加一个价格的字段,再新建一张分类表:

    public class Book {
        private int id;
        private String name;
        private String author;
        private int price;
        
        // ...
    }
    
    public class Category {
        private int id;
        private String name;
        private long count;
        
        // ...
    }
    

    同样需要配置映射模型:

    <?xml version="1.0" encoding="utf-8" ?>
    <litepal>
        <!-- 指定数据库名称 -->
        <dbname value="BookStore"/>
        <!-- 指定数据库版本号:此时数据库版本应该为2 -->
        <version value="2"/>
        <!-- 指定映射模型 -->
        <list>
            <mapping class="cn.tim.litepal_demo.Book"/>
            <mapping class="cn.tim.litepal_demo.Category"/>
        </list>
    </litepal>
    

    由此可见,book表中新增了一个price列,并且新创建了category表。

    CRUD操作

    下面来看看使用Litepal来对数据进行CRUD是多么方便吧:

    首先需要让数据模型对象,也就是定义的Javabean来继承DataSupport

    public class Book extends DataSupport {
        ...
    }
    

    下面直接通过查看log日志的方式来验证LitePal框架的CRUD:

    public class MainActivity extends AppCompatActivity {
        private static final String TAG = "MainActivity";
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            // 创建数据库
            SQLiteDatabase database = LitePal.getDatabase();
    
            // 添加数据
            Book book = new Book("Think In Java", "Tim", 58);
            boolean saveRet = book.save();
            Log.i(TAG, "onCreate: saveRet = " + saveRet);
            new Book("Think In C/C++", "Tom", 38).save();
            Log.i(TAG, "onCreate: 添加数据成功");
    
            // 查询数据
            List<Book> bookList = DataSupport.findAll(Book.class);
            Book[] books = new Book[bookList.size()];
            bookList.toArray(books);
            Log.i(TAG, "onCreate: books = " + Arrays.toString(books));
    
            // 删除数据
            int delete = DataSupport.delete(Book.class, books[0].getId());
            Log.i(TAG, "onCreate: 删除数据成功,delete = " + delete);
            
            // 查询数据
            bookList = DataSupport.findAll(Book.class);
            books = new Book[bookList.size()];
            bookList.toArray(books);
            Log.i(TAG, "onCreate: books = " + Arrays.toString(books));
    
            // 修改数据
            Book cppBook = new Book("Think In C/C++", "Tom", 28);
            int update = cppBook.update(2);
            Log.i(TAG, "onCreate: 修改数据成功,update = " + update);
    
            // 查询数据
            bookList = DataSupport.findAll(Book.class);
            books = new Book[bookList.size()];
            bookList.toArray(books);
            Log.i(TAG, "onCreate: books = " + Arrays.toString(books));
        }
    }
    

    上面演示了具体的操作,其实还有很多高级查询方法,这里不再赘述,如果用到可以参考作者的博客《Android数据库高手秘籍》

  • 相关阅读:
    Fox Dividing Cheese [CF-371B]
    2021计算机专业方向志愿怎么填?哪一个更香?
    【每天一个编程小技巧】C++ return:使函数立即结束!
    我开发了一个女朋友陪聊系统!【全天24小时推送问候,自动回复女友的微信消息】
    【C++框架编程】Qt 的 信号与槽 简单了解!
    程序人生:一流靠数学,二流靠算法!程序员的数学需要很厉害吗?
    【硬核知识】C语言文件操作!文件的打开和关闭!
    程序员真的已经没救了吗?这可能就是前端鄙视后端的真正原因吧!
    刷题 678 天的感受!Coding使我快乐,bug使我憔悴!
    MQ面试题
  • 原文地址:https://www.cnblogs.com/timdevelop/p/14081565.html
Copyright © 2020-2023  润新知