Maison  >  Questions et réponses  >  le corps du texte

Room Java - Est-il possible d'exécuter des transactions dans l'interface ?

J'utilise room framework dans mon projet Android Studio. J'essaie de créer une transaction dans une interface. J'ai lu la documentation d'ici : https://developer.android.com/reference/androidx/room/Transaction

Je sais que nous devrions créer des transactions dans des classes abstraites plutôt que dans des interfaces. Je me demande simplement si cela est possible puisque j'ai déjà plus d'une douzaine d'interfaces dans mon projet et que je ne veux pas les réécrire sous forme de classes abstraites.

P粉852578075P粉852578075283 Il y a quelques jours394

répondre à tous(1)je répondrai

  • P粉464208937

    P粉4642089372024-01-11 13:32:33

    Ce que vous essayez de faire n'est pas possible dans une interface car vous ne pouvez pas utiliser une méthode avec un corps dans une interface.

    Plus précisément, vous essayez d'exécuter plusieurs instructions (une UPDATE, puis une DELETE), mais une seule instruction peut être exécutée en même temps.

    Vos options sont de définir un déclencheur (mis à jour si la ligne de poids peut être déterminée à partir du déclencheur) ou probablement plus probablement d'utiliser une classe abstraite et donc d'utiliser une fonction pour exécuter plusieurs instructions ou d'utiliser des méthodes exploitées (passer/ou récupérer)SupportSQliteDatabase (utiliser des classes abstraites est plus simple).

    • Si vous avez besoin d'un déclencheur, vous devez utiliser un rappel pour créer le déclencheur car Room ne fournit pas d'annotations de déclencheur.

    Ensuite, pour profiter des transactions, vous auriez un @Query factice avant la fonction. Par exemple

    @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();
        }
    }
    • REMARQUE - Le code est essentiellement du code et n'a pas été compilé, exécuté ou testé, il peut donc contenir quelques bugs

    Supplémentaire

    Il s'agit d'une démo fonctionnelle, conçue pour être exécutée une seule fois, elle utilise les trois méthodes.

    D'abord @Entities, basé sur ce qui est disponible dans le code, mais en utilisant déjà long pour représenter la date (au lieu d'utiliser un convertisseur de type).

    Visite

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

    Poids

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

    @Dao Classe abstraite annotée avec des méthodes abstraites normales et des méthodes avec des corps (Solution 1). La méthode insert permet d'insérer des données (une seule ligne).

    @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();
        }
    }

    Maintenant, @Database les classes annotées (utilisant des singletons) sont un peu plus compliquées.

    Ceci a un rappel pour ajouter le déclencheur, le déclencheur est trop complexe car non seulement il supprime après la mise à jour (sans rien supprimer) mais ajoute également une nouvelle ligne dans la table d'accès montrant que le TRIGGER est réellement déclenché (solution 2) .

    De plus, pour de meilleures raisons (ou non selon le style/pratique), incluez une fonction pour obtenir et utiliser la SupportSQLiteDatabase (solution 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);
            }
        };
    }

    Pour utiliser réellement certains des codes d'activité ci-dessusMainActivity

    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
             */
        }
    }

    Résultats de la démonstration via SppInspection

    Comme prévu le tableau des poids est vide :-

    Comme prévu, il y a 4 lignes dans la table d'accès :-

    Enfin, le schéma (c'est-à-dire sqlite_master) montre que le déclencheur existe (il a fallu ajouter 3 lignes supplémentaires) :-

    répondre
    0
  • Annulerrépondre