room升级可以自动配置,官方文档有说明,新版本的库就行,看参考链接。向下面一样配置
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一下,还报错
这是注解自动生成的代码,居然报错,真是奇葩,也可能是没用对。
不纠结了,改成手动配置升级
升级成功了
采用上面的升级方式,用koltin就有问题,真奇葩
这个怎么回事呀?还是注解自动生成代码出错了,为啥不能生成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
@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 数据库
将数据保存到本地数据库