欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android开发之Room(Java)

程序员文章站 2022-05-14 13:05:48
...

Room 在 SQLite 上提供了一个抽象层,以便在充分利用 SQLite 的强大功能的同时,能够流畅地访问数据库。

处理大量结构化数据的应用可极大地受益于在本地保留这些数据。最常见的用例是缓存相关数据。这样,当设备无法访问网络时,用户仍可在离线状态下浏览相应内容。设备之后重新连接到网络后,用户发起的所有内容更改都会同步到服务器。

官方建议使用Room,而不是SQLite

使用Room库要在应用或模块的build.gradle文件中添加依赖:

    dependencies {
      def room_version = "2.2.3"

      implementation "androidx.room:room-runtime:$room_version"
      annotationProcessor "androidx.room:room-compiler:$room_version" // For Kotlin use kapt instead of annotationProcessor

      // optional - Kotlin Extensions and Coroutines support for Room
      implementation "androidx.room:room-ktx:$room_version"

      // optional - RxJava support for Room
      implementation "androidx.room:room-rxjava2:$room_version"

      // optional - Guava support for Room, including Optional and ListenableFuture
      implementation "androidx.room:room-guava:$room_version"

      // Test helpers
      testImplementation "androidx.room:room-testing:$room_version"
    }

Room包括3个主要组件:

  • 数据库:包含数据库持有者,并作为应用已保留的持久关系型数据的底层连接的主要接入点。

  • Entity:表示数据库中的表。

  • DAO:包含用于访问数据库的方法。

它们之间的关系如下
Android开发之Room(Java)

使用Room实体定义数据

使用示例:

    @Entity(primaryKeys = {"firstName", "lastName"})
    public class User {
		//@PrimaryKey
		//public int id;
		
        public String firstName;
        public String lastName;
    }

定义主键如上有两种方式
关于字段的访问权限:要保留某个字段,Room 必须拥有该字段的访问权限。您可以将某个字段设为公开字段,也可以为其提供 getter 和 setter。如果您使用 getter 和 setter 方法,则请注意,这些方法需遵循 Room 中的 JavaBeans 规范。
更多详情请参见

定义对象之间的关系

定义一对多关系
即使您不能使用直接关系,Room 仍允许您定义实体之间的外键约束。

例如,如果存在另一个名为 Book 的实体,您可以使用 @ForeignKey 注释定义该实体与 User 实体的关系,如以下代码段所示:

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

        public String title;

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

在 @ForeignKey 注释中添加 onDelete = CASCADE,可以实现级联删除
更多应用请参见

在数据库中创建视图

使用RoomDAO访问数据

@Insert,@Update,@Delete示例:

    @Dao
    public interface MyDao {
        @Insert(onConflict = OnConflictStrategy.REPLACE)
        public void insertUsers(User... users);

		@Update
        public void updateUsers(User... users);

		@Delete
        public void deleteUsers(User... users);
    }

查询信息:

@Query 是 DAO 类中使用的主要注释。它允许您对数据库执行读/写操作
简单查询示例:

    @Dao
    public interface MyDao {
        @Query("SELECT * FROM user")//括号内可以是任意其他SQL语句
        public User[] loadAllUsers();
    }

在方法返回对象与查询结果字段不匹配时会发出警告
可观察查询: 查询方法对应的返回指声明为LiveData<?>,例如:

    @Dao
    public interface MyDao {
        @Query("SELECT first_name, last_name FROM user WHERE region IN (:regions)")
        public LiveData<List<User>> loadUsersFromRegionsSync(List<String> regions);
    }

这样,应用的界面就会在数据发生变化时自动更新了
更多数据库访问相关请参见

最后一步:编写数据库类

使用 @Database 注释的类应满足以下条件:

  • 是扩展 RoomDatabase 的抽象类。
  • 在注释中添加与数据库关联的实体列表。
  • 包含具有 0 个参数且返回使用 @Dao 注释的类的抽象方法。

在运行时,您可以通过调用 Room.databaseBuilder() 或 Room.inMemoryDatabaseBuilder() 获取

示例模板:

 // Song and Album are classes annotated with @Entity.
 @Database(version = 1, entities = {Song.class, Album.class})
 abstract class MusicDatabase extends RoomDatabase {
   // SongDao is a class annotated with @Dao.
   abstract public SongDao getSongDao();
   // AlbumDao is a class annotated with @Dao.
   abstract public ArtistDao getArtistDao();
   // SongAlbumDao is a class annotated with @Dao.
   abstract public SongAlbumDao getSongAlbumDao();
 }

在上面的例子中有两个表(有entities属性声明)和三个DAO(对应三个获取DAO的方法)

两个获取方法:

public static Builder databaseBuilder (Context context,
Class klass,
String name)

参数/返回名 含义
context 数据库的上下文。这通常是应用程序上下文。
kclass 用注释Database和扩展 的抽象类RoomDatabase。
name 数据库文件的名称。
Builder 一个RoomDatabaseBuilder实例,用于创建数据库

public static Builder inMemoryDatabaseBuilder (Context context,
Class klass)

参数/返回名 含义
context 数据库的上下文。这通常是应用程序上下文。
kclass 用注释Database和扩展 的抽象类RoomDatabase。
Builder 一个RoomDatabaseBuilder实例,用于创建数据库

完成以上步骤,就可以在应用中获取database实例来对数据库进行操作了

实践案例

User

package com.example.demoroom;

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

@Entity
public class User {
    @PrimaryKey
    public int uid;

    @ColumnInfo(name = "first_name")
    public String firstName;

    @ColumnInfo(name = "last_name")
    public String lastName;
}

UserDAO

package com.example.demoroom;

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

import java.util.List;

@Dao
public interface UserDao {
    @Query("SELECT * FROM user")
    List<User> getAll();

    @Query("SELECT * FROM user WHERE uid IN (:userIds)")
    List<User> loadAllByIds(int[] userIds);

    @Query("SELECT * FROM user WHERE first_name LIKE :first AND " +
            "last_name LIKE :last LIMIT 1")
    User findByName(String first, String last);

    @Insert
    void insertAll(User... users);

    @Delete
    void delete(User user);
}

AppDataBase

package com.example.demoroom;

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

@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
    public abstract UserDao getuserDao();
}

MainActivity

package com.example.demoroom;

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

import androidx.appcompat.app.AppCompatActivity;
import androidx.room.Room;

import java.util.List;

public class MainActivity extends AppCompatActivity {

    UserDao uDAO;
    AppDatabase db;
    User u;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        db = Room.databaseBuilder(getApplicationContext(),
                AppDatabase.class, "database-name").build();
        u.firstName = new String("Mike");
        u.lastName = new String("Mathue");
        uDAO = db.getuserDao();
        Log.e("", "onCreate: here");

        ((Button)findViewById(R.id.insert)).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                uDAO.insertAll(u);
                Toast.makeText(MainActivity.this, "刚刚插入信息:firstname:"+u.firstName+"lastname:"+u.lastName, Toast.LENGTH_SHORT).show();
            }
        });

        ((Button)findViewById(R.id.show)).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                List<User> allUsers =  uDAO.getAll();
                StringBuffer message = new StringBuffer("刚刚插入信息:");
                for(int i = 0 ; i < allUsers.size() ; i++) {
                    message.append("firstname-"+i+allUsers.get(i).firstName+"lastname-"+i+allUsers.get(i).lastName);
                }
                Toast.makeText(MainActivity.this, message, Toast.LENGTH_SHORT).show();
            }
        });
    }
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:orientation="horizontal">

    <Button
        android:id="@+id/insert"
        android:text="insert a tuple"
        android:layout_height="wrap_content"
        android:layout_width="match_parent"
        tools:ignore="MissingConstraints" />

    <Button
        android:id="@+id/show"
        android:text="show the first tuple"
        android:layout_height="wrap_content"
        android:layout_width="match_parent"
        tools:ignore="MissingConstraints" />

</LinearLayout>
相关标签: android