android Room的简单使用

使用Room操作数据库

先看一下Room由哪几部分组成的

  • Entity,用来封装实际数据的实体类,它和数据库中的表一一对应,有几张表就建立几个实体类,建立好Entity后,表中各列会自动生成。
  • Dao, 将对数据库的各项操作都封装在这里,逻辑层直接和Dao打交道,不直接操作数据库。
  • Database, 定义数据库的关键信息,如版本号,包含的实体类,以及提供Dao层的访问实例。

下面看一下使用方式

1、导入依赖

    implementation 'androidx.room:room-runtime:2.1.0'
    annotationProcessor "androidx.room:room-compiler:2.1.0"

2 构建实体类

这里构建一个简单的Book类,通过@Entity标注为实体类,它的所有字段默认都会称为表中的一列,默认类名作为表名,列名作为字段名。

package com.demo.model;

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

@Entity
public class Book {
    @ColumnInfo()   // 这里可以指定列的名称,但是我们不直接操作数据库,使用默认即可
    private String name;

    @PrimaryKey(autoGenerate = true)
    private long bookId = 0;
    @ColumnInfo
    private int page;
    @ColumnInfo
    private float price;

    public Book(String name, int page, float price) {
        this.name = name;
        this.page = page;
        this.price = price;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPage() {
        return page;
    }

    public void setPage(int page) {
        this.page = page;
    }

    public float getPrice() {
        return price;
    }

    public void setPrice(float price) {
        this.price = price;
    }

    public long getBookId() {
        return bookId;
    }

    public void setBookId(long bookId) {
        this.bookId = bookId;
    }

    @NonNull
    @Override
    public String toString() {
        return "name: "+getName()+" page: "+String.valueOf(getPage())+" price: "+String.valueOf(getPrice());
    }
}

如果想要修改表名

@Entity(tableName = "book")

默认情况下,类中每个成员即便不添加ColumnInfo注解都会称为一列,如果不要它称为一列,可以通过@Ignore注解忽略

@Ignore
public float price;

主键,复合主键
上面我们使用@PrimaryKey指定单个主键,也可以通过@PrimaryKeys指定复合主键,比如有一个User类,我们希望将firstName和lastName作为联合主键,可以这样做。

@Entity(primaryKeys = {"firstName", "lastName"})

索引
如果希望加快访问速度,可以在@Entity中添加索引

@Entity(indeces = {@index("name")})

也可以添加多个索引

@Entity(indices = {@Index("name"), @Index(value = {"last_name", "address"})})

unique
有时候, 需要保证某个字段或者多个字段组成的组唯一,那么可以通过将@Index的unique属性设置为true来确保唯一性,比如我们希望firstName和lastName 的组合唯一。

@Entity( indices = {@index(value = {"firstName", "lastName"}, unique = true)})

外键
通过@ForeignKey定义外键

@Entity(foreignKeys = @ForeignKey(entity = User.class,
                                  parentColumns = "id",
                                  childColumns = "user_id"))
class Book {
    @PrimaryKey
    public int bookId;

    public String title;

    @ColumnInfo(name = "user_id")
    public int userId;
}

上例中,我们通过将parentColumns和 childColumns 分别进行指定。

3、定义Dao

这里我定义的是一个接口,提供了对数据库的各项操作,如增删改查等功能,可以按照自己的需求定制

package com.demo.model;

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

import java.util.List;

@Dao
public interface BookDao {
    @Query("select * from Book")
    List<Book> selectAll();

    // 根据书名查找
    @Query("select * from Book where name = (:bookName)")
    List<Book> selectByName(String bookName);

    @Update
    void updatePrice(Book book);

    @Query("delete from Book where name = :name")   // 这里是删除操作,但是用的是Query注解,因为Delete注解不能加sql语句
    void deleteBook(String name);

    @Insert
    void insertBook(Book book);

}

从上面的几个函数中可以知道,对于比较简单的操作,我们不需要提供sql语句,只需要进行注解即可,对于逻辑比较复杂的功能,我们提供sql语句,并且它会自动检查sql语句的正确性,重点关注一下sql语句传参的方式,通过 “:”获取参数。
插入时冲突处理
通过Insert插入数据时,可能会发生冲突,默认情况下处理方式是OnConflictStrategy.ABORT,会抛出异常,我们可以通过指定为OnConflictStrategy.REPLACE让新数据取代老数据。

@Insert(onConfict = OnConfictStrategy.REPLACE)

传入一个集合参数
@Dao
public interface MyDao {
@Query(“SELECT first_name, last_name FROM user WHERE region IN (:regions)”)
public List loadUsersFromRegions(List regions);
}
返回一个cursor对象
Room还支持返回一个cursor对象,但是觉得这样没有必要。

@Dao
public interface MyDao {
    @Query("SELECT * FROM user WHERE age > :minAge LIMIT 5")
    public Cursor loadRawUsersOlderThan(int minAge);
}

4、定义Database

package com.demo.model;

import android.content.Context;

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

@Database(version = 1,entities = {Book.class},exportSchema=true)
public abstract class BookDatabase extends RoomDatabase {
    // 提供Dao的实例
    public abstract BookDao bookDao();
    // 单例
    private BookDatabase database;

    public BookDatabase getInstance(Context context){
        if (database == null){
            synchronized (BookDatabase.class){
                if (database == null){
                    database = Room.databaseBuilder(context.getApplicationContext(),BookDatabase.class,"book.db").build();
                }
            }
        }
        return database;
    }
}

定义一个 BookDatabase ,继承RoomDatabase,同时在注解中指明实体集entities,版本号version等, exportSchema 属性一般指定为true,否则可能会报错。对于获取BookDao实例的方法,我们也只需要声明方法即可,底层会自动完成。BookDatabase 结合单例模式进行设计,具体实例化使用方法Room.databaseBuilder()去完成。
下面看一个例子

package com.demo.dashboard;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

import com.demo.model.AppDatabase;
import com.demo.model.Book;
import com.demo.model.BookDao;
import com.demo.model.BookDatabase;
import com.demo.model.EventMsg;
import com.demo.model.User;
import com.demo.model.UserDao;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;

import java.util.List;

public class Activity2 extends AppCompatActivity {

    private static final String TAG = "Activity2";

    private BookDao bookDao;

    private Button btnSearch;
    private Button btnDelete;
    private Button btnUpdate;
    private Button btnInsert;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_2);
        EventBus.getDefault().register(this);


        bookDao = BookDatabase.getInstance(this).bookDao();

        Book book1 = new Book("安徒生童话",389,19.88f);
        Book book2 = new Book("Android入门",469,88.88f);

        EventBus.getDefault().post(new EventMsg(4,book1));
        EventBus.getDefault().post(new EventMsg(4,book2));

        btnSearch = findViewById(R.id.btn1);
        btnDelete = findViewById(R.id.btn2);
        btnUpdate = findViewById(R.id.btn3);
        btnInsert = findViewById(R.id.btn4);

        btnSearch.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                EventBus.getDefault().post(new EventMsg(1,null));
            }
        });
        btnDelete.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                EventBus.getDefault().post(new EventMsg(2,new Book("Android入门",588,48.00f)));

            }
        });
        btnUpdate.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                EventBus.getDefault().post(new EventMsg(3,new Book("Android入门",588,48.00f)));
            }
        });
        btnInsert.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 懒得搜集输入,这里随便写一个
                EventBus.getDefault().post(new EventMsg(4,new Book("操作系统",400,38.00f)));
            }
        });
    }
    @Subscribe(threadMode = ThreadMode.ASYNC)
    public void dealEventMsg(EventMsg msg){
        switch (msg.type){
            case 1:
                Log.d(TAG, "dealEventMsg: ");
                List<Book> bookList =bookDao.selectAll();
                for (Book book:bookList){
                    Log.d(TAG, "bookInfo: "+book.toString());
                }
                break;
            case 2:
                bookDao.deleteBook(msg.book.getName());
                break;
            case 3:
                bookDao.updatePrice(msg.book);
            case 4:
                bookDao.insertBook(msg.book);
                break;

        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        EventBus.getDefault().unregister(this);
    }
}

对数据库的操作由于是耗时操作,所以必须放在子线程中完成,所以我在这里使用了EventBus机制。

5、数据库的升级

如果我们想要在Book中加入一个出版社,Room并没有提供比较简单的方法,和使用SQLHelper一样麻烦。

1、修改Book类

package com.demo.model;

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

@Entity
public class Book {
    @ColumnInfo()   // 这里可以指定列的名称,但是我们不直接操作数据库,使用默认即可
    private String name;

    @PrimaryKey(autoGenerate = true)
    private long bookId = 0;
    @ColumnInfo
    private int page;
    @ColumnInfo
    private float price;
    @ColumnInfo
    private String chuban;

    public Book(String name, int page, float price, String chuban) {
        this.name = name;
        this.page = page;
        this.price = price;
        this.chuban = chuban;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPage() {
        return page;
    }

    public void setPage(int page) {
        this.page = page;
    }

    public float getPrice() {
        return price;
    }

    public void setPrice(float price) {
        this.price = price;
    }

    public long getBookId() {
        return bookId;
    }

    public void setBookId(long bookId) {
        this.bookId = bookId;
    }

    public String getChuban() {
        return chuban;
    }

    public void setChuban(String chuban) {
        this.chuban = chuban;
    }

    @NonNull
    @Override
    public String toString() {
        return "name: "+getName()+" page: "+String.valueOf(getPage())+" price: "+String.valueOf(getPrice())
                +" 出版社:"+getChuban();
    }
}

2、修改BookDatabase

package com.demo.model;

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;

@Database(version = 2,entities = {Book.class})
public abstract class BookDatabase extends RoomDatabase {
    // 提供Dao的实例
    public abstract BookDao bookDao();
    // 单例
    private static BookDatabase database;

    public static BookDatabase getInstance(Context context){
        if (database == null){
            synchronized (BookDatabase.class){
                if (database == null){
                    // 提供一个Migration实例,指明迁移的版本号
                    Migration migration1_2 = new Migration(1,2) {
                        @Override
                        public void migrate(@NonNull SupportSQLiteDatabase database) {
                            database.execSQL("alter table Book add column chuban text");
                        }
                    };
                    database = Room.databaseBuilder(context.getApplicationContext(),BookDatabase.class,"book.db")
                            .addMigrations(migration1_2)
                            .build();
                }
            }
        }
        return database;
    }
}

上面有几个注意的点

  • BookDatabase注解中指明新的版本号
  • 提供一个迁移实例对象Migration,实现migrate方法
  • 在创建Database的时候将migration添加进去
    结果
    在这里插入图片描述
    没有做好重复的判断,有些数据重复插入了。

版权声明:本文为qq_41818873原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。