Android Room 框架公共模块源码深度剖析(四)

news2025/3/18 22:00:48

一、引言

在 Android 开发中,数据持久化是一个常见的需求。Android Room 框架作为 Android Jetpack 组件的一部分,为开发者提供了一个抽象层,使得在 SQLite 数据库上进行数据操作变得更加简单和高效。Room 框架包含多个模块,其中公共模块是其他模块的基础,它提供了一些通用的工具类、注解和接口,为整个框架的运行提供了支撑。本文将深入剖析 Android Room 框架的公共模块,从源码级别详细分析其实现原理和使用方法。

二、公共模块概述

2.1 公共模块的作用

公共模块是 Room 框架的基础,它定义了一些通用的接口、注解和工具类,供其他模块使用。这些接口和注解为 Room 框架的各个组件提供了统一的标准和规范,使得不同模块之间能够协同工作。例如,公共模块中定义了数据库实体类的注解,以及 DAO 接口的方法注解,这些注解在编译时会被处理,生成相应的代码。

2.2 公共模块的主要组件

公共模块主要包含以下几个方面的组件:

  • 注解:如 @Entity@Dao@Query 等,用于定义数据库实体类、数据访问对象和数据库查询语句。
  • 接口:如 RoomDatabaseSupportSQLiteOpenHelper 等,定义了数据库操作的基本接口。
  • 工具类:如 Room 类,提供了创建数据库实例的静态方法。

三、注解分析

3.1 @Entity 注解

@Entity 注解用于定义数据库中的实体类,每个实体类对应数据库中的一张表。

java

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

// 使用 @Entity 注解将该类标记为数据库实体类
@Entity(tableName = "users")
public class User {
    // 使用 @PrimaryKey 注解将 id 字段标记为主键
    @PrimaryKey(autoGenerate = true)
    private int id;
    private String name;
    private int age;

    // 构造函数
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // Getter 和 Setter 方法
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
3.1.1 源码分析

@Entity 注解的定义如下:

java

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

// 注解的保留策略为运行时,这样在运行时可以通过反射获取注解信息
@Retention(RetentionPolicy.RUNTIME)
// 注解的目标类型为类
@Target(ElementType.TYPE)
public @interface Entity {
    // 表名,默认为类名
    String tableName() default "";
    // 主键字段名数组
    String[] primaryKeys() default {};
    // 是否忽略冲突
    boolean ignoreConflicts() default false;
    // 索引数组
    Index[] indices() default {};
    // 外键数组
    ForeignKey[] foreignKeys() default {};
}

在编译时,Room 编译器会处理 @Entity 注解,根据注解中的信息生成相应的 SQL 语句来创建数据库表。例如,对于上述 User 类,会生成如下 SQL 语句:

sql

CREATE TABLE IF NOT EXISTS `users` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `age` INTEGER)

3.2 @Dao 注解

@Dao 注解用于定义数据访问对象(DAO)接口,DAO 接口中定义了对数据库的各种操作方法。

java

import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;
import java.util.List;

// 使用 @Dao 注解将该接口标记为数据访问对象接口
@Dao
public interface UserDao {
    // 使用 @Insert 注解标记插入方法
    @Insert
    void insert(User user);

    // 使用 @Query 注解标记查询方法
    @Query("SELECT * FROM users")
    List<User> getAllUsers();
}
3.2.1 源码分析

@Dao 注解的定义如下:

java

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

// 注解的保留策略为运行时,这样在运行时可以通过反射获取注解信息
@Retention(RetentionPolicy.RUNTIME)
// 注解的目标类型为接口
@Target(ElementType.TYPE)
public @interface Dao {
}

在编译时,Room 编译器会根据 @Dao 接口中的方法注解生成相应的实现类。例如,对于上述 UserDao 接口,会生成一个实现类,其中 insert 方法会将 User 对象插入到数据库中,getAllUsers 方法会从数据库中查询所有用户信息。

3.3 @Query 注解

@Query 注解用于定义数据库查询语句。

java

import androidx.room.Dao;
import androidx.room.Query;
import java.util.List;

// 使用 @Dao 注解将该接口标记为数据访问对象接口
@Dao
public interface UserDao {
    // 使用 @Query 注解标记查询方法,查询年龄大于指定值的用户
    @Query("SELECT * FROM users WHERE age > :age")
    List<User> getUsersByAge(int age);
}
3.3.1 源码分析

@Query 注解的定义如下:

java

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

// 注解的保留策略为运行时,这样在运行时可以通过反射获取注解信息
@Retention(RetentionPolicy.RUNTIME)
// 注解的目标类型为方法
@Target(ElementType.METHOD)
public @interface Query {
    // SQL 查询语句
    String value();
}

在编译时,Room 编译器会解析 @Query 注解中的 SQL 语句,并根据方法的参数和返回值类型生成相应的查询代码。例如,对于上述 getUsersByAge 方法,会生成如下代码:

java

public List<User> getUsersByAge(int age) {
    String sql = "SELECT * FROM users WHERE age > ?";
    // 执行 SQL 查询
    Cursor cursor = db.rawQuery(sql, new String[]{String.valueOf(age)});
    try {
        List<User> users = new ArrayList<>();
        while (cursor.moveToNext()) {
            User user = new User();
            user.setId(cursor.getInt(cursor.getColumnIndex("id")));
            user.setName(cursor.getString(cursor.getColumnIndex("name")));
            user.setAge(cursor.getInt(cursor.getColumnIndex("age")));
            users.add(user);
        }
        return users;
    } finally {
        cursor.close();
    }
}

四、接口分析

4.1 RoomDatabase 接口

RoomDatabase 是 Room 框架的核心接口,它定义了数据库操作的基本方法。

java

import androidx.room.RoomDatabase;
import androidx.room.Database;
import androidx.room.Room;
import android.content.Context;

// 使用 @Database 注解定义数据库类
@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
    public abstract UserDao userDao();

    private static AppDatabase INSTANCE;

    // 获取数据库实例的静态方法
    public static AppDatabase getInstance(Context context) {
        if (INSTANCE == null) {
            synchronized (AppDatabase.class) {
                if (INSTANCE == null) {
                    // 使用 Room.databaseBuilder 方法创建数据库实例
                    INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
                            AppDatabase.class, "app-database")
                           .build();
                }
            }
        }
        return INSTANCE;
    }
}
4.1.1 源码分析

RoomDatabase 接口的定义如下:

java

import androidx.sqlite.db.SupportSQLiteDatabase;
import androidx.sqlite.db.SupportSQLiteOpenHelper;
import java.util.List;
import java.util.concurrent.Executor;

// 抽象类,定义了数据库操作的基本方法
public abstract class RoomDatabase {
    // 获取数据库辅助类
    public abstract SupportSQLiteOpenHelper getOpenHelper();
    // 获取数据库查询执行器
    public abstract Executor getQueryExecutor();
    // 数据库回调列表
    private final List<Callback> mCallbacks = new ArrayList<>();
    // 数据库是否已经打开
    private boolean mIsOpen;

    // 注册数据库回调
    public void registerCallback(Callback callback) {
        mCallbacks.add(callback);
    }

    // 注销数据库回调
    public void unregisterCallback(Callback callback) {
        mCallbacks.remove(callback);
    }

    // 数据库回调接口
    public static abstract class Callback {
        // 数据库创建时调用
        public void onCreate(SupportSQLiteDatabase db) {}
        // 数据库打开时调用
        public void onOpen(SupportSQLiteDatabase db) {}
        // 数据库升级时调用
        public void onUpgrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) {}
        // 数据库降级时调用
        public void onDowngrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) {}
    }
}

RoomDatabase 是一个抽象类,具体的实现由 Room 编译器生成。在上述 AppDatabase 类中,通过 @Database 注解定义了数据库类,entities 属性指定了数据库中包含的实体类,version 属性指定了数据库的版本号。getInstance 方法使用单例模式获取数据库实例,通过 Room.databaseBuilder 方法创建数据库实例。

4.2 SupportSQLiteOpenHelper 接口

SupportSQLiteOpenHelper 接口定义了数据库辅助类的基本方法,用于管理数据库的创建和版本升级。

java

import androidx.sqlite.db.SupportSQLiteDatabase;
import androidx.sqlite.db.SupportSQLiteOpenHelper;
import android.content.Context;

// 实现 SupportSQLiteOpenHelper 接口的数据库辅助类
public class AppDatabaseHelper implements SupportSQLiteOpenHelper {
    private final SupportSQLiteOpenHelper mDelegate;

    public AppDatabaseHelper(Context context) {
        // 创建 SQLiteOpenHelper 的委托对象
        mDelegate = new FrameworkSQLiteOpenHelperFactory().create(new Configuration(
                context,
                "app-database",
                null,
                new SupportSQLiteOpenHelper.Callback(1) {
                    @Override
                    public void onCreate(SupportSQLiteDatabase db) {
                        // 创建数据库表
                        db.execSQL("CREATE TABLE IF NOT EXISTS `users` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `age` INTEGER)");
                    }

                    @Override
                    public void onUpgrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) {
                        // 数据库升级逻辑
                        if (oldVersion < 2) {
                            // 执行升级操作
                            db.execSQL("ALTER TABLE users ADD COLUMN email TEXT");
                        }
                    }
                }
        ));
    }

    @Override
    public String getDatabaseName() {
        return mDelegate.getDatabaseName();
    }

    @Override
    public void setWriteAheadLoggingEnabled(boolean enabled) {
        mDelegate.setWriteAheadLoggingEnabled(enabled);
    }

    @Override
    public SupportSQLiteDatabase getWritableDatabase() {
        return mDelegate.getWritableDatabase();
    }

    @Override
    public SupportSQLiteDatabase getReadableDatabase() {
        return mDelegate.getReadableDatabase();
    }

    @Override
    public void close() {
        mDelegate.close();
    }
}
4.2.1 源码分析

SupportSQLiteOpenHelper 接口的定义如下:

java

import androidx.sqlite.db.SupportSQLiteDatabase;

// 定义数据库辅助类的基本方法
public interface SupportSQLiteOpenHelper {
    // 获取数据库名称
    String getDatabaseName();
    // 设置是否启用预写日志
    void setWriteAheadLoggingEnabled(boolean enabled);
    // 获取可写数据库
    SupportSQLiteDatabase getWritableDatabase();
    // 获取可读数据库
    SupportSQLiteDatabase getReadableDatabase();
    // 关闭数据库
    void close();

    // 数据库回调接口
    abstract class Callback {
        // 数据库版本号
        public final int version;

        public Callback(int version) {
            this.version = version;
        }

        // 数据库创建时调用
        public abstract void onCreate(SupportSQLiteDatabase db);
        // 数据库升级时调用
        public abstract void onUpgrade(SupportSQLiteDatabase db, int oldVersion, int newVersion);
        // 数据库降级时调用
        public void onDowngrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) {}
        // 数据库打开时调用
        public void onOpen(SupportSQLiteDatabase db) {}
    }

    // 数据库配置类
    class Configuration {
        public final Context context;
        public final String name;
        public final Factory factory;
        public final Callback callback;
        public final boolean allowMainThreadQueries;
        public final boolean journalMode;

        public Configuration(Context context, String name, Factory factory, Callback callback,
                             boolean allowMainThreadQueries, boolean journalMode) {
            this.context = context;
            this.name = name;
            this.factory = factory;
            this.callback = callback;
            this.allowMainThreadQueries = allowMainThreadQueries;
            this.journalMode = journalMode;
        }
    }

    // 数据库辅助类工厂接口
    interface Factory {
        SupportSQLiteOpenHelper create(Configuration configuration);
    }
}

SupportSQLiteOpenHelper 接口定义了数据库辅助类的基本方法,如获取数据库名称、获取可写 / 可读数据库、关闭数据库等。Callback 接口定义了数据库创建、升级、降级和打开时的回调方法。Configuration 类用于配置数据库的相关信息,Factory 接口用于创建数据库辅助类实例。在上述 AppDatabaseHelper 类中,实现了 SupportSQLiteOpenHelper 接口,并通过 FrameworkSQLiteOpenHelperFactory 创建了一个数据库辅助类的委托对象。

五、工具类分析

5.1 Room

Room 类是一个工具类,提供了创建数据库实例的静态方法。

java

import androidx.room.Room;
import android.content.Context;

// 使用 Room 类创建数据库实例
public class DatabaseManager {
    private static AppDatabase appDatabase;

    public static AppDatabase getAppDatabase(Context context) {
        if (appDatabase == null) {
            // 使用 Room.databaseBuilder 方法创建数据库实例
            appDatabase = Room.databaseBuilder(context.getApplicationContext(),
                    AppDatabase.class, "app-database")
                   .build();
        }
        return appDatabase;
    }
}
5.1.1 源码分析

Room 类的定义如下:

java

import androidx.room.RoomDatabase;
import androidx.room.RoomDatabase.Builder;
import androidx.sqlite.db.SupportSQLiteOpenHelper;
import androidx.sqlite.db.SupportSQLiteOpenHelper.Factory;
import android.content.Context;

// 工具类,提供创建数据库实例的静态方法
public class Room {
    // 创建数据库构建器
    public static <T extends RoomDatabase> Builder<T> databaseBuilder(Context context,
                                                                    Class<T> klass,
                                                                    String name) {
        return new Builder<>(context, klass, name);
    }

    // 创建内存数据库构建器
    public static <T extends RoomDatabase> Builder<T> inMemoryDatabaseBuilder(Context context,
                                                                              Class<T> klass) {
        return new Builder<>(context, klass, null);
    }

    // 数据库构建器类
    public static class Builder<T extends RoomDatabase> {
        private final Context mContext;
        private final Class<T> mKlass;
        private final String mName;
        private Factory mOpenHelperFactory;
        private RoomDatabase.Callback mCallback;
        private boolean mAllowMainThreadQueries;
        private boolean mJournalMode;

        public Builder(Context context, Class<T> klass, String name) {
            mContext = context;
            mKlass = klass;
            mName = name;
        }

        // 设置数据库辅助类工厂
        public Builder<T> openHelperFactory(Factory factory) {
            mOpenHelperFactory = factory;
            return this;
        }

        // 设置数据库回调
        public Builder<T> addCallback(RoomDatabase.Callback callback) {
            if (mCallback == null) {
                mCallback = callback;
            } else {
                mCallback = new CompositeCallback(mCallback, callback);
            }
            return this;
        }

        // 设置是否允许在主线程进行数据库查询
        public Builder<T> allowMainThreadQueries() {
            mAllowMainThreadQueries = true;
            return this;
        }

        // 设置日志模式
        public Builder<T> setJournalMode(boolean journalMode) {
            mJournalMode = journalMode;
            return this;
        }

        // 构建数据库实例
        public T build() {
            if (mOpenHelperFactory == null) {
                mOpenHelperFactory = new FrameworkSQLiteOpenHelperFactory();
            }
            SupportSQLiteOpenHelper.Configuration configuration =
                    SupportSQLiteOpenHelper.Configuration.builder(mContext)
                           .name(mName)
                           .callback(new RoomOpenHelper(mKlass, mCallback))
                           .factory(mOpenHelperFactory)
                           .allowMainThreadQueries(mAllowMainThreadQueries)
                           .journalMode(mJournalMode)
                           .build();
            T database = RoomDatabase.newInstance(mContext, mKlass, configuration);
            if (mCallback != null) {
                database.registerCallback(mCallback);
            }
            return database;
        }
    }

    // 组合回调类
    private static class CompositeCallback extends RoomDatabase.Callback {
        private final RoomDatabase.Callback mCallback1;
        private final RoomDatabase.Callback mCallback2;

        public CompositeCallback(RoomDatabase.Callback callback1,
                                 RoomDatabase.Callback callback2) {
            mCallback1 = callback1;
            mCallback2 = callback2;
        }

        @Override
        public void onCreate(SupportSQLiteDatabase db) {
            mCallback1.onCreate(db);
            mCallback2.onCreate(db);
        }

        @Override
        public void onOpen(SupportSQLiteDatabase db) {
            mCallback1.onOpen(db);
            mCallback2.onOpen(db);
        }

        @Override
        public void onUpgrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) {
            mCallback1.onUpgrade(db, oldVersion, newVersion);
            mCallback2.onUpgrade(db, oldVersion, newVersion);
        }

        @Override
        public void onDowngrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) {
            mCallback1.onDowngrade(db, oldVersion, newVersion);
            mCallback2.onDowngrade(db, oldVersion, newVersion);
        }
    }
}

Room 类提供了 databaseBuilderinMemoryDatabaseBuilder 两个静态方法,用于创建数据库构建器。Builder 类用于配置数据库的相关信息,如数据库辅助类工厂、数据库回调、是否允许在主线程进行数据库查询等。build 方法用于构建数据库实例。在上述 DatabaseManager 类中,使用 Room.databaseBuilder 方法创建了一个数据库实例。

六、类型转换器分析

6.1 类型转换器的作用

在 Room 中,实体类的字段类型必须是 SQLite 支持的基本类型(如 intlongString 等)。如果实体类的字段类型不是基本类型,就需要使用类型转换器将其转换为基本类型进行存储和读取。

java

import androidx.room.TypeConverter;
import java.util.Date;

// 类型转换器类
public class DateConverter {
    // 将 Date 类型转换为 Long 类型
    @TypeConverter
    public static Long toTimestamp(Date date) {
        return date == null ? null : date.getTime();
    }

    // 将 Long 类型转换为 Date 类型
    @TypeConverter
    public static Date toDate(Long timestamp) {
        return timestamp == null ? null : new Date(timestamp);
    }
}
6.1.1 源码分析

类型转换器通过 @TypeConverter 注解标记的方法实现。在上述 DateConverter 类中,toTimestamp 方法将 Date 类型转换为 Long 类型,toDate 方法将 Long 类型转换为 Date 类型。在使用类型转换器时,需要在 @Database 注解中指定类型转换器类。

java

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

// 使用 @Database 注解定义数据库类,并指定类型转换器类
@Database(entities = {User.class}, version = 1)
@TypeConverters({DateConverter.class})
public abstract class AppDatabase extends RoomDatabase {
    public abstract UserDao userDao();
}

在编译时,Room 编译器会处理类型转换器,将实体类中的非基本类型字段转换为基本类型进行存储和读取。

七、事务处理分析

7.1 事务的作用

在数据库操作中,事务用于确保一组操作要么全部成功,要么全部失败。Room 框架提供了事务处理的支持。

java

import androidx.room.RoomDatabase;
import androidx.room.Transaction;

// 在 DAO 接口中定义事务方法
@Dao
public interface UserDao {
    // 插入用户
    @Insert
    void insert(User user);

    // 更新用户
    @Query("UPDATE users SET name = :name WHERE id = :id")
    void updateUser(int id, String name);

    // 定义事务方法
    @Transaction
    default void insertAndUpdate(User user, int updateId, String newName) {
        insert(user);
        updateUser(updateId, newName);
    }
}
7.1.1 源码分析

@Transaction 注解用于标记事务方法。在上述 UserDao 接口中,insertAndUpdate 方法使用 @Transaction 注解标记,该方法包含了插入和更新两个操作。在编译时,Room 编译器会生成相应的代码,将这两个操作封装在一个事务中执行。

java

// 生成的事务方法实现
@Override
public void insertAndUpdate(User user, int updateId, String newName) {
    db.beginTransaction();
    try {
        insert(user);
        updateUser(updateId, newName);
        db.setTransactionSuccessful();
    } finally {
        db.endTransaction();
    }
}

在执行事务方法时,会先调用 beginTransaction 方法开始事务,然后执行事务中的操作,最后调用 setTransactionSuccessful 方法标记事务成功,调用 endTransaction 方法结束事务。如果在事务执行过程中发生异常,setTransactionSuccessful 方法不会被调用,事务会自动回滚。

八、公共模块的编译时处理分析

8.1 编译时注解处理器

Room 框架使用编译时注解处理器来处理 @Entity@Dao@Query 等注解。编译时注解处理器会在编译阶段扫描代码中的注解,并根据注解信息生成相应的代码。

java

import androidx.room.compiler.processing.XProcessingEnv;
import androidx.room.compiler.processing.XProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.TypeElement;
import java.util.Set;

// 编译时注解处理器类
public class RoomProcessor implements XProcessor {
    private final XProcessingEnv mEnv;

    public RoomProcessor(ProcessingEnvironment env) {
        mEnv = XProcessingEnv.create(env);
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        // 返回支持的注解类型
        return Set.of(
                "androidx.room.Entity",
                "androidx.room.Dao",
                "androidx.room.Query"
        );
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        // 处理注解
        for (TypeElement annotation : annotations) {
            if (annotation.getQualifiedName().contentEquals("androidx.room.Entity")) {
                // 处理 @Entity 注解
                processEntityAnnotation(roundEnv, annotation);
            } else if (annotation.getQualifiedName().contentEquals("androidx.room.Dao")) {
                // 处理 @Dao 注解
                processDaoAnnotation(roundEnv, annotation);
            } else if (annotation.getQualifiedName().contentEquals("androidx.room.Query")) {
                // 处理 @Query 注解
                processQueryAnnotation(roundEnv, annotation);
            }
        }
        return true;
    }

    private void processEntityAnnotation(RoundEnvironment roundEnv, TypeElement annotation) {
        // 处理 @Entity 注解的逻辑
        Set<? extends Element> entities = roundEnv.getElementsAnnotatedWith(annotation);
        for (Element entity : entities) {
            // 生成数据库表的 SQL 语句
            generateTableSql(entity);
        }
    }

    private void processDaoAnnotation(RoundEnvironment roundEnv, TypeElement annotation) {
        // 处理 @Dao 注解的逻辑
        Set<? extends Element> daos = roundEnv.getElementsAnnotatedWith(annotation);
        for (Element dao : daos) {
            // 生成 DAO 接口的实现类
            generateDaoImplementation(dao);
        }
8.1.1 processEntityAnnotation 方法详细分析

java

private void processEntityAnnotation(RoundEnvironment roundEnv, TypeElement annotation) {
    // 获取所有被 @Entity 注解标记的类元素
    Set<? extends Element> entities = roundEnv.getElementsAnnotatedWith(annotation);
    for (Element entity : entities) {
        // 获取 @Entity 注解实例
        Entity entityAnnotation = entity.getAnnotation(Entity.class);
        String tableName = entityAnnotation.tableName();
        if (tableName.isEmpty()) {
            // 如果未指定表名,使用类名作为表名
            tableName = entity.getSimpleName().toString();
        }

        StringBuilder createTableSql = new StringBuilder();
        createTableSql.append("CREATE TABLE IF NOT EXISTS `").append(tableName).append("` (");

        // 获取类的所有字段元素
        List<? extends Element> fields = entity.getEnclosedElements().stream()
               .filter(e -> e.getKind() == ElementKind.FIELD)
               .collect(Collectors.toList());

        boolean isFirstColumn = true;
        for (Element field : fields) {
            if (!isFirstColumn) {
                createTableSql.append(", ");
            }
            isFirstColumn = false;

            String columnName = field.getSimpleName().toString();
            String columnType = getColumnType(field.asType());

            // 检查是否为主键
            PrimaryKey primaryKeyAnnotation = field.getAnnotation(PrimaryKey.class);
            if (primaryKeyAnnotation != null) {
                createTableSql.append("`").append(columnName).append("` ").append(columnType)
                       .append(" PRIMARY KEY");
                if (primaryKeyAnnotation.autoGenerate()) {
                    createTableSql.append(" AUTOINCREMENT");
                }
            } else {
                createTableSql.append("`").append(columnName).append("` ").append(columnType);
            }
        }

        createTableSql.append(")");
        // 生成数据库表的 SQL 语句
        System.out.println("Generated SQL for table " + tableName + ": " + createTableSql.toString());
    }
}

private String getColumnType(TypeMirror typeMirror) {
    if (typeMirror.toString().equals("int") || typeMirror.toString().equals("java.lang.Integer")) {
        return "INTEGER";
    } else if (typeMirror.toString().equals("long") || typeMirror.toString().equals("java.lang.Long")) {
        return "INTEGER";
    } else if (typeMirror.toString().equals("String")) {
        return "TEXT";
    } else if (typeMirror.toString().equals("double") || typeMirror.toString().equals("java.lang.Double")) {
        return "REAL";
    } else if (typeMirror.toString().equals("float") || typeMirror.toString().equals("java.lang.Float")) {
        return "REAL";
    }
    // 可以添加更多类型的映射
    return "TEXT";
}

processEntityAnnotation 方法中,首先获取所有被 @Entity 注解标记的类元素。对于每个实体类,获取 @Entity 注解中的表名,如果未指定表名,则使用类名作为表名。接着,遍历实体类的所有字段,根据字段类型和是否为主键生成相应的 SQL 列定义。最后,拼接出完整的 CREATE TABLE SQL 语句。getColumnType 方法用于将 Java 类型映射为 SQLite 类型。

8.1.2 processDaoAnnotation 方法详细分析

java

private void processDaoAnnotation(RoundEnvironment roundEnv, TypeElement annotation) {
    // 获取所有被 @Dao 注解标记的接口元素
    Set<? extends Element> daos = roundEnv.getElementsAnnotatedWith(annotation);
    for (Element dao : daos) {
        String daoClassName = dao.getSimpleName().toString();
        String implementationClassName = daoClassName + "_Impl";

        StringBuilder classBuilder = new StringBuilder();
        classBuilder.append("public class ").append(implementationClassName)
               .append(" implements ").append(daoClassName).append(" {\n");
        classBuilder.append("    private final RoomDatabase mDatabase;\n");

        classBuilder.append("    public ").append(implementationClassName)
               .append("(RoomDatabase database) {\n");
        classBuilder.append("        this.mDatabase = database;\n");
        classBuilder.append("    }\n");

        // 获取接口的所有方法元素
        List<? extends Element> methods = dao.getEnclosedElements().stream()
               .filter(e -> e.getKind() == ElementKind.METHOD)
               .collect(Collectors.toList());

        for (Element method : methods) {
            String methodName = method.getSimpleName().toString();
            String returnType = method.asType().toString();
            List<? extends VariableElement> parameters = ((ExecutableElement) method).getParameters();

            Insert insertAnnotation = method.getAnnotation(Insert.class);
            if (insertAnnotation != null) {
                // 处理插入方法
                classBuilder.append("    @Override\n");
                classBuilder.append("    public void ").append(methodName).append("(");
                for (int i = 0; i < parameters.size(); i++) {
                    VariableElement param = parameters.get(i);
                    classBuilder.append(param.asType().toString()).append(" ").append(param.getSimpleName());
                    if (i < parameters.size() - 1) {
                        classBuilder.append(", ");
                    }
                }
                classBuilder.append(") {\n");
                classBuilder.append("        mDatabase.beginTransaction();\n");
                classBuilder.append("        try {\n");
                for (VariableElement param : parameters) {
                    String entityClassName = param.asType().toString();
                    String tableName = getTableNameFromEntity(entityClassName);
                    StringBuilder insertSql = new StringBuilder();
                    insertSql.append("INSERT INTO `").append(tableName).append("` (");

                    // 获取实体类的字段
                    TypeElement entityElement = (TypeElement) mEnv.getElementUtils().getTypeElement(entityClassName);
                    List<? extends Element> fields = entityElement.getEnclosedElements().stream()
                           .filter(e -> e.getKind() == ElementKind.FIELD)
                           .collect(Collectors.toList());

                    boolean isFirstColumn = true;
                    for (Element field : fields) {
                        if (!isFirstColumn) {
                            insertSql.append(", ");
                        }
                        isFirstColumn = false;
                        insertSql.append("`").append(field.getSimpleName()).append("`");
                    }
                    insertSql.append(") VALUES (");
                    isFirstColumn = true;
                    for (int i = 0; i < fields.size(); i++) {
                        if (!isFirstColumn) {
                            insertSql.append(", ");
                        }
                        isFirstColumn = false;
                        insertSql.append("?");
                    }
                    insertSql.append(")");

                    classBuilder.append("            mDatabase.compileStatement("").append(insertSql.toString()).append("").executeInsert();\n");
                }
                classBuilder.append("            mDatabase.setTransactionSuccessful();\n");
                classBuilder.append("        } finally {\n");
                classBuilder.append("            mDatabase.endTransaction();\n");
                classBuilder.append("        }\n");
                classBuilder.append("    }\n");
            }

            Query queryAnnotation = method.getAnnotation(Query.class);
            if (queryAnnotation != null) {
                // 处理查询方法
                String sql = queryAnnotation.value();
                classBuilder.append("    @Override\n");
                classBuilder.append("    public ").append(returnType).append(" ").append(methodName).append("(");
                for (int i = 0; i < parameters.size(); i++) {
                    VariableElement param = parameters.get(i);
                    classBuilder.append(param.asType().toString()).append(" ").append(param.getSimpleName());
                    if (i < parameters.size() - 1) {
                        classBuilder.append(", ");
                    }
                }
                classBuilder.append(") {\n");
                classBuilder.append("        Cursor cursor = mDatabase.query("").append(sql).append("", new String[]{");
                for (int i = 0; i < parameters.size(); i++) {
                    VariableElement param = parameters.get(i);
                    classBuilder.append(param.getSimpleName());
                    if (i < parameters.size() - 1) {
                        classBuilder.append(", ");
                    }
                }
                classBuilder.append("});\n");
                classBuilder.append("        try {\n");
                if (returnType.startsWith("java.util.List")) {
                    // 返回列表类型
                    String entityClassName = returnType.substring(returnType.indexOf("<") + 1, returnType.length() - 1);
                    classBuilder.append("            List<").append(entityClassName).append("> result = new ArrayList<>();\n");
                    classBuilder.append("            while (cursor.moveToNext()) {\n");
                    classBuilder.append("                ").append(entityClassName).append(" item = new ").append(entityClassName).append("();\n");
                    // 获取实体类的字段
                    TypeElement entityElement = (TypeElement) mEnv.getElementUtils().getTypeElement(entityClassName);
                    List<? extends Element> fields = entityElement.getEnclosedElements().stream()
                           .filter(e -> e.getKind() == ElementKind.FIELD)
                           .collect(Collectors.toList());
                    for (Element field : fields) {
                        String fieldName = field.getSimpleName().toString();
                        String columnType = getColumnType(field.asType());
                        if (columnType.equals("INTEGER")) {
                            classBuilder.append("                item.set").append(capitalize(fieldName)).append("(cursor.getInt(cursor.getColumnIndex("").append(fieldName).append("")));\n");
                        } else if (columnType.equals("TEXT")) {
                            classBuilder.append("                item.set").append(capitalize(fieldName)).append("(cursor.getString(cursor.getColumnIndex("").append(fieldName).append("")));\n");
                        }
                        // 可以添加更多类型的处理
                    }
                    classBuilder.append("                result.add(item);\n");
                    classBuilder.append("            }\n");
                    classBuilder.append("            return result;\n");
                } else {
                    // 返回单个实体类型
                    String entityClassName = returnType;
                    classBuilder.append("            ").append(entityClassName).append(" item = null;\n");
                    if (cursor.moveToNext()) {
                        classBuilder.append("                item = new ").append(entityClassName).append("();\n");
                        // 获取实体类的字段
                        TypeElement entityElement = (TypeElement) mEnv.getElementUtils().getTypeElement(entityClassName);
                        List<? extends Element> fields = entityElement.getEnclosedElements().stream()
                               .filter(e -> e.getKind() == ElementKind.FIELD)
                               .collect(Collectors.toList());
                        for (Element field : fields) {
                            String fieldName = field.getSimpleName().toString();
                            String columnType = getColumnType(field.asType());
                            if (columnType.equals("INTEGER")) {
                                classBuilder.append("                item.set").append(capitalize(fieldName)).append("(cursor.getInt(cursor.getColumnIndex("").append(fieldName).append("")));\n");
                            } else if (columnType.equals("TEXT")) {
                                classBuilder.append("                item.set").append(capitalize(fieldName)).append("(cursor.getString(cursor.getColumnIndex("").append(fieldName).append("")));\n");
                            }
                            // 可以添加更多类型的处理
                        }
                    }
                    classBuilder.append("            return item;\n");
                }
                classBuilder.append("        } finally {\n");
                classBuilder.append("            cursor.close();\n");
                classBuilder.append("        }\n");
                classBuilder.append("    }\n");
            }
        }

        classBuilder.append("}\n");
        // 生成 DAO 接口的实现类
        System.out.println("Generated implementation class for " + daoClassName + ": " + classBuilder.toString());
    }
}

private String getTableNameFromEntity(String entityClassName) {
    TypeElement entityElement = (TypeElement) mEnv.getElementUtils().getTypeElement(entityClassName);
    Entity entityAnnotation = entityElement.getAnnotation(Entity.class);
    String tableName = entityAnnotation.tableName();
    if (tableName.isEmpty()) {
        tableName = entityElement.getSimpleName().toString();
    }
    return tableName;
}

private String capitalize(String str) {
    if (str == null || str.isEmpty()) {
        return str;
    }
    return Character.toUpperCase(str.charAt(0)) + str.substring(1);
}

processDaoAnnotation 方法中,首先获取所有被 @Dao 注解标记的接口元素。对于每个 DAO 接口,生成一个实现类。遍历接口的所有方法,根据方法上的注解(如 @Insert@Query)生成相应的实现代码。对于插入方法,生成插入 SQL 语句并在事务中执行;对于查询方法,生成查询 SQL 语句并根据返回类型处理查询结果。

8.2 编译时生成的代码使用分析

8.2.1 生成的数据库表创建代码使用

在应用启动时,Room 会根据编译时生成的 SQL 语句创建数据库表。例如,在 RoomDatabaseonCreate 回调中执行这些 SQL 语句:

java

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

    private static AppDatabase INSTANCE;

    public static AppDatabase getInstance(Context context) {
        if (INSTANCE == null) {
            synchronized (AppDatabase.class) {
                if (INSTANCE == null) {
                    INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
                            AppDatabase.class, "app-database")
                           .addCallback(new Callback() {
                                @Override
                                public void onCreate(SupportSQLiteDatabase db) {
                                    super.onCreate(db);
                                    // 执行生成的 SQL 语句创建表
                                    db.execSQL("CREATE TABLE IF NOT EXISTS `users` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `age` INTEGER)");
                                }
                            })
                           .build();
                }
            }
        }
        return INSTANCE;
    }
}
8.2.2 生成的 DAO 实现类使用

在应用中,通过 RoomDatabase 获取 DAO 实例并调用其方法:

java

AppDatabase database = AppDatabase.getInstance(context);
UserDao userDao = database.userDao();

User user = new User("John", 25);
userDao.insert(user);

List<User> users = userDao.getAllUsers();

这里的 userDao.insertuserDao.getAllUsers 方法会调用编译时生成的 DAO 实现类中的相应方法。

九、公共模块的性能优化分析

9.1 预编译 SQL 语句

在生成的 DAO 实现类中,对于插入和查询等操作,使用预编译的 SQL 语句。例如,在插入方法中:

java

mDatabase.compileStatement("INSERT INTO `users` (`name`, `age`) VALUES (?, ?)").executeInsert();

预编译 SQL 语句可以提高性能,因为数据库只需要解析一次 SQL 语句,后续执行时只需要绑定参数即可。

9.2 事务处理优化

在插入和更新等操作中,使用事务处理。例如,在插入多个实体时:

java

mDatabase.beginTransaction();
try {
    for (User user : users) {
        mDatabase.compileStatement("INSERT INTO `users` (`name`, `age`) VALUES (?, ?)").executeInsert();
    }
    mDatabase.setTransactionSuccessful();
} finally {
    mDatabase.endTransaction();
}

事务处理可以减少数据库的 I/O 操作,提高插入和更新的性能。

9.3 缓存机制

Room 框架内部可能使用了缓存机制,例如对于查询结果的缓存。当多次执行相同的查询时,如果数据没有发生变化,可能会直接从缓存中获取结果,而不需要再次查询数据库,从而提高查询性能。

十、公共模块的扩展性分析

10.1 自定义注解处理器

开发者可以自定义编译时注解处理器,扩展 Room 框架的功能。例如,可以定义一个新的注解,用于实现一些特定的数据库操作。

java

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

// 自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface CustomQuery {
    String value();
}

然后在自定义的注解处理器中处理这个注解:

java

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import java.util.Set;

// 自定义注解处理器
public class CustomQueryProcessor extends AbstractProcessor {
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        return Set.of("com.example.CustomQuery");
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        for (TypeElement annotation : annotations) {
            if (annotation.getQualifiedName().contentEquals("com.example.CustomQuery")) {
                Set<? extends Element> methods = roundEnv.getElementsAnnotatedWith(annotation);
                for (Element method : methods) {
                    CustomQuery customQueryAnnotation = method.getAnnotation(CustomQuery.class);
                    String sql = customQueryAnnotation.value();
                    // 处理自定义查询注解的逻辑
                    System.out.println("Custom query SQL: " + sql);
                }
            }
        }
        return true;
    }
}

10.2 自定义类型转换器

开发者可以自定义类型转换器,支持更多的数据类型。例如,支持自定义的枚举类型:

java

import androidx.room.TypeConverter;

// 自定义类型转换器
public class EnumConverter {
    @TypeConverter
    public static MyEnum toEnum(String value) {
        return value == null ? null : MyEnum.valueOf(value);
    }

    @TypeConverter
    public static String fromEnum(MyEnum enumValue) {
        return enumValue == null ? null : enumValue.name();
    }
}

然后在 @Database 注解中指定自定义的类型转换器:

java

@Database(entities = {MyEntity.class}, version = 1)
@TypeConverters({EnumConverter.class})
public abstract class MyDatabase extends RoomDatabase {
    public abstract MyDao myDao();
}

十一、公共模块的兼容性分析

11.1 与不同 Android 版本的兼容性

Room 框架的公共模块在设计上考虑了与不同 Android 版本的兼容性。例如,它使用了 SupportSQLiteOpenHelper 接口,该接口是 Android 支持库提供的,确保在不同版本的 Android 系统上都能正常使用 SQLite 数据库。同时,Room 框架会根据 Android 系统的版本进行一些优化和适配,例如在较新的 Android 版本上可能会使用更高效的数据库操作方法。

11.2 与其他 Jetpack 组件的兼容性

Room 框架是 Android Jetpack 组件的一部分,与其他 Jetpack 组件(如 LiveData、ViewModel、WorkManager 等)具有良好的兼容性。例如,Room 可以与 LiveData 结合使用,当数据库中的数据发生变化时,LiveData 会自动更新,从而实现数据的实时同步。

java

import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Query;

@Dao
public interface UserDao {
    @Query("SELECT * FROM users")
    LiveData<List<User>> getAllUsersLiveData();
}

在 ViewModel 中使用 LiveData:

java

import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import android.app.Application;
import java.util.List;

public class UserViewModel extends AndroidViewModel {
    private final UserDao userDao;
    private final LiveData<List<User>> allUsers;

    public UserViewModel(Application application) {
        super(application);
        AppDatabase database = AppDatabase.getInstance(application);
        userDao = database.userDao();
        allUsers = userDao.getAllUsersLiveData();
    }

    public LiveData<List<User>> getAllUsers() {
        return allUsers;
    }
}

在 Activity 或 Fragment 中观察 LiveData:

java

import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import android.os.Bundle;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        UserViewModel userViewModel = new ViewModelProvider(this).get(UserViewModel.class);
        userViewModel.getAllUsers().observe(this, new Observer<List<User>>() {
            @Override
            public void onChanged(List<User> users) {
                // 数据更新时的处理逻辑
            }
        });
    }
}

十二、总结

Android Room 框架的公共模块是整个框架的基础,它提供了注解、接口、工具类等核心组件,为数据库操作提供了统一的标准和规范。通过编译时注解处理器,Room 可以根据注解信息生成相应的代码,减少了开发者的工作量。同时,公共模块在性能优化、扩展性和兼容性方面都有很好的设计,使得 Room 框架在 Android 开发中得到了广泛的应用。开发者可以深入理解公共模块的源码,更好地使用 Room 框架进行数据持久化开发。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2317439.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ollama docker设置模型常驻显存

参考&#xff1a; https://github.com/ollama/ollama/issues/5272 https://deepseek.csdn.net/67cfd7c93b685529b708fdee.html 通过-e传入环境变量&#xff0c;ollama运行&#xff1a; docker run -d --gpusall -e OLLAMA_KEEP_ALIVE-1 -v ollama:/root/.ollama -p 11434:114…

无再暴露源站!群联AI云防护IP隐匿方案+防绕过实战

一、IP隐藏的核心原理 群联AI云防护通过三层架构实现源站IP深度隐藏&#xff1a; 流量入口层&#xff1a;用户访问域名解析至高防CNAME节点&#xff08;如ai-protect.example.com&#xff09;智能调度层&#xff1a;基于AI模型动态分配清洗节点&#xff0c;实时更新节点IP池回…

【深度学习|目标检测】YOLO系列anchor-based原理详解

YOLO之anchor-based 一、关于anchors的设置二、网络如何利用anchor来训练关于register_buffer训练阶段的anchor使用推理阶段的anchor使用 三、训练时的正负样本匹配静态策略&#xff1a;跨分支采样跨anchor采样跨grid采样 动态策略 总结起来其实就是&#xff1a;基于anchor-bas…

Linux 入门:权限的认识和学习

目录 一.shell命令以及运行原理 二.Linux权限的概念 1.Linux下两种用户 cannot open directory .: Permission denied 问题 2.Linux权限管理 1).是什么 2).为什么&#xff08;权限角色目标权限属性&#xff09; 3).文件访问者的分类&#xff08;角色&#xff09; 4).文…

搭建opensbi+kernel+rootfs及基本设备驱动开发流程

目录 一.编译qemu 运行opensbikernelrootfs 1.编译qemu-9.1.1 2.安装riscv64编译器 3. 编译opensbi 4.编译kernel 5.编译rootfs 设备驱动开发流程 1.安装 RISC-V 交叉编译工具链 2.驱动开发准备 3.编写简易中断控制器驱动&#xff08;PLIC&#xff09;​ 4.配置内核…

QT非UI设计器生成界面的国际化

目的 UI设计器生成界面的国际化&#xff0c;比较容易实现些&#xff0c;因为有现成的函数可以调用&#xff0c;基本过程如下&#xff1a; void MainWindow::on_actLang_CN_triggered() {//中文界面qApp->removeTranslator(trans);delete trans;transnew QTranslator;trans…

python | 输入日期,判断这一天是这一年的第几天

题目&#xff1a; 使用 python 编程&#xff0c;实现输入日期&#xff0c;判断这一天是这一年的第几天? 具体实现代码如下&#xff1a; import datetime year input(请输入年份&#xff1a;) month input(请输入月份&#xff1a;) day input(请输入天&#xff1a;) date…

单片机开发资源分析的实战——以STM32F103C8T6为例子的单片机资源分析

目录 第一点&#xff1a;为什么叫STM32F103C8T6 从资源手册拿到我们的对STM32F103C8T6的资源描述 第二件事情&#xff0c;关心我们的GPIO引脚输出 第三件事情&#xff1a;去找对应外设的说明部分 前言 本文章隶属于项目&#xff1a; Charliechen114514/BetterATK: This is…

Maven | 站在初学者的角度配置

目录 Maven 是什么 概述 常见错误 创建错误代码示例 正确代码示例 Maven 的下载 Maven 依赖源 Maven 环境 环境变量 CMD测试 Maven 文件配置 本地仓库 远程仓库 Maven 工程创建 IDEA配置Maven IDEA Maven插件 Maven 是什么 概述 Maven是一个项目管理和构建自…

【css酷炫效果】纯CSS实现3D翻转卡片动画

【css酷炫效果】纯CSS实现3D翻转卡片动画 缘创作背景html结构css样式完整代码效果图 想直接拿走的老板&#xff0c;链接放在这里&#xff1a;https://download.csdn.net/download/u011561335/90490472 缘 创作随缘&#xff0c;不定时更新。 创作背景 刚看到csdn出活动了&am…

并发编程面试题二

1、java线程常见的基本状态有哪些&#xff0c;这些状态分别是做什么的 &#xff08;1&#xff09;创建&#xff08;New&#xff09;&#xff1a;new Thread()&#xff0c;生成线程对象。 &#xff08;2&#xff09;就绪&#xff08;Runnable&#xff09;:当调用线程对象的sta…

Spring Cloud Stream - 构建高可靠消息驱动与事件溯源架构

一、引言 在分布式系统中&#xff0c;传统的 REST 调用模式往往导致耦合&#xff0c;难以满足高并发和异步解耦的需求。消息驱动架构&#xff08;EDA, Event-Driven Architecture&#xff09;通过异步通信、事件溯源等模式&#xff0c;提高了系统的扩展性与可观测性。 作为 S…

突破连接边界!O9201PM Wi-Fi 6 + 蓝牙 5.4 模块重新定义笔记本无线体验

在当今数字化时代&#xff0c;笔记本电脑已成为人们工作、学习和娱乐的必备工具。而无线连接技术&#xff0c;作为笔记本电脑与外界交互的关键桥梁&#xff0c;其性能的优劣直接关乎用户体验的好坏。当下&#xff0c;笔记本电脑无线连接领域存在诸多痛点&#xff0c;严重影响着…

Python----计算机视觉处理(Opencv:图像颜色替换)

一、开运算 开运算就是对图像先进行腐蚀操作&#xff0c; 然后进行膨胀操作。开运算可以去除二值化图中的小的噪点&#xff0c;并分离相连的物体。 其主要目的就是消除那些小白点 在开运算组件中&#xff0c;有一个叫做kernel的参数&#xff0c;指的是核的大小&#xff0c;通常…

一周学会Flask3 Python Web开发-SQLAlchemy查询所有数据操作-班级模块

锋哥原创的Flask3 Python Web开发 Flask3视频教程&#xff1a; 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 我们来新建一个的蓝图模块-班级模块&#xff0c;后面可以和学生模块&#xff0c;实现一对多的数据库操作。 blueprint下新建g…

Matlab 风力发电机磁悬浮轴承模型pid控制

1、内容简介 略 Matlab 174-风力发电机磁悬浮轴承模型pid控制 可以交流、咨询、答疑 2、内容说明 磁悬浮轴承具有无接触、无摩擦、高速度、高精度、能耗低、不需要需润滑无油污染、可靠性高、寿命长和密封等一系列显著的优点。将磁悬浮技术应用于风力发电机中可以降低风机切入…

FPGA中级项目1——IP核(ROM 与 RAM)

FPGA中级项目1——IP核&#xff08;ROM 与 RAM&#xff09; IP核简介 在 FPGA&#xff08;现场可编程门阵列&#xff09;设计中&#xff0c;IP 核&#xff08;Intellectual Property Core&#xff0c;知识产权核&#xff09;是预先设计好的、可重用的电路模块&#xff0c;用于实…

Matlab 基于专家pid控制的时滞系统

1、内容简介 Matlab 185-基于专家pid控制的时滞系统 可以交流、咨询、答疑 2、内容说明 略 在处理时滞系统&#xff08;Time Delay Systems&#xff09;时&#xff0c;使用传统的PID控制可能会面临挑战&#xff0c;因为时滞会导致系统的不稳定或性能下降。专家PID控制通过结…

Unity 笔记:在EditorWindow中绘制 Sorting Layer

在Unity开发过程中&#xff0c;可能会对旧资源进行批量修改&#xff0c;一个个手动修改费人费事&#xff0c;所以催生出了一堆批量工具。 分享一下在此过程中绘制 Sorting Layer 面板的代码脚本。 示意图&#xff1a; 在 EditorGUI 和 EditorGUILayer 中内置了 SortingLayerF…

2024浙江大学计算机考研上机真题

2024浙江大学计算机考研上机真题 2024浙江大学计算机考研复试上机真题 2024浙江大学计算机考研机试真题 2024浙江大学计算机考研复试机试真题 历年浙江大学计算机复试上机真题 历年浙江大学计算机复试机试真题 2024浙江大学计算机复试上机真题 2024浙江大学计算机复试机试真题 …