Google Room简单使用

Room Library也是作为Jetpack的一个组件使用,提供了一个覆盖SQLite的抽象层,采用注解处理工具辅助生成对应的数据库相关的代码,可以更加快速的进行开发和测试,对于数据库存储google也建议使用Room Library代替直接使用SQLite;

官方文档:https://developer.android.google.cn/training/data-storage/room

下面简单引入Room Library:

一,依赖:

  plugin:

apply plugin: 'kotlin-kapt'

  dependency:

    implementation 'android.arch.persistence.room:runtime:1.0.0'
    kapt 'android.arch.persistence.room:compiler:1.0.0'

二,建表:

 通过@Entity设置PersonlInfo为数据库的一张数据表,并设置基本的字段,如果设置mName为peimaryKey,需要指定mName字段为非空约束;

@Entity(tableName = "user_info")
public class PersonalInfo {
    @ColumnInfo(name = "name")
    @PrimaryKey
    @NonNull
    public String mName;
    @ColumnInfo(name = "age")
    public String mAge;
    //@ColumnInfo(name = "interest")
    //public String mInterest;

}

在简单定义一张contacts联系人表;

@Entity(tableName = "contacts")
public class Contact {
    @PrimaryKey
    @ColumnInfo(name = "contactId")
    public int mContactId;
    @ColumnInfo(name = "name")
    public String mName;
}

 在chatMesage表中设置aboutContactMessage和contact表的contactId关联,并且设置当删除contact数据时,附带的chatMessage中指定的aboutContactMessage等于contactId的所有数据也被删除;

@Entity(
        tableName = "chatMessage",
        foreignKeys = {
                @ForeignKey(
                        entity = Contact.class,//外键关联
                        parentColumns = "contactId",
                        childColumns = "aboutContactMessage",
                        onDelete = CASCADE,
                        onUpdate = NO_ACTION,
                        deferred = false
                )
        }
)
public class ChatMessage {
    @PrimaryKey(autoGenerate = true)
    public int mid;
    @ColumnInfo(name = "fromOthers")
    public int mFromOthers;
    @ColumnInfo(name = "aboutContactMessage")
    public int mAboutContactMessage;
    @ColumnInfo(name = "startTime")public long mShowTime;
    @ColumnInfo(name = "stringContent")
    public String mStringContent;
}

三,数据库访问DAO:

@Dao
public interface ChatMessageDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    void insertChatMessage(ChatMessage message);

    @Delete
    void deleteData(ChatMessage message);

    @Update
    void upDateData(ChatMessage message);

    @Query("SELECT * FROM chatMessage WHERE aboutContactMessage LIKE :aboutContactMessage")
    Flowable<List<ChatMessage>> rxQueryChatMessageWithContact(int aboutContactMessage);

}

插入和更新操作数据的时候,当主键出现冲突时,我们也可以设置不同的策略, 比如IGNORE或者REPLACE等,同时Room数据也支持rxJava操作,但是需要在dependencies中添加依赖:implementation "android.arch.persistence.room:rxjava2:1.0.0" 

四,数据库连接DataBase:

@Database(entities = {
        PersonalInfo.class,
        Contact.class,
        ChatMessage.class
//        Account.class
        },
        version = 1, exportSchema = true)
public abstract class ProjectDataBase extends RoomDatabase {

    private static final String DB_NAME = "DataBase.db";
    private static ProjectDataBase instance;

    public static synchronized ProjectDataBase getInstance(Context context) {
        if (instance == null) {
            instance = create(context);
        }
        return instance;
    }

    private static ProjectDataBase create(final Context context) {
        return Room.databaseBuilder(
                context,
                ProjectDataBase.class,
                DB_NAME)
                .addCallback(new Callback() {
                    @Override
                    public void onCreate(@NonNull SupportSQLiteDatabase db) {
                        super.onCreate(db);
                        //创建各个数据表时,并且创建完毕调用,后续并不会调用
                        Log.d("dataBase", "db onCreate");
                    }

                    @Override
                    public void onOpen(@NonNull SupportSQLiteDatabase db) {
                        super.onOpen(db);
                        //打开数据库连接时调用
                        Log.d("dataBase", "db onOpen");
                    }
                })
//                .addMigrations(new MyMigration2_3(1, 2))
                .allowMainThreadQueries()//Cannot access database on the main thread
//                .fallbackToDestructiveMigration()//(当不指定Migration时,可以跟新表,但是用户原始数据被清除)
                .build();
    }

    private static class MyMigration1_2 extends Migration {

        public MyMigration1_2(int startVersion, int endVersion) {
            super(startVersion, endVersion);
        }

        @Override
        public void migrate(@NonNull SupportSQLiteDatabase database) {
            Log.d("dataBase", "db migrate1");
            database.execSQL("ALTER TABLE user_info ADD COLUMN interest TEXT");
        }
    }

    private static class MyMigration2_3 extends Migration {

        public MyMigration2_3(int startVersion, int endVersion) {
            super(startVersion, endVersion);
        }

        @Override
        public void migrate(@NonNull SupportSQLiteDatabase database) {
            Log.d("dataBase", "db migrate2");
            database.execSQL("            CREATE TABLE [account](\n" +
                    "                    [pId] INTEGER NOT NULL PRIMARY KEY, \n" +
                    "  [count] TEXT REFERENCES [user_info]([name]));");

        }
    }

    public abstract ChatMessageDao getChatMessageDao();

    public abstract ContactDao getContactDao();

    public abstract PersonalInfoDao getPersonalInfoDao();
}

 RoomDatabase包含数据库持有者,并作为应用程序持久关系数据的基础连接的主要访问点,我们定义的抽象方法,通过kapt自动生成实现代码,可以通过点击ChatMessageData的实现方法,在buid/generated/kapt下找到这些实现类

五,增删改查简单操作:

简单测试一下基于Room数据库的操作:

插入个人基本信息:

 private fun justInsetPersonal() {
       ProjectDataBase.getInstance(this).personalInfoDao.insertPerson(PersonalInfo().apply {
                this.mName = "zhangSan"
                this.mAge = "18"
            })
    }

插入联系人信息:

    private fun justInsetContact() {
        ProjectDataBase.getInstance(this).contactDao.apply {
            for (i in 1..10) {
                insertPerson(Contact().apply {
                    this.mContactId = i
                    this.mName = "name$i"
                })
            }
        }
    }

插入一些chat message:

    private fun justInsetMessage() {
        ProjectDataBase.getInstance(this).apply {
            for (i in 1..10) {
                chatMessageDao.insertChatMessage(ChatMessage().apply {
                    this.mFromOthers = 0
                    this.mAboutContactMessage = i
                    this.mStartTime = System.currentTimeMillis()
                    this.mStringContent = "hello zhangSan"
                })
            }
            chatMessageDao.insertChatMessage(ChatMessage().apply {
                this.mFromOthers = 1
                this.mAboutContactMessage = 3
                this.mStartTime = System.currentTimeMillis()
                this.mStringContent = "你好 name3"
            })
        }
    }

查询简单操作:

base.chatMessageDao.rxQueryChatMessageWithContact(3).subscribe {
                it.forEach {
                    Log.d("dataBase", "rxQueryChatMessageWithContact: ${it.toString()}")
                }
            }

我遇到的问题是,db文件导出到桌面没有数据,我们生成的数据库文件包含db-shm和db-wal后缀名的两个零时文件,9.0引入,在stackoverflow上找到了原因,这里有一些介绍https://www.sqlite.org/tempfiles.html,处理方式主要有两种,设置disableWriteAheadLogging(),或者写入数据后便关闭数据连接close(),并刷新我们的databases文件夹,就会自动删除临时文件,导入到桌面使用Expert或者DB Browser都可以查看,也可安装studio插件;

六,数据库版本升级:

数据库升级,我们将 version = 1版本增加,并设置Room.databaseBuilder().addMigrations(1,2),既可以完成数据的迁移;

比如在user_info表中增加一个interest字段:

    private static class MyMigration1_2 extends Migration {

        public MyMigration1_2(int startVersion, int endVersion) {
            super(startVersion, endVersion);
        }

        @Override
        public void migrate(@NonNull SupportSQLiteDatabase database) {
            Log.d("dataBase", "db migrate1");
            database.execSQL("ALTER TABLE user_info ADD COLUMN interest TEXT");
        }
    }

Room.databaseBuilder().addMigrations()设置数据库迁移,这里以在user_info表中增加一个interest字段为例,先在PersonalInfo类中增加这个字段, @columnInfo(name = "interest"),然后执行上面execSQL语句,将字段添加到user_info数据表中;

下面这个升级是增加一张关联user_info表的account表:

@Entity(
        tableName = "account",
        foreignKeys = {
                @ForeignKey(
                        entity = PersonalInfo.class,
                        parentColumns = "name",
                        childColumns = "count"
                )
        }
)
public class Account {
    @PrimaryKey
    public int pId;
    @ColumnInfo(name = "count")
    public String mCount;
}
    private static class MyMigration2_3 extends Migration {

        public MyMigration2_3(int startVersion, int endVersion) {
            super(startVersion, endVersion);
        }

        @Override
        public void migrate(@NonNull SupportSQLiteDatabase database) {
            Log.d("dataBase", "db migrate2");
            database.execSQL("            CREATE TABLE [account](\n" +
                    "                    [pId] INTEGER NOT NULL PRIMARY KEY, \n" +
                    "  [count] TEXT REFERENCES [user_info]([name]));"); 

        }
    }

先增加一个@Entity类Account,设置表名为account,对于execSQL语句,我是直接在Expert中生成一个account表查看SQL语句进行测试,我们设置的entity类所生成的表和execSQL语句生成的表的构建信息要匹配,不然会出现不匹配异常;

此外,当设置fallbackToDestructiveMigration()时,也可以完成数据库的升级,但是会将之前数据库中已有的数据清除;

同时可以在defaultConfig配置块中设置:

javaCompileOptions {
            annotationProcessorOptions {
                arguments = ["room.schemaLocation":
                                     "$projectDir/schemas".toString()]
            }
        }

执行ReBuild会将当前版本的数据库信息生成对应的1.json文件,保留升级之前的数据库信息;

猜你喜欢

转载自www.cnblogs.com/woshijinlei/p/10459556.html