JetPack轻量级数据库Room使用和原理解析

学习目标:

JetPack轻量级数据库Room原理解析

学习内容:

官网链接

  • 简单介绍
    Room是一个轻量级orm数据库,是对SQLite的再封装,使用起来比SQLite更加简单, 通过注解的方式标记相关功能,编译时自动生成响应的实现类(Impl)。
  • Room三大概念
    • @Entity
      标识数据库中的表
    • @DAO
      标记数据操作的sql语句
    • @Database
      必须是扩展RoomDatabase的抽象类。标记数据库的创建/管理类。
      还有其他的注解,就不一个一个列举了,大家可以自行翻阅官方文档。

开始使用
首先依赖库

def room_version = "2.2.6"
//注解
implementation "androidx.room:room-runtime:$room_version"
//注解处理器
annotationProcessor "androidx.room:room-compiler:$room_version"

定义实体类

package com.suyong.myroom.bean;

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

//所有的成员变量(不被@Ignore修饰的都会创建表字段)
//由此看出不仅有注解加反射的模式还有orm(对象关系映射型)结构。
@Entity
public class User {
    //自增主键
    @PrimaryKey(autoGenerate = true)
    @NonNull
    public int uid;

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

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

    //不需要创建该字段
    @Ignore
    public String nickName;
}

定义DAO类

//对表进行修改数据的类
@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(onConflict = OnConflictStrategy.REPLACE)
    void insertAll(User... users);

    @Delete
    void delete(User user);
}

定义创建数据库的类

//记住必须是抽象类
//必须继承RoomDatabase
//必须定义要创建的抽象的类
@Database(entities = {User.class}, version = 1)
public abstract class AbstractAppDatabase extends RoomDatabase {
    public abstract UserDao userDao();
}

愉快的使用了。

//创建临时数据库
//进程杀死就没了
Room.inMemoryDatabaseBuilder(getApplicationContext(),AbstractAppDatabase.class);

//永久数据库
//这里返回的其实通过apt技术生成的AbstractAppDatabase_Impl对象
AbstractAppDatabase db = Room.databaseBuilder(getApplicationContext(),
                AbstractAppDatabase.class, "MyDatabase")
                //是否运行再主线程做数据库操作
                .allowMainThreadQueries()
                //回调
                .addCallback(new RoomDatabase.Callback() {
                    @Override
                    public void onCreate(@NonNull SupportSQLiteDatabase db) {
                        super.onCreate(db);
                    }

                    @Override
                    public void onOpen(@NonNull SupportSQLiteDatabase db) {
                        super.onOpen(db);
                    }

                    @Override
                    public void onDestructiveMigration(@NonNull SupportSQLiteDatabase db) {
                        super.onDestructiveMigration(db);
                    }
                })
                //设置查询线程池
                .setQueryExecutor(executor)
                //实现加密存储,默认不加密存储
                .openHelperFactory(new SupportSQLiteOpenHelper.Factory() {
                    @Override
                    public SupportSQLiteOpenHelper create(SupportSQLiteOpenHelper.Configuration configuration) {
                        return null;
                    }
                })
                //数据库升级
                .addMigrations(new Migration(1,2) {
                    @Override
                    public void migrate(@NonNull SupportSQLiteDatabase database) {
                        
                    }
                })
                .build();
UserDao userDao = db.userDao();
List<User> users = userDao.getAll();

接下来我们从使用的角度来查看源码(省去花里胡哨的设置,直接构建)

AbstractAppDatabase db = Room.databaseBuilder(getApplicationContext(),
                AbstractAppDatabase.class, "MyDatabase").build();
 @NonNull
        public T build() {
            //noinspection ConstantConditions
            if (mContext == null) {
                throw new IllegalArgumentException("Cannot provide null context for the database.");
            }
            //noinspection ConstantConditions
            if (mDatabaseClass == null) {
                throw new IllegalArgumentException("Must provide an abstract class that"
                        + " extends RoomDatabase");
            }
            if (mQueryExecutor == null && mTransactionExecutor == null) {
                mQueryExecutor = mTransactionExecutor = ArchTaskExecutor.getIOThreadExecutor();
            } else if (mQueryExecutor != null && mTransactionExecutor == null) {
                mTransactionExecutor = mQueryExecutor;
            } else if (mQueryExecutor == null && mTransactionExecutor != null) {
                mQueryExecutor = mTransactionExecutor;
            }

            if (mMigrationStartAndEndVersions != null && mMigrationsNotRequiredFrom != null) {
                for (Integer version : mMigrationStartAndEndVersions) {
                    if (mMigrationsNotRequiredFrom.contains(version)) {
                        throw new IllegalArgumentException(
                                "Inconsistency detected. A Migration was supplied to "
                                        + "addMigration(Migration... migrations) that has a start "
                                        + "or end version equal to a start version supplied to "
                                        + "fallbackToDestructiveMigrationFrom(int... "
                                        + "startVersions). Start version: "
                                        + version);
                    }
                }
            }

            if (mFactory == null) {
                mFactory = new FrameworkSQLiteOpenHelperFactory();
            }

            if (mCopyFromAssetPath != null || mCopyFromFile != null) {
                if (mName == null) {
                    throw new IllegalArgumentException("Cannot create from asset or file for an "
                            + "in-memory database.");
                }
                if (mCopyFromAssetPath != null && mCopyFromFile != null) {
                    throw new IllegalArgumentException("Both createFromAsset() and "
                            + "createFromFile() was called on this Builder but the database can "
                            + "only be created using one of the two configurations.");
                }
                mFactory = new SQLiteCopyOpenHelperFactory(mCopyFromAssetPath, mCopyFromFile,
                        mFactory);
            }
            DatabaseConfiguration configuration =
                    new DatabaseConfiguration(
                            mContext,
                            mName,
                            mFactory,
                            mMigrationContainer,
                            mCallbacks,
                            mAllowMainThreadQueries,
                            mJournalMode.resolve(mContext),
                            mQueryExecutor,
                            mTransactionExecutor,
                            mMultiInstanceInvalidation,
                            mRequireMigration,
                            mAllowDestructiveMigrationOnDowngrade,
                            mMigrationsNotRequiredFrom,
                            mCopyFromAssetPath,
                            mCopyFromFile);
            T db = Room.getGeneratedImplementation(mDatabaseClass, DB_IMPL_SUFFIX);
            db.init(configuration);
            return db;
        }

很明显这个额build方法通过getGeneratedImplementation返回了一个对象。

static <T, C> T getGeneratedImplementation(Class<C> klass, String suffix) {
        final String fullPackage = klass.getPackage().getName();
        String name = klass.getCanonicalName();
        final String postPackageName = fullPackage.isEmpty()
                ? name
                : (name.substring(fullPackage.length() + 1));
        final String implName = postPackageName.replace('.', '_') + suffix;
        //noinspection TryWithIdenticalCatches
        try {

            @SuppressWarnings("unchecked")
            //通过反射的方式创建一个对象(以_Impl为后缀)
            final Class<T> aClass = (Class<T>) Class.forName(
                    fullPackage.isEmpty() ? implName : fullPackage + "." + implName);
            return aClass.newInstance();
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("cannot find implementation for "
                    + klass.getCanonicalName() + ". " + implName + " does not exist");
        } catch (IllegalAccessException e) {
            throw new RuntimeException("Cannot access the constructor"
                    + klass.getCanonicalName());
        } catch (InstantiationException e) {
            throw new RuntimeException("Failed to create an instance of "
                    + klass.getCanonicalName());
        }
    }

所以AbstractAppDatabase db = Room.databaseBuilder(getApplicationContext(), AbstractAppDatabase.class, "MyDatabase").build() db对象其实是AbstractAppDatabase_Impl,所以db.userDao();就是AbstractAppDatabase_Impl.userDao()

 @Override
  public UserDao userDao() {
    if (_userDao != null) {
      return _userDao;
    } else {
      synchronized(this) {
        if(_userDao == null) {
         //可以看到这里创建了一个UserDao_Impl类
          _userDao = new UserDao_Impl(this);
        }
        return _userDao;
      }
    }
  }

所以userDao.getAll();就是UserDao_Impl.getAll();

public final class UserDao_Impl implements UserDao {
......
//具体查询数据库数据的实现类
 @Override
  public List<User> getAll() {
    final String _sql = "SELECT * FROM user";
    final RoomSQLiteQuery _statement = RoomSQLiteQuery.acquire(_sql, 0);
    __db.assertNotSuspendingTransaction();
    final Cursor _cursor = DBUtil.query(__db, _statement, false, null);
    try {
      final int _cursorIndexOfUid = CursorUtil.getColumnIndexOrThrow(_cursor, "uid");
      final int _cursorIndexOfFirstName = CursorUtil.getColumnIndexOrThrow(_cursor, "first_name");
      final int _cursorIndexOfLastName = CursorUtil.getColumnIndexOrThrow(_cursor, "last_name");
      final List<User> _result = new ArrayList<User>(_cursor.getCount());
      while(_cursor.moveToNext()) {
        final User _item;
        _item = new User();
        _item.uid = _cursor.getInt(_cursorIndexOfUid);
        _item.firstName = _cursor.getString(_cursorIndexOfFirstName);
        _item.lastName = _cursor.getString(_cursorIndexOfLastName);
        _result.add(_item);
      }
      return _result;
    } finally {
      _cursor.close();
      _statement.release();
    }
  }
 ......
}

到这里就结束了,Room就是采用注解的方式,通过APT(编译时技术)生成了我们所需要的实现类(以_Impl结尾的)来实现我们具体的业务逻辑。
APT 技术我也出过一个案例,有兴趣的可以看一下。
Android编译时技术,仿照ButterKnife,实现自己的自动注入框架


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