SQLiteOpenHelper: Mit Upgrade, Beispielen und zentraler Instanz

  • Antworten:7
Gelöschter Account
  • Forum-Beiträge: 694

09.08.2012, 16:38:43 via Website

Dieses Beispiel zeigt die Nutzung des SQLiteOpenHelper mit einer zentralen Instanz (Stichwort: MyApplication), onCreate/onUpgrade und einigen kleinen Beispielen:

1.) Zunächst eine neue Klasse MyApplication anlegen. Diese erweitert Application und existiert genau einmal pro App. Wird beim Start angelegt und wieder zum Schluss abgewickelt:

1public class MyApplication extends Application {
2
3 private static SQLiteDatabase sqliteDatabase;
4 private static MySQLiteOpenHelper sqliteOpenHelper;
5
6 public static SQLiteDatabase getSqliteDatabase() {
7 return sqliteDatabase;
8 }
9
10 public static MySQLiteOpenHelper getSqliteOpenHelper() {
11 return sqliteOpenHelper;
12 }
13
14 @Override
15 public void onCreate() {
16 super.onCreate();
17
18 Context context = getApplicationContext();
19
20 sqliteOpenHelper = new MySQLiteOpenHelper(context);
21 if (sqliteOpenHelper != null) {
22 sqliteDatabase = sqliteOpenHelper.getWritableDatabase();
23 if (sqliteDatabase != null) {
24 }
25 }
26 }
27
28 @Override
29 public void onTerminate() {
30 if (sqliteDatabase != null) {
31 sqliteDatabase.close();
32 }
33
34 if (sqliteOpenHelper != null) {
35 sqliteOpenHelper.close();
36 }
37
38 super.onTerminate();
39 }
40}


2.) Damit diese genutzt werden kann muss sie im Manifest eingetragen werden:

1<application
2 ...
3 android:name="MyApplication" >


3.) Nun der SQLiteOpenHelper. Ich gehe davon aus das Ihr pro Tabelle eine Klasse besitzt (Tablename.java) die mit privaten Membern sowie public getter/setter ausgestattet ist. Was ist anders:

* getWritableDatabase ist überschrieben damit ich an den sqliteDatabase komme. Gleiches gilt für onCreate und onUpgrade. So spare ich mir in jedem Methodenaufruf den zusätzlichen sqliteDatabase Parameter. Die Instanz kennt ihn selbst.

* onUpgrade nutzt ein switch Konstrukt ohne break im case. Damit rutscht die Befehlsfolge durch und bringt jede beliebige Version auf den letzten Stand.

1public class MySQLiteOpenHelper extends SQLiteOpenHelper {
2
3 private static final int DATENBANK_VERSION = 3;
4 private static final String DATENBANK_NAME = "deine.db";
5
6 private Context context;
7 private SQLiteDatabase sqliteDatabase;
8
9 public MySQLiteOpenHelper(Context context) {
10 super(context, DATENBANK_NAME, null, DATENBANK_VERSION);
11
12 this.context = context;
13 }
14
15 @Override
16 public void onCreate(SQLiteDatabase sqliteDatabase) {
17 this.sqliteDatabase = sqliteDatabase;
18
19 sqliteDatabase.execSQL("create table if not exists tablename (...)");
20 }
21
22 @Override
23 public void onUpgrade(SQLiteDatabase sqliteDatabase, int oldVersion, int newVersion) {
24 this.sqliteDatabase = sqliteDatabase;
25
26 switch (newVersion) {
27 case DATENBANK_VERSION:
28 switch (oldVersion) {
29 case 1:
30 upgradeFrom1To2();
31 case 2:
32 upgradeFrom2To3();
33 }
34
35 break;
36 }
37 }
38
39 private void upgradeFrom1To2() {
40 sqliteDatabase.execSQL("alter table tablename add column columnname integer default 0");
41 }
42
43 private void upgradeFrom2To3() {
44 sqliteDatabase.execSQL("alter table tablename add column columnname2 integer default 0");
45 }
46
47 @Override
48 public SQLiteDatabase getWritableDatabase() {
49 try {
50 sqliteDatabase = super.getWritableDatabase();
51 } catch (Exception exception) {
52 }
53
54 return sqliteDatabase;
55 }
56
57 public long deleteTablename(long _id) {
58 return sqliteDatabase.delete("tablename", "_id = ?", new String[] { String.valueOf(_id) } );
59 }
60
61 public Cursor fetchTablename() {
62 return sqliteDatabase.query("tablename",
63 new String[] { "_id",
64 "name" },
65 null,
66 null,
67 null,
68 null,
69 "name");
70 }
71
72 public Tablename getTablename(long _id) {
73 Cursor cursor;
74 Tablename tablename = null;
75
76 if ((cursor = sqliteDatabase.rawQuery("select " +
77 "name" +
78 " from tablename" +
79 " where _id = ?",
80 new String[] { String.valueOf(_id) } )) != null) {
81 if (cursor.moveToFirst()) {
82 tablename = new Tablename();
83
84 tablename.setId(_id);
85 tablename.setName(cursor.getString(0));
86 }
87
88 cursor.close();
89 }
90
91 return tablename;
92 }
93
94 public long insertTablename(Tablename tablename) {
95 ContentValues values = new ContentValues();
96 values.put("name", tablename.getName());
97 return sqliteDatabase.insert("tablename", null, values);
98 }
99
100 public long updateTablename(Tablename tablename) {
101 ContentValues values = new ContentValues();
102 values.put("name", tablename.getName());
103 return sqliteDatabase.update("tablename", values, "_id = ?", new String[] { String.valueOf(tablename.getId()) } );
104 }
105}

4.) Der Zugriff aus jedem beliebigen App-Teil (Activity, Service, Adapter, ...) geht dann wie folgt:

1Cursor cursor = MyApplication.getSqliteOpenHelper().fetchTablename();
2oder
3Tablename tablename = MyApplication.getSqliteOpenHelper().getTablename(1);
4oder
5MyApplication.getSqliteOpenHelper().deleteTablename(2);
6oder
7MyApplication.getSqliteOpenHelper().updateTablename(tablename);
8oder
9MyApplication.getSqliteOpenHelper().insertTablename(tablename);

— geändert am 02.11.2012, 14:20:05

Ansgar M

Antworten
Stefan S.
  • Forum-Beiträge: 560

02.11.2012, 13:20:21 via Website

Das mit der zentralen Instanz muss ich mir merken für weitere Apps.

Ansgar M

Antworten
Dominik V.
  • Forum-Beiträge: 4

14.11.2012, 19:31:02 via Website

Gut gemacht..... Danke!

Antworten
Mac Systems
  • Forum-Beiträge: 1.727

25.11.2012, 01:30:17 via Website

Gemeine Frage :bashful:

Was machst du wenn der User von DB-Version 1 auf 3 Updatet, z.b hat er Update Nr. 2 nicht mitbekommen (Urlaub ..whatever) ?


thx,
Mac

Windmate HD, See you @ IO 14 , Worked on Wundercar, Glass V3, LG G Watch, Moto 360, Android TV

Antworten
Gelöschter Account
  • Forum-Beiträge: 694

26.11.2012, 04:42:37 via App

Schau Dir mal genau das switch(oldVersion) im onUpgrade an. Es gibt kein break, also wird in diesem Fall der Upgrade völlig korrekt von Version 1, über Version 2, nach Version 3 durchgeführt. Das steht auch explizit im Text.

Nachtrag: Vorsicht, so können natürlich einige Statements zusammen kommen. Es bietet sich dann an den getWritableDatabase in einem Thread auszuführen. Das wiederum kann zu einer Race-Condition beim Start der APP führten, also sollte man syncronized nutzen - aber das ist ein anderes Thema.

— geändert am 26.11.2012, 06:30:17

Antworten
Andreas Hoffmann
  • Forum-Beiträge: 36

01.12.2012, 13:47:46 via Website

Hallo,

Ich finde deine Ideen gut, jedoch sind mir ein paar Sachen nicht ganz schlüssig.

Im Text hast du geschrieben für jede Tabelle eine Klasse zu erstellen. Das ist auch für mich ein guter Ansatz, jedoch ist für mich nicht eindeutig, wo das Objekt aus dem Tabelleneintrag erstellt wird oder wie ich ein Objekt in die Datenbank übertrage. Ich bin mir nicht sicher, ob die Methoden fetchTabelname() insertTabelname() und unpdateTabelname() aus der Klasse MySQLiteOpenHelper eine Rolle spielen, aber ich sehe da keinen Zusammenhang.

Außerdem verstehe ich die Methode getWritableDatabase() aus der Klasse MySQLiteOpenHelper nicht. Sollte in dem Attribut sqliteDatabase nicht schon die Datenbank enthalten sein? Wozu das try catch Konstrukt? Was passiert im Fall einer Exeption?

Ich wäre froh, wenn du mir da etwas Klarheit verschaffen könntest =)

LG Andy

Antworten
Gelöschter Account
  • Forum-Beiträge: 694

02.12.2012, 05:55:11 via App

Der Fokus in diesem Post liegt auf der zentralen Instanz und der damit verbundenen Möglichkeit alle Methodenaufrufe ohne SQLiteDatabase Parameter realisieren zu können. Dieser wird in onCreate, onUpgrade und getWritableDatabase gesammelt.

Wie man ein Java Klassenabbild einer Tabelle mit seinen privaten Membern und öffentlichen Methoden erzeugt findet man tonnenweise im Web. Mit new erzeugt man ein Instanz.

Methoden die Cursor liefern heissen bei mir aus historischen Gründen fetch. Ein get liefert bei mir eine Instanz der o.g. Klasse - also eine Row.

Antworten
Andreas Hoffmann
  • Forum-Beiträge: 36

02.12.2012, 16:42:05 via Website

Hallo,

danke für die Antwort. Ich habe übersehen, dass du in der get-Methode ein Objekt erzeugst und das mit dem fetch habe ich auch noch nicht gekannt.
Ich hatte leider nur Tutorials gefunden, in denen ein eigener SQLiteOpenHelper geschrieben wurde und z.B. der Cursor einer Liste übergeben wurde.
Weil ich noch nicht viel mit Datenbanken gearbeitet habe bin ich auch nicht auf die Idee gekommen zu jeder Tabelle eine Klasse zu schreiben und habe auch nicht nach so etwas gesucht.

Ich habe jetzt die ORMLite Library gefunden, die ich mir anschauen werde.

Antworten