Room中List<>使用详解

手摸手教你在Room中使用List<Object>.

实际项目有很多这样的结构:

{
  "id": "001",
  "name": "王诛魔",
  "age": 2018,
  "books": [
    {
      "bookId": 10081,
      "userId": 1,
      "book": "断罪小学",
      "desc": "漫画讲述了李杀神、王诛魔和刘斩仙三位少年,为了十万小学生的未来、为了改变被诅咒的命运,相聚在狼牙区飞虎路私立断罪小学,誓死打败四年级的魔神赵日天的故事。"
    },
    {
      "bookId": 10088,
      "userId": 1,
      "book": "狂霸天下之断罪学园",
      "desc": "《狂霸天下之断罪学园》,后来因为名字太长,而且“学园”和“学院”容易混淆的原因而改名。"
    }
  ]
}

也就是,类似books这样的,List<Object>作为一个元素存在.

下面就以上面的结构为例,做一个简单的演示,按照最简答的方法实现.

1.添加依赖

https://developer.android.google.cn/topic/libraries/architecture/adding-components.html

值得注意的是,如果你在kotlin中使用

apply plugin: 'kotlin-kapt'
...
dependencies{
  
  ...
    //这个无法生成代码,要用下面的那一条,不要疏忽忘掉了.
    //annotationProcessor "android.arch.persistence.room:compiler:1.0.0"
    
    kapt "android.arch.persistence.room:compiler:1.0.0"
}

一定记得使用kapt,要不然就会遇到这个BUG.

2.创建表

  • User.java表

    /**
     * If you have any questions, you can contact by email { wangzhumoo@gmail.com}
     * @author 王诛魔 2018/1/3 下午1:31
     */
    @Entity(tableName = "users")
    public class User {
        @PrimaryKey
        public int id;
        public String name;
        public int age;
    }
    

    @Entity 中指定了表名

    @PrimaryKey 主键

    这张表users 用于保存用户数据

  • Book.java表

    /**
     * If you have any questions, you can contact by email { wangzhumoo@gmail.com}
     * @author 王诛魔 2018/1/3 下午1:30
     */
    @Entity(foreignKeys = {
            @ForeignKey(
                    entity = User.class,
                    parentColumns ={"id"},
                    childColumns = {"userId"})
    },tableName = "books")
    public class Book {
        @PrimaryKey
        public int bookId;
        public int userId;//同User.java中的id
        public String book;
        public String desc;
    
    }
    

    @Entity 中

    • foreignKeys = {} 中外键,可以是多个,这里只关联了User表
    • tableName = "books" 指定了表名

    @PrimaryKey 该表主键

  • UserAndBook.java表 ----UserAndBook只是辅助,不作为一张表存在.

    /**
     * If you have any questions, you can contact by email { wangzhumoo@gmail.com}
     * @author 王诛魔 2018/1/3 下午1:43
     */
    public class UserAndBook {
        @Embedded   //请看之前的文章,这是指把User中所有元素带入到这里作为元素存在
        public User userEntity;
    
          //@Relation
        @Relation(parentColumn = "id",entityColumn = "userId",entity = Book.class)
        public List<Book> books;
    
    }
    
    

    @Embedded 请看另一篇,有解释

    @Relation 点这里去看官方文档

    A convenience annotation which can be used in a Pojo to automatically fetch relation entities. When the Pojo is returned from a query, all of its relations are also fetched by Room.

    个人以为就是一注解,指明关系,可以把实体关联起来,然后自动组装起来.

    并且在文档中还展示了一种高端的功能,用上面做例

    //@Relation
        @Relation(parentColumn = "id",entityColumn = "userId",
                  entity = Book.class ,projection = {"book"})
        public List<String> bookNames;
    

    解释一下,projection指定了要取出的值,当前指明要在 books表中寻找符合条件parentColumn = "id",entityColumn = "userId" 的项,找到之后会拿到book这个值并返回.

3.Dao实现

/**
 * If you have any questions, you can contact by email { wangzhumoo@gmail.com}
 * @author 王诛魔 2018/1/3 下午4:20
 */
@Dao
public abstract class UserAndBookDao {

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    public abstract void insert(User user);

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    public abstract void insert(List<Book> books);

    @Transaction
    public void insert(UserAndBook userAndBook){
        insert(userAndBook.userEntity);
        insert(userAndBook.books);
    }
  
    @Query("SELECT * FROM users")
    public abstract List<UserAndBook> getAll();
}

insert(User user); ,insert(List<Book> books);

两个方法,也可以私有化,只对外提供insert(UserAndBook userAndBook)

最近换公司项目的数据库,用sql,真的有意思,可以很方便的实现很多功能

4.建立数据库

/**
 * If you have any questions, you can contact by email { wangzhumoo@gmail.com}
 * @author 王诛魔 2017/10/24 上午10:13
 * 数据库
 */
@Database(entities = {Book.class, User.class}, 
          version = 1, exportSchema = false)
public abstract class AppDB extends RoomDatabase {
    private static final String TAG = "AppDB";

    /**
     * 数据库实例
     */
    private static AppDB sInstance;
    private static String DATABASE_NAME = "developer.db";
    private ObservableBoolean mIsDatabaseCreated = new ObservableBoolean(false);

    
    public abstract UserAndBookDao userbook();

    /**
     * 单例
     * @param context
     * @return
     */
    public static AppDB getInstance(Context context) {
        if (sInstance == null) {
            synchronized (AppDB.class) {
                if (sInstance == null) {
                    sInstance = buildDatabase(context);
                    sInstance.updateDatabaseCreated(context);
                }
            }
        }
        return sInstance;
    }

    /**
     * Build the database.
     * creates a new instance of the database.
     */
    private static AppDB buildDatabase(Context appContext) {
         AppDB appDB = Room.databaseBuilder(appContext, AppDB.class, DATABASE_NAME)
                .addCallback(new Callback() {
                    @Override
                    public void onCreate(@NonNull SupportSQLiteDatabase db) {
                        super.onCreate(db);
                        AppDB database = AppDB.getInstance(appContext);
                        database.setDatabaseCreated();
                    }

                    @Override
                    public void onOpen(@NonNull SupportSQLiteDatabase db) {
                        super.onOpen(db);
                        Log.e(TAG, "onOpen: " + db.getPath());
                    }
                }).build();

        return appDB;
    }

    /**
     * Check whether the database already exists and expose it via 
     */
    private void updateDatabaseCreated(final Context context) {
        if (context.getDatabasePath(DATABASE_NAME).exists()) {
            setDatabaseCreated();
        }
    }

    private void setDatabaseCreated() {
        mIsDatabaseCreated.set(true);
    }

    public ObservableBoolean getDatabaseCreated() {
        return mIsDatabaseCreated;
    }
}

为了方便使用,在Application中添加了

public static AppDB getDatabase() {
    return AppDB.getInstance(context);
}

5.使用

这里的使用方式,不建议使用,太粗暴了.

可以学学Demo,Google出的googlesamples/android-architecture-components

class RoomActivity : BindingActivity<ActivityRoomBinding, RoomViewModel>() {
    val gson = Gson()

    override fun getBundle(extras: Bundle) {
    }

    override fun layoutID(): Int = R.layout.activity_room

    override fun initViews(savedInstanceState: Bundle?) {
        
        //点击事件  ADD按钮,添加数据到数据库
        mBindingView.button.setOnClickListener({
            val userBook = UserAndBook()
            val jsonStr = FileUtils.getJson(this, "data.txt")
            val objectJson = JsonParser().parse(jsonStr).asJsonObject

            //获取用户
            val user = objectJson.get("user")
            userBook.userEntity = gson.fromJson<User>(user, User::class.java)

            //保存用户
            Observable
                .empty<Any>()
                .observeOn(Schedulers.io())
                .subscribe(object : EmptyObserver<Any?>() {
                    override fun onComplete() {
                        App.getDatabase().userbook().insert(userBook.userEntity)
                    }
                })

            //获取书籍
            val books = objectJson.get("books")
            userBook.books = gson.fromJson<ArrayList<Book>>
                                (books, object : TypeToken<List<Book>>() {}.type)
            //保存书籍
            Observable
                .empty<Any>()
                .observeOn(Schedulers.io())
                .subscribe(object : EmptyObserver<Any?>() {
                     override fun onComplete() {
                        App.getDatabase().userbook().insert(userBook.books)
                    }
                })
        })


        //点击事件,按钮QUERY   查询数据
        mBindingView.button2.setOnClickListener {
            Flowable.create(FlowableOnSubscribe<List<UserAndBook>> { e ->
                val userBook = App.getDatabase().userbook().all
                e.onNext(userBook)
                e.onComplete()
            }, BackpressureStrategy.DROP)
                    .compose(DatabaseTransformer())
                    .subscribe(object : DatabaseSubscriber<List<UserAndBook>>() {
                        override fun onNext(userAndBooks: List<UserAndBook>) {
                            mBindingView.data = userAndBooks[0]
                        }
                    })
        }

    }
}
<?xml version="1.0" encoding="utf-8"?>
<layout
    xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <import type="com.wangzhumo.app.data.UserAndBook"/>
        <import type="com.wangzhumo.app.data.User"/>
        <import type="com.wangzhumo.app.data.Book"/>
        <variable
            name="data"
            type="com.wangzhumo.app.data.UserAndBook"/>
    </data>
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_alignParentStart="true"
            android:layout_alignParentTop="true"
            android:layout_marginLeft="57dp"
            android:layout_marginStart="57dp"
            android:layout_marginTop="125dp"
            android:text="Add" />

        <Button
            android:id="@+id/button2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignBaseline="@+id/button"
            android:layout_alignBottom="@+id/button"
            android:layout_alignParentEnd="true"
            android:layout_alignParentRight="true"
            android:layout_marginEnd="56dp"
            android:layout_marginRight="56dp"
            android:text="Query" />

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_alignParentLeft="true"
            android:layout_alignParentStart="true"
            android:layout_below="@+id/button"
            android:layout_marginTop="68dp">

            <TextView
                android:id="@+id/textView"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentLeft="true"
                android:layout_alignParentStart="true"
                android:layout_alignParentTop="true"
                android:layout_marginLeft="36dp"
                android:layout_marginStart="36dp"
                android:layout_marginTop="32dp"
                android:text="@{data.userEntity.name}" />

            <TextView
                android:id="@+id/textView2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignLeft="@+id/textView"
                android:layout_alignStart="@+id/textView"
                android:layout_below="@+id/textView"
                android:layout_marginTop="41dp"
                android:text="@{data.books.get(0).book}" />

            <TextView
                android:id="@+id/textView3"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignLeft="@+id/textView2"
                android:layout_alignStart="@+id/textView2"
                android:layout_centerVertical="true"
                android:text="@{data.books.get(0).desc}" />
        </RelativeLayout>
    </RelativeLayout>
</layout>

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,100评论 18 139
  • PLEASE READ THE FOLLOWING APPLE DEVELOPER PROGRAM LICENSE...
    念念不忘的阅读 13,305评论 5 6
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,296评论 18 399
  • 配置 修改config/database.php在connection数组中添加mongodb的配置信息,如下 '...
    ingming阅读 8,337评论 3 8
  • 一. Java基础部分.................................................
    wy_sure阅读 3,731评论 0 11