Maison > Questions et réponses > le corps du texte
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粉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).
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(); } }
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) :-