Room使用遇到的问题

Room数据库升级,增加字段

room升级可以自动配置,官方文档有说明,新版本的库就行,看参考链接。向下面一样配置

Room使用遇到的问题_第1张图片

is_my_attention是新添加的字段,配置完build一下,报如下的错:
/Users/zhongyili/work/AndroidStudioProjects/SohuModuleLibrary/sohuPush/src/main/java/com/sohu/sohuvideo/sohupush/data/SocketDatabase.java:26: 错误: New NOT NULL column'is_my_attention' added with no default value specified. Please specify the default value using @ColumnInfo. public abstract class SocketDatabase extends RoomDatabase { ^
意思是没有设置默认值,我们设置一个默认值
在这里插入图片描述
再build一下,还报错
Room使用遇到的问题_第2张图片

这是注解自动生成的代码,居然报错,真是奇葩,也可能是没用对。
不纠结了,改成手动配置升级
Room使用遇到的问题_第3张图片

升级成功了

Room和kotlin结合使用,升级数据库遇到奇葩问题

采用上面的升级方式,用koltin就有问题,真奇葩


Room使用遇到的问题_第4张图片

这个怎么回事呀?还是注解自动生成代码出错了,为啥不能生成set方法呢?
后来猜想是不是名字带is导致的,把is删了结果可以了,奇葩吧

还有sqLite是不支持boolean类型的
在migrate()方法中执行SQL语句新增某字段时,该字段不能为BOOLEAN类型的,需要用 INTEGER 替换,如果不指定默认值,则系统默认为1也就是true,但是在写表Model 实体时,时可以定义Boolean类型的变量的,Room会自己转化为INTEGER。
深坑!! 表里是BOOLEAN类型,但是SQL语句需要用INTEGER

但是room还是可以使用boolean类型的,因为通过注解解析后会自动转换的

升级数据库遇到错误

08-15 12:37:05.291 14342 14669 E AndroidRuntime: java.lang.RuntimeException: Exception while computing database live data.
08-15 12:37:05.291 14342 14669 E AndroidRuntime: 	at androidx.room.RoomTrackingLiveData$1.run(RoomTrackingLiveData.java:92)
08-15 12:37:05.291 14342 14669 E AndroidRuntime: 	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
08-15 12:37:05.291 14342 14669 E AndroidRuntime: 	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
08-15 12:37:05.291 14342 14669 E AndroidRuntime: 	at java.lang.Thread.run(Thread.java:919)
08-15 12:37:05.291 14342 14669 E AndroidRuntime: Caused by: java.lang.IllegalStateException: Migration didn't properly handle: session_table(com.sohu.sohuvideo.sohupush.bean.Session).
08-15 12:37:05.291 14342 14669 E AndroidRuntime:  Expected:
08-15 12:37:05.291 14342 14669 E AndroidRuntime: TableInfo{name='session_table', columns={is_my_attention=Column{name='is_my_attention', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='0'}, last_msg_id=Column{name='last_msg_id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, session_id=Column{name='session_id', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, s_session_id=Column{name='s_session_id', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, s_content=Column{name='s_content', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, s_show_time=Column{name='s_show_time', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=0, defaultValue='null'}, s_to_uid=Column{name='s_to_uid', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, read_msg_id=Column{name='read_msg_id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, s_send_time=Column{name='s_send_time', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, s_msgId=Column{name='s_msgId', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=0, defaultValue='null'}, s_category=Column{name='s_category', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=0, defaultValue='null'}, unread_count=Column{name='unread_count', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, last_show_time=Column{name='last_show_time', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, s_nick_name=Column{name='s_nick_name', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, s_local_id=Column{name='s_local_id', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, s_animation_already_do=Column{name='s_animation_already_do', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=0, defaultValue='0'}, first_show_time=Column{name='first_show_time', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, s_msgStatus=Column{name='s_msgStatus', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=0, defaultValue='null'}, s_local_uid=Column{name='s_local_uid', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, user_uid=Column{name='user_uid', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=1, defaultValue='null'}, s_from_uid=Column{name='s_from_uid', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}}, foreignKeys=[], indices=[]}
08-15 12:37:05.291 14342 14669 E AndroidRuntime:  Found:
08-15 12:37:05.291 14342 14669 E AndroidRuntime: TableInfo{name='session_table', columns={is_my_attention=Column{name='is_my_attention', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='0'}, last_msg_id=Column{name='last_msg_id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, session_id=Column{name='session_id', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, s_session_id=Column{name='s_session_id', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, s_content=Column{name='s_content', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, s_show_time=Column{name='s_show_time', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=0, defaultValue='null'}, s_to_uid=Column{name='s_to_uid', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, read_msg_id=Column{name='read_msg_id', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, s_send_time=Column{name='s_send_time', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, s_msgId=Column{name='s_msgId', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=0, defaultValue='null'}, s_category=Column{name='s_category', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=0, defaultValue='null'}, unread_count=Column{name='unread_count', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, last_show_time=Column{name='last_show_time', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, s_nick_name=Column{name='s_nick_name', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, s_local_id=Column{name='s_local_id', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, first_show_time=Column{name='first_show_time', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, s_msgStatus=Column{name='s_msgStatus', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=0, defaultValue='null'}, s_local_uid=Column{name='s_local_uid', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, user_uid=Column{name='user_uid', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=1, defaultValue='null'}, s_from_uid=Column{name='s_from_uid', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}}, foreignKeys=[], indices=[]}
08-15 12:37:05.291 14342 14669 E AndroidRuntime: 	at androidx.room.RoomOpenHelper.onUpgrade(RoomOpenHelper.java:103)
08-15 12:37:05.291 14342 14669 E AndroidRuntime: 	at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.onUpgrade(FrameworkSQLiteOpenHelper.java:177)
08-15 12:37:05.291 14342 14669 E AndroidRuntime: 	at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:417)
08-15 12:37:05.291 14342 14669 E AndroidRuntime: 	at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:317)
08-15 12:37:05.291 14342 14669 E AndroidRuntime: 	at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper$OpenHelper.getWritableSupportDatabase(FrameworkSQLiteOpenHelper.java:145)
08-15 12:37:05.291 14342 14669 E AndroidRuntime: 	at androidx.sqlite.db.framework.FrameworkSQLiteOpenHelper.getWritableDatabase(FrameworkSQLiteOpenHelper.java:106)
08-15 12:37:05.291 14342 14669 E AndroidRuntime: 	at androidx.room.RoomDatabase.inTransaction(RoomDatabase.java:476)
08-15 12:37:05.291 14342 14669 E AndroidRuntime: 	at androidx.room.RoomDatabase.assertNotSuspendingTransaction(RoomDatabase.java:281)
08-15 12:37:05.291 14342 14669 E AndroidRuntime: 	at androidx.room.RoomDatabase.query(RoomDatabase.java:324)
08-15 12:37:05.291 14342 14669 E AndroidRuntime: 	at androidx.room.util.DBUtil.query(DBUtil.java:83)
08-15 12:37:05.291 14342 14669 E AndroidRuntime: 	at com.sohu.sohuvideo.sohupush.data.dao.SessionDao_Impl$10.call(SessionDao_Impl.java:1195)
08-15 12:37:05.291 14342 14669 E AndroidRuntime: 	at com.sohu.sohuvideo.sohupush.data.dao.SessionDao_Impl$10.call(SessionDao_Impl.java:1192)
08-15 12:37:05.291 14342 14669 E AndroidRuntime: 	at androidx.room.RoomTrackingLiveData$1.run(RoomTrackingLiveData.java:90)


这种情况下msg插入字段的时候,在升级数据库的时候session表也得插入字段

    static final Migration migration_2_3 = new Migration(2, 3) {
        @Override
        public void migrate(@NonNull SupportSQLiteDatabase database) {
            database.execSQL("ALTER TABLE `msg_table` ADD COLUMN `animation_already_do` INTEGER NOT NULL DEFAULT 0");
            database.execSQL("ALTER TABLE `session_table` ADD COLUMN `s_animation_already_do` INTEGER NOT NULL DEFAULT 0");
        }
    };

这个要格外注意,一开始不知道怎么解决这个问题。后来使用自动升级配置,看看自动升级是怎么做的,才知道session中也需要插入字段

奇葩
自动升级生成的配置是

  public void migrate(@NonNull SupportSQLiteDatabase database) {
    database.execSQL("ALTER TABLE `msg_table` ADD COLUMN `animation_already_do` INTEGER NOT NULL DEFAULT 0");
    database.execSQL("ALTER TABLE `session_table` ADD COLUMN `s_animation_already_do` INTEGER DEFAULT NULL");
  }

用这个会报错,测试发现得用

database.execSQL("ALTER TABLE `session_table` ADD COLUMN `s_animation_already_do` INTEGER DEFAULT 0");

允许为空,而且默认值得是0

room的不同版本注解解析的结果有差异

     @SuppressWarnings(RoomWarnings.CURSOR_MISMATCH)
     @Query("SELECT session_table.* FROM session_table WHERE ((select COUNT(*) FROM msg_table WHERE session_table.user_uid = msg_table.local_uid AND msg_table.from_uid =:uid) >0) ORDER BY s_send_time desc LIMIT 0,:size ")
     List<Session> getLastSession(int size, long uid);

项目中有这么一条查询语句,session表的结构如下:

package com.sohu.sohuvideo.sohupush.bean;

import androidx.room.ColumnInfo;
import androidx.room.Embedded;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
import androidx.room.RoomWarnings;
import androidx.annotation.NonNull;

import java.io.Serializable;
import java.util.Objects;

@Entity(tableName = "session_table")
public class Session implements Serializable {

    @ColumnInfo(name = "session_id")
    public String session_id;

    @NonNull
    @PrimaryKey
    @ColumnInfo(name = "user_uid")
    public long user_id;

    @ColumnInfo(name = "is_my_attention", defaultValue = "0")
    public int is_my_attention;

    // 这个session收到的最新消息的消息ID
    @ColumnInfo(name = "last_msg_id")
    public long last_msg_id;

    // 这个session已读到的消息的消息ID
    @ColumnInfo(name = "read_msg_id")
    public long read_msg_id;

    /**
     * 最新收到的消息发送时间距上一次展示了时间的消息的发送时间,超过5分钟,在对话页面需要展示此消息的发送时间
     * last_show_time记录了上一次展示了时间的消息的发送时间,用于计算后续收到的消息,间隔是否超过了5分钟
     */
    @ColumnInfo(name = "last_show_time")
    public long last_show_time;

    // 记录了第一条消息的发送时间
    @ColumnInfo(name = "first_show_time")
    public long first_show_time;

    @ColumnInfo(name = "unread_count")
    public long unread_count;

    // 这session收到的最新消息
    @SuppressWarnings(RoomWarnings.PRIMARY_KEY_FROM_EMBEDDED_IS_DROPPED)
    @Embedded(prefix = "s_")
    public Msg msg;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Session session = (Session) o;

        return user_id == session.user_id;
    }

    @Override
    public int hashCode() {
        return (int) (user_id ^ (user_id >>> 32));
    }

    @Override
    public String toString() {
        return "Session{" +
                "session_id='" + session_id + '\'' +
                ", user_id=" + user_id +
                ", last_msg_id=" + last_msg_id +
                ", read_msg_id=" + read_msg_id +
                ", last_show_time=" + last_show_time +
                ", first_show_time=" + first_show_time +
                ", unread_count=" + unread_count +
                ", msg=" + msg +
                ", is_my_attention=" + is_my_attention +
                '}';
    }
}

在room 2.2.5版本中,查询处理session数据中msg字段是null,这个很莫名其妙,把room升级到2.5.0版本,查询结果就正常了。

研究了一下注解自动生成的代码,发现最终的查询语句是

final String _sql = "SELECT `session_table`.`session_id`, `session_table`.`user_uid`, `session_table`.`is_my_attention`, `session_table`.`last_msg_id`, `session_table`.`read_msg_id`, `session_table`.`last_show_time`, `session_table`.`first_show_time`, `session_table`.`unread_count` FROM session_table WHERE ((select COUNT(*) FROM msg_table WHERE session_table.user_uid = msg_table.local_uid AND msg_table.from_uid =?) >0) ORDER BY s_send_time desc LIMIT 0,? ";

这明显没有查询session中msg呀,后来改成
如下

     @SuppressWarnings(RoomWarnings.CURSOR_MISMATCH)
     @Query("SELECT * FROM session_table WHERE ((select COUNT(*) FROM msg_table WHERE session_table.user_uid = msg_table.local_uid AND msg_table.from_uid =:uid) >0) ORDER BY s_send_time desc LIMIT 0,:size ")
     List<Session> getLastSession(int size, long uid);

注解解析后的查询语句如下:

final String _sql = "SELECT `s_msgId`, `s_from_uid`, `s_nick_name`, `s_to_uid`, `s_local_uid`, `s_session_id`, `s_send_time`, `s_content`, `s_category`, `s_msgStatus`, `s_show_time`, `s_local_id`, `s_animation_already_do`, `session_table`.`session_id` AS `session_id`, `session_table`.`user_uid` AS `user_uid`, `session_table`.`is_my_attention` AS `is_my_attention`, `session_table`.`last_msg_id` AS `last_msg_id`, `session_table`.`read_msg_id` AS `read_msg_id`, `session_table`.`last_show_time` AS `last_show_time`, `session_table`.`first_show_time` AS `first_show_time`, `session_table`.`unread_count` AS `unread_count` FROM session_table WHERE ((select COUNT(*) FROM msg_table WHERE session_table.user_uid = msg_table.local_uid AND msg_table.from_uid =?) >0) ORDER BY s_send_time desc LIMIT 0,? ";

这样就对了。

通过这次的问题,可以知道
2.2.5room版本,对于表名.*只能查询到自己中的列,对于嵌套在自己表中的msg列就查不到,但是2.5.0可以。

Msg表可以通过@Embedded嵌入到Session表中,而且是把msg中的字段都放到Session表中,同时为了区分可以指定前缀。

记录一次疑惑

一开始为了解决这个问题,目标是去升级room库的,我们的项目是在library库中写的,所以我在library中对room进行了升级,编译打包上传到maven上。然后后主项目中把这个包下载下来,我们主项目强制把room库版本设置成了2.2.6。

所以我理解最终的room库还是2.2.6版本,这个问题应该还没有解决,结果发现这个问题居然解决了,我就吐血了。。。难道用的是room 2.5.0的版本?

后来仔细想了一下,知道问题所在了,我们在library中是用room 2.5.0编译的代码,这个代码已经边缘好。然后在住项目里最终的room版本还是2.2.6,最终结果就对了。
只能说用2.5.0和2.2.6编译处理的代码是不一样的。上面的组合会有问题。

所以说,编译的版本和最终使用的版本可以不一样,但是这样做会有风险,编译出来的代码,可以在使用的版本中找不到代码。

参考

android Room数据库新增BOOLEAN类型的坑
迁移 Room 数据库
将数据保存到本地数据库

你可能感兴趣的:(Android,room)