java - Room Java - 是否可以在界面中运行事务?

标签 java android mysql android-room

我在我的 Android Studio 项目中使用 room 框架。我正在尝试在接口(interface)内创建事务。我已经从这里阅读了文档:https://developer.android.com/reference/androidx/room/Transaction

我知道我们应该在抽象类中而不是在接口(interface)中创建事务。我只是想知道这是否可能,因为我的项目中已经有十多个接口(interface),并且不想将它们重写为抽象类。

room transaction error in interface

最佳答案

您所尝试的操作在接口(interface)中是不可能的,因为您不能在接口(interface)中使用带有主体的方法。

更具体地说,您尝试执行多个语句(一条 UPDATE,然后一条 DELETE),但同时只能执行一条语句。

您可以选择定义一个触发器(更新后,如果可以从触发器内确定权重行),或者可能更可能使用抽象类,从而使用一个函数来执行多个语句或使用利用的方法(传递/或检索)SupportSQliteDatabase(使用抽象类更简单)。

  • 如果您需要触发器,则必须使用回调来创建触发器,因为 Room 不提供触发器注释。

然后,要利用事务,您将在函数之前有一个虚拟的@Query。例如

@Dao
abstract class TheClassForMethodsWithBodies {

    @Query("UPDATE visits SET date=:date WHERE id=5")
    void testUpdate(Date date);
    @Query("DELETE FROM wieght WHERE id_weight=1")
    void testDelete();

    @Query("")
    void test(Date date) {
        testUpdate(date);
        testDelete();
    }
}
  • 注意 - 代码本质上是代码,尚未编译、运行或测试,因此可能包含一些错误

附加

这是一个工作演示,设计为仅运行一次,它使用所有三种方法。

首先是@Entities,基于代码中可用的内容,但已使用 long 来表示日期(而不是使用类型转换器)。

访问

@Entity
class Visits {
   @PrimaryKey
   Long id=null;
   Long date = System.currentTimeMillis() / 1000;
}

重量

@Entity
class Weight {
    @PrimaryKey
    Long id_weight=null;
}

@Dao 带注释的抽象类,具有普通的抽象方法和带主体的方法(解决方案 1)。 insert 方法允许插入一些数据(仅一行)。

@Dao
abstract class AllDao {

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    abstract long insert(Visits visits);

    @Query("UPDATE visits SET date=:date WHERE id=1")
    abstract void resetVisitData(long date);
    @Query("DELETE FROM weight WHERE id_weight=5")
    abstract void deleteFromWeight();
    @Query("")
    void doBoth(long date) {
        resetVisitData(date);
        deleteFromWeight();
    }
}

现在,@Database 带注释的类(使用单例)稍微复杂一些。

这有一个回调来添加触发器,触发器过于复杂,因为它不仅在更新后进行删除(不是删除任何内容),而且还在访问表中添加一个新行显示 TRIGGER 实际上正在被触发(解决方案 2)。

此外,为了需要更好的地方(或不取决于风格/实践),包含一个函数来获取和使用 SupportSQLiteDatabase(解决方案 3)

@Database(entities = {Weight.class,Visits.class}, version = 1,exportSchema = false)
abstract class TheDatabase extends RoomDatabase {
    abstract AllDao getAllDao();

    private static TheDatabase INSTANCE;
    static TheDatabase getINSTANCE(Context context) {
        if (INSTANCE==null) {
            INSTANCE = Room.databaseBuilder(
                            context,
                            TheDatabase.class,
                            "the_database.db"
                    )
                    .allowMainThreadQueries()
                    .addCallback(cb)
                    .build();
        }
        return INSTANCE;
    }

    /* Solution 2 - via SupportSQLiteDatabase */
    void viaSupportSB(long date) {
        SupportSQLiteDatabase db = this.getOpenHelper().getWritableDatabase();
        db.beginTransaction();
        db.execSQL("UPDATE visits SET date=? WHERE id=1",new String[]{String.valueOf(date)});
        db.execSQL("DELETE FROM weight WHERE id_weight=-600");
        db.setTransactionSuccessful();
        db.endTransaction();
    }


    /* USING a TRIGGER (not intended to make sense/do anything useful just demo) */
    private static final String CREATETRIGGERSQL = "CREATE TRIGGER IF NOT EXISTS theTrigger AFTER UPDATE ON visits BEGIN DELETE FROM weight WHERE id_weight=5; INSERT OR IGNORE INTO visits (date) VALUES(strftime('%s','now')); END";
    static Callback cb  = new Callback() {
        @Override
        public void onCreate(@NonNull SupportSQLiteDatabase db) {
            super.onCreate(db);
            db.execSQL(CREATETRIGGERSQL);
        }

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

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

要实际利用上面的一些 Activity 代码MainActivity

public class MainActivity extends AppCompatActivity {

    TheDatabase roomInstance;
    AllDao dao;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        roomInstance = TheDatabase.getINSTANCE(this);
        dao = roomInstance.getAllDao();

        dao.insert(new Visits()); /* Insert a row */

        /* Solution 2 - via Trigger */
        dao.resetVisitData(System.currentTimeMillis() - (24 * 60 * 60 * 7 /* one week ago BUT OOOPS not divided by 1000 */));
        /* Solution 1 - via abstract class aka method with body */
        dao.doBoth(System.currentTimeMillis() / 1000);
        /* Solution 3 - via SupportSQLiteDatabase */
        roomInstance.viaSupportSB(System.currentTimeMillis() + (24 * 60 * 60 * 7 /*week in the future  again OOOPS not divided by 1000*/));
        
        /* Expected result
            1. sinlge row inserted into visits
            2. trigger adds another row into visits (row 2)
            3. doBoth updates so another row added to visits (row 3)
            4. via SupportSQLiteDatabase updates so another row added to visits (row 4)
            
            So 4 rows in visits no rows in weight
         */
    }
}

演示结果 通过 SppInspection

正如预期的重量表是空的:-

enter image description here

正如预期的那样,访问表中有 4 行:-

enter image description here

最后,架构(即 sqlite_master)显示触发器存在(必须添加额外的 3 行):-

enter image description here

关于java - Room Java - 是否可以在界面中运行事务?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74566801/

相关文章:

php - 使用 select 形式更新 Mysql 中的数据

c# - 使用 C# 应用程序连接 Web 服务器中的 mysql 数据库

java - 如何使用正则表达式测试 URL 是否包含某些参数但不包含其他参数

android - 安卓按钮数组

在 Fragment 和 Activity 之间创建通信监听器时出现 Android NullPointerException

java - DalvikVM 操作码的 Smali 语法

php - 在 Wordpress 管理仪表板中执行查询

java - 使用递归技术将二进制数转换为十进制

java - 在 Java 中同步对象

java - Spring MVC 作为 REST 提供者,AngularJS 与 Web 层的 JSP/Velocity/Freemarker