前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JetPack--Room数据库

JetPack--Room数据库

作者头像
aruba
发布2021-12-06 17:28:33
1.5K0
发布2021-12-06 17:28:33
举报
文章被收录于专栏:android技术
JetPack提供了Room数据库,和GreenDAO等开源库一样,在SQLite做了封装
Room主要使用三个注解:
1.Entity:实体类,对应一张表
2.Dao:包含操作表的一些列方法
3.Database:数据库持有者,数据库驱动。需要满足:定义的类是一个继承RoomDatabase的抽象类,注解中定义包含实体类列表,包含一个没有参数的抽象方法并返回Dao对象
一、Room上手

首先添加依赖:

代码语言:javascript
复制
    implementation 'androidx.room:room-runtime:2.3.0-rc01'
    annotationProcessor 'androidx.room:room-compiler:2.3.0-rc01'

定义一个实体类,在class上使用 @Entity注解 ,还需要一个构造方法,Room会根据这个构造将表里的数据转化为实体类,对于其他我们代码里使用的构造方法,可以使用@Ignore注解表示Room将忽略它,属性也可以使用这个注解,表示这个属性将不会生成数据库字段 使用@PrimaryKey注解指定主键并且是自增长的 属性还可以指定在数据库的字段等,使用@ColumnInfo注解

代码语言:javascript
复制
package com.aruba.room;

import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.Ignore;
import androidx.room.PrimaryKey;

/**
 * Created by aruba on 2021/9/12.
 */
@Entity
public class User {
    @PrimaryKey(autoGenerate = true)
    public int id;
    public String name;
    @ColumnInfo(name = "age", typeAffinity = ColumnInfo.INTEGER)
    public int age;
    @Ignore
    public boolean flag;

    public User(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    @Ignore
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Ignore
    public User() {
        
    }
}

定义Dao接口来对刚刚的User表进行操作,对接口使用@Dao注解 @Query、@Insert、@Delete、@Update注解,分别表示:查询、新增、删除、更新 增删改操作内部会自动使用主键进行操作

代码语言:javascript
复制
package com.aruba.room;

import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Update;

import java.util.List;

/**
 * Created by aruba on 2021/9/12.
 */
@Dao
public interface UserDao {
    //查询
    @Query("SELECT * FROM user")
    List<User> getUsers();

    //根据id查询
    @Query("SELECT * FROM user WHERE id = :id")
    User getUserById(int id);

    //插入一条数据
    @Insert
    void insertUser(User user);

    //删除一条数据
    @Delete
    void deleteUser(User user);

    //更新一条数据
    @Update
    void updateUser(User user);
}

定义抽象类,继承于RoomDatabase,并使用@Database注解,注解中指定表的实体类、数据库版本、是否输出日志 使用单例模式时,构造方法不能私有化,因为Room内部会调用构造方法 定义获取Dao对象的抽象函数

代码语言:javascript
复制
package com.aruba.room;

import android.content.Context;

import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;

/**
 * Created by aruba on 2021/9/12.
 */
@Database(entities = {User.class}, version = 1, exportSchema = false)
public abstract class MyDataBase extends RoomDatabase {
    private static final String DBNAME = MyDataBase.class.getSimpleName();
    private static MyDataBase instance;

    public static MyDataBase getInstance() {
        if (instance == null) throw new NullPointerException("database not init!!");
        return instance;
    }

    public static synchronized MyDataBase init(Context context) {
        if (instance == null)
            instance = Room.databaseBuilder(context.getApplicationContext()
                    , MyDataBase.class, DBNAME)
                    .build();

        return instance;
    }

    //获取Dao对象
    public abstract UserDao getUserDao();
}

界面中使用一个RecyclerView展示User表内的数据,并使用四个按钮分别进行查询,新增,删除,修改操作。 效果:

不过每次我们做了操作后,还需要手动查询下,有没有可以自动刷新数据的方法呢?

二、ViewModel+LiveData+Room

Room支持返回LiveData类型,结合ViewModel、DataBinding,就可以改造成一个非常棒的MVVM架构

代码语言:javascript
复制
package com.aruba.room;

import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Update;

import java.util.List;

/**
 * Created by aruba on 2021/9/12.
 */
@Dao
public interface UserDao {
    //查询
    @Query("SELECT * FROM user")
    LiveData<List<User>> getUsers();

    //根据id查询
    @Query("SELECT * FROM user WHERE id = :id")
    User getUserById(int id);

    //插入一条数据
    @Insert
    void insertUser(User user);

    //删除一条数据
    @Delete
    void deleteUser(User user);

    //删除所有数据
    @Query("DELETE  FROM user")
    void deleteAllUser();

    //更新一条数据
    @Update
    void updateUser(User user);
}

首先定义Repository层,里面实现对数据库的操作

代码语言:javascript
复制
package com.aruba.room;

import android.content.Context;
import android.os.AsyncTask;
import android.view.View;

import androidx.lifecycle.LiveData;

import java.util.List;

/**
 * Created by aruba on 2021/9/12.
 */
public class UserRepository {
    private UserDao userDao;

    public UserRepository(Context context) {
        this.userDao = MyDataBase.init(context).getUserDao();
    }

    public void insert(User user) {
        new AsyncTask<Void, Void, Void>() {

            @Override
            protected Void doInBackground(Void... voids) {
                userDao.insertUser(user);
                return null;
            }
        }.execute();
    }

    public void update(User user) {
        new AsyncTask<Void, Void, Void>() {

            @Override
            protected Void doInBackground(Void... voids) {
                userDao.updateUser(user);
                return null;
            }
        }.execute();
    }

    public void delete(User user) {
        new AsyncTask<Void, Void, Void>() {

            @Override
            protected Void doInBackground(Void... voids) {
                userDao.deleteUser(user);
                return null;
            }
        }.execute();
    }

    public void deleteAllUser(){
        new AsyncTask<Void, Void, Void>() {

            @Override
            protected Void doInBackground(Void... voids) {
                userDao.deleteAllUser();
                return null;
            }
        }.execute();
    }
    
    public LiveData<List<User>> query() {
        return userDao.getUsers();
    }
}

定义ViewModel

代码语言:javascript
复制
package com.aruba.room;

import android.app.Application;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;

import java.util.List;

/**
 * Created by aruba on 2021/9/12.
 */
public class UserViewModel extends AndroidViewModel {
    private UserRepository userRepository;

    public UserViewModel(@NonNull Application application) {
        super(application);
        userRepository = new UserRepository(application);
    }

    public void insert(View v) {
        userRepository.insert(new User("张三", 12));
    }

    public void update(View v) {
        User user = new User("赵四", 18);
        user.id = 1;
        userRepository.update(user);
    }

    public void delete(View v) {
        User user = new User();
        user.id = 2;
        userRepository.delete(user);
    }

    public void deleteAll(View v) {
        userRepository.deleteAllUser();
    }
    
    public LiveData<List<User>> getUsers() {
        return userRepository.query();
    }
}

Activity中使用:

代码语言:javascript
复制
package com.aruba.room;

import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import android.os.Bundle;

import com.aruba.room.databinding.ActivityMainBinding;

import java.util.List;

public class MainActivity extends AppCompatActivity {

    private RecyclerViewAdapter recyclerViewAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityMainBinding activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        UserViewModel userViewModel = new ViewModelProvider(this, ViewModelProvider.AndroidViewModelFactory.getInstance(getApplication())).get(UserViewModel.class);
        activityMainBinding.setUserViewModel(userViewModel);
        activityMainBinding.setLifecycleOwner(this);

        userViewModel.getUsers().observe(this, new Observer<List<User>>() {
            @Override
            public void onChanged(List<User> users) {
                recyclerViewAdapter.setDatas(users);
                recyclerViewAdapter.notifyDataSetChanged();
            }
        });
        recyclerViewAdapter = new RecyclerViewAdapter();
        activityMainBinding.recyclerview.setAdapter(recyclerViewAdapter);
        activityMainBinding.recyclerview.setLayoutManager(new LinearLayoutManager(this, RecyclerView.VERTICAL, false));
    }
}

效果:

三、升级与填充
1.使用Migration升级数据库

定义Migration,构造时需要低版本号和高版本号,初始化数据库时,通过addMigrations方法传入

代码语言:javascript
复制
package com.aruba.room;

import android.content.Context;

import androidx.annotation.NonNull;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import androidx.room.migration.Migration;
import androidx.sqlite.db.SupportSQLiteDatabase;

/**
 * Created by aruba on 2021/9/12.
 */
@Database(entities = {User.class}, version = 3, exportSchema = false)
public abstract class MyDataBase extends RoomDatabase {
    private static final String DBNAME = MyDataBase.class.getSimpleName();
    private static MyDataBase instance;

    public static MyDataBase getInstance() {
        if (instance == null) throw new NullPointerException("database not init!!");
        return instance;
    }

    public static synchronized MyDataBase init(Context context) {
        if (instance == null)
            instance = Room.databaseBuilder(context.getApplicationContext()
                    , MyDataBase.class, DBNAME)
                    .addMigrations(MIGRATION_1_2, MIGRATION_2_3)
                    .build();

        return instance;
    }

    static final Migration MIGRATION_1_2 = new Migration(1, 2) {
        @Override
        public void migrate(@NonNull SupportSQLiteDatabase database) {
            database.execSQL("ALTER TABLE user ADD COLUMN sex INTEGER NOT NULL DEFAULT 1");
        }
    };

    static final Migration MIGRATION_2_3 = new Migration(2, 3) {
        @Override
        public void migrate(@NonNull SupportSQLiteDatabase database) {
            database.execSQL("ALTER TABLE user ADD COLUMN height INTEGER NOT NULL DEFAULT 1");
        }
    };

    //获取Dao对象
    public abstract UserDao getUserDao();
}
2.异常处理

如果我们将版本升级到3,但是没有写相应的Migration,那么会出现一个IIlegalStateException异常,使用fallbackToDestructiveMigration方法,出现异常时,会重新构造表,当然以前的数据会丢失

3.Schema文件

我们在使用@Database注解exportSchema指定为true,那么每次升级时,都会导出一个Schema文件,里面包含的数据库的创建信息,方便排查问题 同时我们也需要在gradle里指定下导出文件夹位置

代码语言:javascript
复制
    defaultConfig {
        applicationId "com.aruba.room"
        minSdk 21
        targetSdk 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        dataBinding {
            enabled = true
        }
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
            }
        }
    }
代码语言:javascript
复制
{
  "formatVersion": 1,
  "database": {
    "version": 3,
    "identityHash": "5a971aace7f8ede39ea6eb469ab90b10",
    "entities": [
      {
        "tableName": "User",
        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `age` INTEGER NOT NULL, `sex` INTEGER NOT NULL, `height` INTEGER NOT NULL)",
        "fields": [
          {
            "fieldPath": "id",
            "columnName": "id",
            "affinity": "INTEGER",
            "notNull": true
          },
          {
            "fieldPath": "name",
            "columnName": "name",
            "affinity": "TEXT",
            "notNull": false
          },
          {
            "fieldPath": "age",
            "columnName": "age",
            "affinity": "INTEGER",
            "notNull": true
          },
          {
            "fieldPath": "sex",
            "columnName": "sex",
            "affinity": "INTEGER",
            "notNull": true
          },
          {
            "fieldPath": "height",
            "columnName": "height",
            "affinity": "INTEGER",
            "notNull": true
          }
        ],
        "primaryKey": {
          "columnNames": [
            "id"
          ],
          "autoGenerate": true
        },
        "indices": [],
        "foreignKeys": []
      }
    ],
    "views": [],
    "setupQueries": [
      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '5a971aace7f8ede39ea6eb469ab90b10')"
    ]
  }
}
4.销毁与重建策略

SQLite中修改表结构比较麻烦,如果想要将sex字段从INTEGER改为TEXT,最好的方式是采用销毁与重建策略,将数据复制到一个临时表,在删除原表,再将临时表重命名成原表名,可以参考schema文件

代码语言:javascript
复制
    static final Migration MIGRATION_3_4 = new Migration(3, 4) {
        @Override
        public void migrate(@NonNull SupportSQLiteDatabase database) {
            //"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` 
            // (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT
            // , `age` INTEGER NOT NULL, `sex` INTEGER NOT NULL, `height` INTEGER NOT NULL)",
            //根据schema文件创建新表,注意TEXT不需要加上NOT NULL
            database.execSQL(
                    "CREATE TABLE temp_user (" +
                            "id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " +
                            "name TEXT, " +
                            "age INTEGER NOT NULL, " +
                            "sex TEXT DEFAULT 'M', " +
                            "height INTEGER NOT NULL )"
            );
            //数据导入临时表
            database.execSQL("INSERT INTO temp_user (name,age,sex,height)" +
                    "SELECT name,age,sex,height FROM user");
            //丢弃原表
            database.execSQL("DROP TABLE user");
            //临时表重命名
            database.execSQL("ALTER TABLE temp_user RENAME TO user");
        }
    };
5.预填充数据库

我们可以将数据库文件放入assets目录下,初始化数据库时,通过createFromAsset方法或createFromFile方法导入

代码语言:javascript
复制
    public static synchronized MyDataBase init(Context context) {
        if (instance == null)
            instance = Room.databaseBuilder(context.getApplicationContext()
                    , MyDataBase.class, DBNAME)
                    .addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4)
                    .fallbackToDestructiveMigration()
                    .createFromAsset("mem.db")
                    .build();

        return instance;
    }
Demo地址:https://gitee.com/aruba/my-jetpack-application.git
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/9/12 下,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • JetPack提供了Room数据库,和GreenDAO等开源库一样,在SQLite做了封装
  • Room主要使用三个注解:
    • 1.Entity:实体类,对应一张表
      • 2.Dao:包含操作表的一些列方法
        • 3.Database:数据库持有者,数据库驱动。需要满足:定义的类是一个继承RoomDatabase的抽象类,注解中定义包含实体类列表,包含一个没有参数的抽象方法并返回Dao对象
        • 一、Room上手
        • 二、ViewModel+LiveData+Room
        • 三、升级与填充
          • 1.使用Migration升级数据库
            • 2.异常处理
              • 3.Schema文件
                • 4.销毁与重建策略
                  • 5.预填充数据库
                    • Demo地址:https://gitee.com/aruba/my-jetpack-application.git
                    相关产品与服务
                    数据库
                    云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档