Mobile App Development ORM

Werbung
Mobile App
Development
- Persistenz -
Inhalt
• Shared Preferences
• Dateien
• Datenbanken
• ORM
Mobile App Development
2
Shared Preferences
Shared Preferences
• Framework zum Speichern von Key-ValuePaaren
• Datentypen: boolean, float, int, long, String
• Daten werden in einer Datei gespeichert
• Nach Deinstallation der App werden
Preferences-Dateien mit gelöscht
Mobile App Development
4
Shared Preferences
// DemoActivity für SharedPreferences
public class SharedPrefsDemoActivity extends Activity {"
!
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
}
public static final String PREFS_FILE = "prefsFile";"
public static final String MY_KEY = "myKey";"
"
private int mMyMember;"
"
protected void onCreate(Bundle savedInstanceState) {"
"
super.onCreate(savedInstanceState);"
"
"
"
// Aufrufen der SharedPreferences"
"
SharedPreferences prefs = getSharedPreferences(PREFS_FILE, MODE_PRIVATE);"
"
// Abruf des Wertes für MY_KEY (default: 0)"
"
mMyMember = prefs.getInt(MY_KEY, 0);"
}"
"
@Override"
protected void onStop() {"
super.onStop();"
// Aufruf der SharedPreferences"
SharedPreferences prefs = getSharedPreferences(PREFS_FILE, MODE_PRIVATE);"
// Editor aufrufen, um Änderungen zu machen"
Editor editPrefs = prefs.edit();"
// Key-Value-Paar anlegen/aktualisieren"
editPrefs.putInt(MY_KEY, mMyMember);"
// Änderungen schreiben"
editPrefs.commit();"
}"
Mobile App Development
5
Dateien Dateien
• Zwei mögliche Plätze zum Speichern von
Dateien:
• Internal Storage
geräteinterner Speicher
• External Storage
externer Speicher, wie SD-Karten
Mobile App Development
7
Interner Speicher
• Standardmäßig sind Dateien, die auf dem
internen Speicher abgelegt werden privat,
d.h. andere Anwendungen können nicht
darauf zugreifen
• Wird die Anwendung deinstalliert, werden
auch die internen Dateien gelöscht
Mobile App Development
8
Interner Speicher
• Lesen von Dateien
•
Context.openFileInput(...) liefert
einen FileInputStream
• Die weitere Verarbeitung erfolgt wie bei
Standard Datei- und Stream-Verarbeitung
in Java
Mobile App Development
9
Interner Speicher
• Datei lesen
private void readFile(String fileName) throws Exception {"
" /* openFileInput() öffnet die Datei mit dem angegebenen Namen"
"
* und liefert eine FIS zurück */"
" FileInputStream fis = openFileInput(fileName);"
" BufferedReader reader = new BufferedReader("
" " " new InputStreamReader(fis));"
" String line = "";"
" " "
" /* Auslesen der Datei */"
" while ( (line = reader.readLine()) != null) {"
" " /* zeilenweise Verarbeitung */"
" " Log.d(LOG, line);"
" }"
" " "
" /* reader und stream schließen */"
}
Mobile App Development
10
Interner Speicher
• Dateien schreiben
•
Context.openFileOutput(...) liefert
einen FileOutputStream
• existiert die Datei noch nicht, wird sie
automatische angelegt
• das Schreiben der Datei funktioniert wie
auch bei Standard Java
Mobile App Development
11
Interner Speicher
• Dateien schreiben
private void writeFile(String fileName, String content) "
" " " throws Exception {"
" " "
" /* openFileOutput öffnet einen FOS, der mit der angegebenen "
"
Datei verbunden ist und legt die Datei an, wenn sie noch "
"
nicht existiert */"
" FileOutputStream fos = openFileOutput(fileName, MODE_PRIVATE);"
" OutputStreamWriter osw = new OutputStreamWriter(fos);"
" " "
" /* Inhalt in die Datei schreiben */"
" osw.write(content);"
" "
" /* writer und stream schließen */"
" osw.close();"
}
Mobile App Development
12
Interner Speicher
• Android unterschiedet verschiedene Modi
zum Anlegen einer Datei
•MODE_PRIVATE"
• Datei ist privat, kann also nur von der
erstellenden App gelesen werden
• wenn Datei bereits vorhanden, wird sie
überschrieben
Mobile App Development
13
Interner Speicher
•MODE_APPEND"
• Wenn Datei bereits vorhanden, wird
neuer Inhalt angehängt
•MODE_WORLD_READABLE"
• Alle anderen Apps haben lesenden
Zugriff
Mobile App Development
14
Interner Speicher
•MODE_WORLD_WRITEABLE"
• Alle anderen Apps haben schreibenden
Zugriff auf die Datei
Mobile App Development
15
Interner Speicher
• Es gibt einige weitere nützliche Methoden,
um mit Dateien und Verzeichnisse zu
arbeiten"
• Context.getFilesDir()
Absoluter Pfad zum dem Verzeichnis, in
dem interne Dateien abgelegt werden
• Context.getDir(name,
mode)
Erzeugt oder öffnet ein Verzeichnis
Mobile App Development
16
Interner Speicher
• Weitere Methoden"
• Context.deleteFile(name)
Entfernt die angegebene Datei
• Context.fileList()
Listet alle mit der App assoziierten
(internen) Dateien auf
Mobile App Development
17
Interner Speicher
• Weitere Methoden"
• Context.getCacheDir()
Gibt den Pfad zum Verzeichnis für CacheDateien zurück
• File.createTempFile(prefix,
suffix)
Erzeugt eine temporäre Datei im tempVerzeichnis
Mobile App Development
18
Externer Speicher
• Im Gegensatz zum internen Speicher gibt
es bei externem Besonderheiten zu
beachten:
• Daten können von jedermann gelesen
werden
• Speicher muss nicht immer verfügbar sein (SD-Karte kann entfernt werden)
Mobile App Development
19
Externer Speicher
• Verfügbarkeit des ext. Speichers prüfen
"
private boolean mExtStorageReadable = false;"
private boolean mExtStorageWritable = false;
"
..."
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
// Überprüft, ob der externe Speicher gelesen und geschrieben werden kann
private void checkAvailability() {"
"
"
"
// Status des externen Speichers abrufen"
"
String state = Environment.getExternalStorageState();"
"
"
"
if (Environment.MEDIA_MOUNTED.equals(state)) {"
"
"
// Speicher kann gelesen und geschrieben werden"
"
"
mExtStorageReadable = true;"
"
"
mExtStorageWritable = true;"
"
} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {"
"
"
// Speicher kann nur gelesen werden"
"
"
mExtStorageReadable = true;"
"
"
mExtStorageWritable = false;"
"
} else {"
"
"
// Speicher ist z.B. entfernt oder hat kein unterstütztes Dateisystem"
"
"
mExtStorageReadable = false;"
"
"
mExtStorageWritable = false;"
"
}"
}
Mobile App Development
20
Externer Speicher
• Unterscheidung zwischen App-eigenen
Daten und geteilten Daten
• App-eigene Daten sind nur für die eigene
App relevant und werden bei
Deinstallation mit gelöscht
• Geteilte Daten können auch von anderen
Apps interpretiert werden (Musik, Bilder, etc.)
Mobile App Development
21
Externer Speicher
• Zugriff auf App-eigene Daten
"
"
"
// Methode, um auf externen Speicher zuzugreifen
private void accessFiles() {"
"
"
"
// Verfügbarkeit des ext. Speichers prüfen"
"
checkAvailability();"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
}
!
..."
"
// bis API Level 7 (Eclair)"
File extStore = Environment.getExternalStorageDirectory();"
File extAppDir7 = new File(extStore, "
"
"Android" + File.separator + "
"
"data" + File.separator + "
getPackageName() + File.separator + "
"files");"
extAppDir7.mkdirs();"
"
// ab API Level 8 (Froyo)"
File extAppDir8 = getExternalFilesDir(null);"
// ..."
// Standard Java Dateiarbeit, wie gehabt
// ..."
Mobile App Development
22
Externer Speicher
• Geteilte Daten sollten in öffentlichen
Verzeichnissen abgelegt werden
• Bsp.: /Music, /Podcasts, /Alarms, /Download, etc.
Mobile App Development
23
Externer Speicher
• auf geteilte Daten zugreifen
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
private void accessPublicFiles() {"
"
"
"
// Verfügbarkeit des ext. Speichers prüfen"
"
checkAvailability();
"
"
..."
"
"
"
// öffentliches Verzeichnis für Musik"
"
File musicDir = null;"
"
"
"
// bis API Level 7"
"
File extDir = Environment.getExternalStorageDirectory();"
"
musicDir = new File(extDir, "Music");"
"
"
"
// ab API Level 8"
"
musicDir = Environment"
"
"
.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC);"
"
"
"
// Danach, wie gewohnt"
"
File musicFile = new File(musicDir, "myMusic.mp3");"
"
musicFile.createNewFile();"
}
Mobile App Development
24
Externer Speicher
• Temporäre Dateien auf den externen
Speicher schreiben
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
private void cacheFiles() {"
"
"
"
// Verfügbarkeit des ext. Speichers prüfen"
"
checkAvailability();"
"
// ..."
"
"
"
"
"
File cacheDir = null;"
"
"
"
// bis API Level 7"
"
File extDir = Environment.getExternalStorageDirectory();"
"
cacheDir = new File(extDir, "/Android/data/" + getPackageName() + "/cache/");"
"
"
"
// ab API Level 8"
"
cacheDir = getExternalCacheDir();"
"
"
"
// Erstellen der temporären Datei"
"
File cacheFile = File.createTempFile("myPrefix", "mySuffix", cacheDir);"
"
"
}
Mobile App Development
25
Datenbanken
SQLite
• SQLite ist die Standard Datenbank-Engine,
die in Android verwendet wird
• optimiert für Embedded Systems
• zeichnet sich aus durch • Abgeschlossenheit, Serverlosigkeit
• Minimal- bis 0-Konfiguration
• unterstützt Standard SQL
Mobile App Development
27
SQLite
• Daten werden in SQLite durch sogenannte
Storage Classes repräsentiert
• Jedem Wert wird ein Datentyp zugeordnet
Mobile App Development
28
Storage Classes
Datentyp
Beschreibung
NULL
Null-Wert
INTEGER
vorzeichenbehaftete Ganzzahl (1-8 Byte)
REAL
Dezimalzahl (8 Byte)
TEXT
String
BLOB
Binärdaten
Mobile App Development
29
Storage Classes
Datentyp Konvertierung
Boolean INTEGER
Date/
Time
Darstellung
0 = false, 1 = true
TEXT
ISO8601: YY-MM-DD HH:MM.SS.SSS
REAL
Zeit in Tagen seit 01.01.4713 v.Chr. 12:00
INTEGER
UNIX-Zeit, Sekunden seit
1970-01-01 00:00:00 UTC
Mobile App Development
30
Datentypen
• Alle Standard SQL-Datentypen können
verwendet werden
• Integer, Numeric, Decimal, Float, Real,
Double
• Varchar, Char
• Date, Time, Timestamp
• Boolean
• Blob, Clob
Mobile App Development
31
Operatoren
||"
*
+
<<
<
=
IN
AND
OR
/
-"
>>
<=
==
LIKE
"
%"
&
>
!=
|"
>="
<>
GLOB"
IS
IS NOT
• siehe: http://www.sqlite.org/lang_expr.html
Mobile App Development
32
DatenbankfunkConen
• Kernfunktionen
• abs, hex, min, max, random, round, ...
• substr, trim, replace, upper, lower, ...
• siehe: http://www.sqlite.org/
lang_corefunc.html
Mobile App Development
33
DatenbankfunkConen
• Aggregatfunktionen
• min, max, avg, count, sum
• weitere: http://www.sqlite.org/
lang_aggfunc.html
• Datum & Zeit
• date, time, datetime, strftime
• http://www.sqlite.org/lang_datefunc.html
Mobile App Development
34
SQLiteOpenHelper
• abstrakte Oberklasse für DB-Zugriffe
• empfohlene Schnittstelle zum
• Erstellen, Öffnen und
• Aktualisieren
von Datenbanken
Mobile App Development
35
SQLiteOpenHelper
•
Wenn von SQLiteOpenHelper abgeleitet wird,
müssen folgende Methode überschrieben
werden
•
Konstruktor: Hier wird dem super
Konstruktor der Context, Datenbankname
und -version mitgegeben
•
onCreate(): wird aufgerufen, wenn die
Datenbank erstellt wird. Hier werden SQL
Statements ausgeführt, um die Tabellen zu
erstellen.
Mobile App Development
36
SQLiteOpenHelper
•
onUpgrade(): wird aufgerufen, wenn eine
•
onDowngrade(): ist das Pendant zu der
Methdode onUpgrade() (ab API 11)
Datenbankversion gefunden wurde, die
älter ist, als die aktuelle. Hier werden alte
Datenbanktabellen angepasst. Mobile App Development
37
SQLiteOpenHelper
• Beispiel: Erstellung einer Datenbank, mit der TODOs gespeichert werden können
• Datenmodell:
todo
date
Integer
title
Text
description
Text
Mobile App Development
38
DatabaseHelper
// Hilfsklasse zum Erstellen, Aktualisieren und Öffnen der Datenbank
public class DatabaseHelper extends SQLiteOpenHelper {"
!
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
public static final String DATABASE_NAME = "todo.db"; " // Datenbankname"
private static final int DATABASE_VERSION = 1;""
"
// Datenbankversion"
"
public static final String TABLE_NAME = "todo";"
"
public static final String DATE_FIELD_NAME = "date";"
public static final String DATE_FIELD_TYPE = "INTEGER";"
public static final String TITLE_FIELD_NAME = "title";"
public static final String TITLE_FIELD_TYPE = "TEXT";"
public static final String DESCR_FIELD_NAME = "description";"
public static final String DESCR_FIELD_TYPE = "TEXT";"
"
// SQL statement zum Erstellen der Tabelle"
private static final String TABLE_CREATE = "
"
"
"CREATE TABLE " + TABLE_NAME + "(" "
"
"
"
+ DATE_FIELD_NAME + " " + DATE_FIELD_TYPE + ", ""
"
"
"
+ TITLE_FIELD_NAME + " " + TITLE_FIELD_TYPE + ", ""
"
"
"
+ DESCR_FIELD_NAME + " " + DESCR_FIELD_TYPE + ")";"
"
// Konstruktor"
public DatabaseHelper(Context context) {"
"
super(context, " "
"
// der Context
"
"
DATABASE_NAME, " "
// Datenbankname
"
"
null, " "
"
"
// CursorFactory
"
"
DATABASE_VERSION);" // Datenbankversion"
}"
"
// ..."
Mobile App Development
39
DatabaseHelper
// Hilfsklasse zum Erstellen, Aktualisieren und Öffnen der Datenbank
public class DatabaseHelper extends SQLiteOpenHelper {
"
"
"
"
"
"
"
"
// Wird aufgerufen, wenn die DB das erste mal erstellt wird"
public void onCreate(SQLiteDatabase db) {"
" try {"
" " db.execSQL(TABLE_CREATE);"
" } catch (SQLException ex) {"
" " Log.e("DatabaseHelper", "error creating tables", ex);"
" }"
}"
"
"
"
"
"
"
"
"
"
"
}
public void onUpgrade(SQLiteDatabase db, int oldVer, int newVer) {"
" // alte Tabellen auslesen und löschen, neue Tabellen anlegen"
" // alten Inhalt in neue Tabellen einfügen"
}"
!
// ab API-Level 11"
public void onDowngrade(SQLiteDatabase db, int oldVer, int newVer) {"
" // alte Tabellen auslesen und löschen, neue Tabellen anlegen"
" // alten Inhalt in neue Tabellen einfügen"
}"
Mobile App Development
40
Daten abfragen
• Mit der Methode getReadableDatabase()
wird aus dem SQLiteOpenHelper eine
lesbare Datenbankinstanz erzeugt
• Mit der query()-Methode der DB Instanz
können Daten gelesen werden
• Die query() Methode besitzt einige
Parameter, die einer SQL-Abfrage
entsprechen
Mobile App Development
41
Daten abfragen
•
query()-Parameter
• table: der Tabellenname
• columns: String Array, das die
abzufragenden Spaltennamen enthält
oder null für alle Spalten
• selection: entspricht der WHERE-Klausel
eines SQL-Statements ohne das WHERE
Schlüsselwort
Mobile App Development
42
Daten abfragen
•
query()-Parameter
• selectionArgs: dient der Parametrisierung
der selection
• groupBy: dient der Gruppierung von
Zeilen in Verbindung mit
Aggregatfunktionen
• having: Filter für Gruppierungen
Mobile App Development
43
Daten abfragen
•
query()-Parameter
• orderBy: dient der Sortierung des
Ergebnisses nach einer Spalte
• limit: begrenzt die Anzahl der Ergebnisse
Mobile App Development
44
Daten abfragen
// Methode zum abfragen von Daten aus der DB"
private void doSelect() {"
" " "
" // Datenbank zum Lesen öffnen "
" SQLiteDatabase db = new DatabaseHelper(this).getReadableDatabase();"
" " "
" // query(String table, String[] columns, String selection, String[] "
" //
selectionArgs, String groupBy, String having, String orderBy,
" //
String limit)"
" Cursor result = db.query("
" " " DatabaseHelper.TABLE_NAME, null, null, null, null, null, "
" " " DatabaseHelper.DATE_FIELD_NAME, null);"
" " "
" // Ergebnis verarbeiten"
" " "
" // Datenbank schließen"
" db.close();"
}
Mobile App Development
45
Daten abfragen
• Die query() Methode der DatenbankInstanz liefert einen Cursor zurück
• Cursor repräsentieren das Ergebnis der
Datenbankabfrage
• Ein Cursor besitzt einen Zeiger, mit dem
man datensatzweise über das ResultSet
navigieren kann
Mobile App Development
46
Daten abfragen
• Nützliche Cursor Methoden
•
getCount(): gibt die Anzahl der
•
moveTo[First, Next, Last]():
•
getPosition(): gibt die aktuelle
Datensätze im Cursor an
bewegt den Datensatzzeiger im Cursor
Zeigerposition im Cursor zurück
Mobile App Development
47
Daten abfragen
• Nützliche Cursor Methoden
•
getColumnIndex(): gibt den Index zum
•
get[Long,String,Int,Short,...]():
übergebenen Spaltennamen zurück
gibt den Wert im Datensatz für den
angegebenen Spaltenindex zurück
Mobile App Development
48
Daten abfragen
// Methode zum abfragen von Daten aus der DB"
private void doSelect() {"
" // Datenbank zum Lesen öffnen und query ausführen"
" Cursor result = db.query(...);"
" " "
" if (result.moveToFirst()) { // wenn Cursor nicht leer"
" " // Spaltenindex vom Datums- und Titel-Feld abrufen"
" " int dateIdx = result.getColumnIndex(DatabaseHelper.DATE_FIELD_NAME);"
" " int titleIdx = result.getColumnIndex(DatabaseHelper.TITLE_FIELD_NAME);"
" " do { "
" " " // Datum und Titel auslesen"
" " " long date = result.getLong(dateIdx);"
" " " String title = result.getString(titleIdx);"
" " " // ... und verarbeiten"
" " " Log.d(LOG_TAG, new Date(date) + " " + title);"
" " } while (result.moveToNext()); // solange Datensätze vorhanden"
" }"
!
"
"
"
"
}
// Cursor direkt wieder schließen, um Ressourcen freizugeben "
result.close();"
// Datenbank schließen"
db.close();"
Mobile App Development
49
Daten abfragen
• Zur Abfrage von Daten können auch
Standard SQL Statements verwendet
werden
• Optional können diese parametrisiert
werden
Mobile App Development
50
Daten abfragen
public void doRawSelect() {"
" // Datenbank zum Lesen öffnen "
" SQLiteDatabase db = new DatabaseHelper(this).getReadableDatabase();"
" // SQL Statement erstellen"
" String sql = "SELECT * FROM " + DatabaseHelper.TABLE_NAME + "
" " " " ORDER BY " + DatabaseHelper.DATE_FIELD_NAME;"
" // query ausführen"
" Cursor result = db.rawQuery(sql, null);"
" " "
" if (result.moveToFirst()) { // wenn Cursor nicht leer"
" " // Spaltenindex vom Datums- und Titel-Feld abrufen"
" " int dateIdx = result.getColumnIndex(DatabaseHelper.DATE_FIELD_NAME);"
" " int titleIdx = result.getColumnIndex(DatabaseHelper.TITLE_FIELD_NAME);"
" " do { // für jeden Datensatz"
" " " // Datum und Titel auslesen"
" " " long date = result.getLong(dateIdx);"
" " " String title = result.getString(titleIdx);"
" " " // ... und verarbeiten"
" " " Log.d(LOG_TAG, new Date(date) + " " + title);"
" " } while (result.moveToNext()); // solange Datensätze vorhanden"
" }"
result.close();" // Cursor schließen"
" db.close(); // Datenbank schließen"
}
Mobile App Development
51
Daten einfügen
• Um Datensätze in die Datenbank
einzufügen werden ContentValues Objekte
verwendet
• Ein ContentValues Objekt repräsentiert
einen Datensatz in einer Tabelle
• Ein CotentValues Objekt besitzt put()
Methoden, die einen Schlüssel und einen
Wert erhalten
Mobile App Development
52
Daten einfügen
• Schlüssel ist der Spaltenname aus der
Datenbank und dazu der zu setzende Wert
• ContentValues Objekte werden unter
Angabe der Tabelle per insert() Methode
in die Datenbank eingefügt
Mobile App Development
53
Daten einfügen
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
// Methode zum Einfügen von Daten in die DB"
private void doInsert() {"
" "
" // Datenbank zum Schreiben öffnen"
" SQLiteDatabase db = mDbHelper.getWritableDatabase();"
" "
" // Datensatz erstellen"
" ContentValues vals = new ContentValues();"
" vals.put(DatabaseHelper.DATE_FIELD_NAME, System.currentTimeMillis());"
" vals.put(DatabaseHelper.TITLE_FIELD_NAME, "meinTitel");"
" vals.put(DatabaseHelper.DESCR_FIELD_NAME, "meineBeschreibung");"
" "
" // Datensatz in die Datenbank einfügen"
" db.insert(DatabaseHelper.TABLE_NAME, null, vals);"
" "
" // Datenbank schließen"
" db.close();"
}
Mobile App Development
54
Daten aktualisieren
• Die Aktualisierung von Datensätzen
funktioniert ähnlich wie das Einfügen
• Es wird ein ContentValues Objekt mit den
neuen Daten erzeugt und per update()
Methode abgeschickt
Mobile App Development
55
Daten aktualisieren
"
"
"
"
"
"
"
"
"
"
"
"
"
!
"
"
"
"
"
"
// Mehtode zum Aktualisieren von Datensätzen"
private void doUpdate() {"
" "
" // Datenbank zum Schreiben öffnen"
" SQLiteDatabase db = mDbHelper.getWritableDatabase();"
" "
" // ContentValues Objekt mit den neuen Daten erzeugen"
" ContentValues values = new ContentValues();"
" values.put(DatabaseHelper.DATE_FIELD_NAME, " " " new java.util.Date().getTime());"
" "
" // Datensätze unter Angabe des Tabellennamens und einer "
" // WHERE Bedingung aktualisieren"
!
int rows = db.update(DatabaseHelper.TABLE_NAME, values, "
" " " DatabaseHelper.DATE_FIELD_NAME + " LIKE 'b%'", null);"
" Log.d(LOG_TAG, "affected rows: " + rows);"
"
" // Datenbank schließen"
" db.close();"
}
Mobile App Development
56
Daten löschen
• Datensätze werden gelöscht, indem eine
WHERE Bedingung angegeben wird, die auf
die zu löschende Datensätze zutrifft
Mobile App Development
57
Daten löschen
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
// Methode zum Löschen von Datensätzen"
private void doDelete() {"
" "
" // Datenbank zum Schreiben öffnen"
" SQLiteDatabase db = mDbHelper.getWritableDatabase();"
" "
" // Datensätze unter Angabe einer WHERE Bedingung löschen"
" int rows = db.delete(DatabaseHelper.TABLE_NAME, "
" " " DatabaseHelper.DATE_FIELD_NAME + " < date('now')", null);"
" "
" // Anzahl gelöschter Datensätze löschen"
" Log.d(LOG_TAG, rows + " rows deleted");"
" "
" // Datenbank schließen"
" db.close();"
}
Mobile App Development
58
Raw Queries
• Einfügen, Aktualisieren und Löschen
private void doRawInsertUpdateDelete() {"
" // Datenbank zum Schreiben öffnen"
" SQLiteDatabase db = new DatabaseHelper(this).getWritableDatabase();"
" "
" // insert statement erstellen & abschicken"
" String insertSQL = "INSERT INTO " + DatabaseHelper.TABLE_NAME + " VALUES(""
" " + System.currentTimeMillis() + ", 'mein Titel', 'meineBeschreibung')";"
" db.execSQL(insertSQL);"
" " "
" // update statement erstellen & abschicken"
" String updateSQL = "UPDATE " + DatabaseHelper.TABLE_NAME + " SET " "
" " + DatabaseHelper.TITLE_FIELD_NAME + "=neuerTitel WHERE " "
" " + DatabaseHelper.DATE_FIELD_NAME + "<" + System.currentTimeMillis();"
" db.execSQL(updateSQL);"
" " "
" // delete statement erstellen & abschicken"
" String deleteSQL = "DELETE FROM " + DatabaseHelper.TABLE_NAME + " WHERE ""
" " + DatabaseHelper.TITLE_FIELD_NAME + " LIKE 'n%'";"
" db.execSQL(deleteSQL);"
" " "
" db.close(); // Datenbank schließen"
}
Mobile App Development
59
TransakConen
• Standardmäßig wird jedes SQL-Statement
in einer eigenen Transaktion ausgeführt
• Gründe, um einen eigenen
Transaktionsrahmen zu setzen:
• „succeed or fail as a whole“
• Performance
Mobile App Development
60
TransakConen
• Transaktionen werden mit
beginTransaction() gestartet
• ... und mit endTransaction() beendet
• Um die Transaktion als erfolgreich zu
markieren, muss vor dem Aufruf von
endTransaction() die Methode
setTransactionSuccessful()
aufgerufen werden
Mobile App Development
61
TransakConen
• Transaktionen ausführen
private void doTransaction() {"
" // Datenbank zum Schreiben öffnen"
" SQLiteDatabase db = new DatabaseHelper(this).getWritableDatabase();"
" "
" try {"
" " // Transaktion starten"
" " db.beginTransaction();"
" " "
" " /* auf der Datenbank arbeiten */"
" " "
" " // Transaktion als erfolgreich markieren"
" " db.setTransactionSuccessful();"
" } catch (Exception ex) {"
" " // Fehler behandeln"
" } finally {"
" " // Transaktion beenden"
" " db.endTransaction();"
" }"
}
Mobile App Development
62
CursorAdapter
CursorAdapter
• Die Ergebnisse von Datenbankabfragen
können über einen Adapter direkt in
AdapterViews (ListView, SpinnerView)
dargestellt werden
• Hierfür werden CursorAdapter verwendet,
die den AdapterViews ebenso wie
ArrayAdapter ihre Daten zur Verfügung
stellen
Mobile App Development
64
CursorAdapter
• Exkurs: Eigene List Layouts
• Einträge in einer Liste, also die Darstel-
lung der einzelnen Einträge, können mit
einem eigenen Layout versehen werden
• Layout-Dateien werden genauso erstellt,
wie bei Activity-Layouts
Mobile App Development
65
CursorAdapter
• Exkurs: Eigene List Layouts
<?xml version="1.0" encoding="utf-8"?>"
<LinearLayout xmlns:android="...""
android:layout_width="match_parent""
android:layout_height="match_parent""
android:orientation="horizontal" >"
!
!
!
<TextView"
android:id="@+id/title""
android:layout_width="match_parent""
android:layout_height="wrap_content""
android:layout_weight="1"/>"
<TextView"
android:id="@+id/date""
android:layout_width="wrap_content""
android:layout_height="wrap_content"/>"
</LinearLayout>
Mobile App Development
66
CursorAdapter
• Exkurs: Eigene List Layouts
• Layout verwenden
private void useCustomListLayout() {"
" // ListView Referenz abrufen"
" ListView listView = "
" " (ListView) findViewById(R.id.listView1);"
" // Adapter bekommt die Layout Referenz "
" // zum angepassten Layout"
" ArrayAdapter<String> listAdapter = "
" " new ArrayAdapter<String>("
" " " this, R.layout.list_item);"
" " "
" // ... wie gehabt"
}
Mobile App Development
67
CursorAdapter
• CursorAdapter stellen eine Brücke
zwischen AdapterViews und Cursor
Objekten her
• Im CursorAdapter wird angegeben, welche
Spalte auf welches Element im ListItem
Layout abgebildet wird
• Das Auslesen des Cursors und Setzen der
ListItems wird vom Adapter übernommen
Mobile App Development
68
CursorAdapter
private void setListCursorAdapter() {"
!
"
"
"
"
"
"
"
"
"
// Datenbank öffnen und Abfrage ausführen"
SQLiteDatabase db = new DbHelper(this).getReadableDatabase();"
String sql = "select _id, title, date from todo";"
Cursor cursor = db.rawQuery(sql, null);"
" "
// gibt an, welche Spalten in der ListView dargestellt werden sollen"
String[] from = new String[] {"title", "date"};"
// gibt an, welche Spalte in welcher View dargestellt werden soll"
int[] to = new int[]{R.id.title, R.id.date};"
"
"
"
"
"
"
"
}
// CursorAdapter erstellen"
SimpleCursorAdapter sca = new SimpleCursorAdapter(this,"
" R.layout.list_item, cursor, from, to, 0);"
" "
// Adapter sezten"
ListView lv = (ListView) findViewById(R.id.listView1);"
lv.setAdapter(sca);""
!
Mobile App Development
69
CursorAdapter
• Hinweis: Um einen SimpleCursorAdapter zu nutzen,
muss die verwendete Tabelle eine Spalte
„_id“ besitzen!
Mobile App Development
70
Objekt-­‐relaConales Mapping
ORM
• Problemstellung
• Objekt-Modell und Relationales Modell
unterscheiden sich
• Objekte müssen immer wieder in
Tabellen-Datensätze konvertiert werden
und umgekehrt
Mobile App Development
72
ORM
• Problemstellung
• Hohe Fehleranfälligkeit durch
Verwendung von Plain SQL (Typo‘s)
• große Menge an SQL Statements muss
programmiert werden
Mobile App Development
73
ORM
• Beispiel
• zwei Tabellen mit einfacher 1:nVerknüpfung
todo
date
title
description
fk_todo_lis
t
Integer
Text
Text
Integer
Mobile App Development
1
n
74
todo_group
id
name
Integer
Text
ORM
• Beispiel
public class TodoDbHelper extends SQLiteOpenHelper {"
"
"
"
public static final String LOG_TAG = TodoDbHelper.class.getName();"
!
"
"
!
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
}
public static final String DATABASE_NAME = "groupedtodo.db";"
private static final int DATABASE_VERSION = 1;"
// table creation attributes"
public static final String TODO_TABLE_NAME = "todo";"
"
public static final String TODO_ID_FIELD_NAME = "_id";"
public static final String TODO_ID_FIELD_TYPE = "INTEGER PRIMARY KEY AUTOINCREMENT";"
public static final String TODO_DATE_FIELD_NAME = "date";"
public static final String TODO_DATE_FIELD_TYPE = "INTEGER";"
public static final String TODO_TITLE_FIELD_NAME = "title";"
public static final String TODO_TITLE_FIELD_TYPE = "TEXT";"
public static final String TODO_PARENT_FIELD_NAME = "parent";"
public static final String TODO_PARENT_FIELD_TYPE = "INTEGER";"
public static final String TODO_PARENT_FOREIGN_KEY = "
"CONSTRAINT fk_parent FOREIGN KEY (parent) REFERENCES todoGroup(_id)";"
"
public static final String TODO_GROUP_TABLE_NAME = "todo_group";"
public static final String TODO_GROUP_ID_FIELD_NAME = "_id";"
public static final String TODO_GROUP_ID_FIELD_TYPE = "INTEGER PRIMARY KEY AUTOINCREMENT";"
public static final String TODO_GROUP_NAME_FIELD_NAME = "name";"
public static final String TODO_GROUP_NAME_FIELD_TYPE = "TEXT";"
"
// ..."
Mobile App Development
75
ORM
• Beispiel
public class TodoDbHelper extends SQLiteOpenHelper {"
"
"
"
// ..."
"
"
"
// SQL statement for table creation"
"
private static final String TODO_TABLE_CREATE = "
"
"
"
"CREATE TABLE " + TODO_TABLE_NAME + "(" "
"
"
"
"
+ TODO_ID_FIELD_NAME + " " + TODO_ID_FIELD_TYPE + ", ""
"
"
"
"
+ TODO_DATE_FIELD_NAME + " " + TODO_DATE_FIELD_TYPE + ", ""
"
"
"
"
+ TODO_TITLE_FIELD_NAME + " " + TODO_TITLE_FIELD_TYPE + ", " "
"
"
"
"
+ TODO_PARENT_FIELD_NAME + " " + TODO_PARENT_FIELD_TYPE + ", ""
"
"
"
"
+ TODO_PARENT_FOREIGN_KEY + ")";"
!
"
"
"
"
}"
// SQL statement for TodoList table creation"
private static final String TODO_GROUP_TABLE_CREATE = "
"
"
String.format("CREATE TABLE %s (%s %s, %s %s)", TODO_GROUP_TABLE_NAME, "
"
"
"
"
TODO_GROUP_ID_FIELD_NAME, TODO_GROUP_ID_FIELD_TYPE,"
"
"
"
"
TODO_GROUP_NAME_FIELD_NAME, TODO_GROUP_NAME_FIELD_TYPE);"
Mobile App Development
76
ORM
• Lösung
• Objekt-relationales Mapping (ORM)
ermöglicht die Abbildung von Objekten
auf Tabellen
• Abstraktion der Datenbankzugriffe durch
Methoden zum Erstellen, Löschen, ...
• Foreign-Key Beziehungen zwischen
Datensätzen werden in Objektreferenzen
umgewandelt
Mobile App Development
77
ORM
• Lösung
• Es muss kein SQL mehr geschrieben
werden (kann aber)
• Es gibt viele verschiede ORM
Frameworks (OrmLite, greenDAO, ...)
• Hier in der Vorlesung wird OrmLite
exemplarisch für Android vorgestellt
Mobile App Development
78
ORM
• OrmLite (http://ormlite.com)
• Sehr einfaches OrmFramework
• schnelle Einarbeitungszeit
• Es muss nur eine jar-Datei eingebunden
werden
• Entities (Objekte) werden mit
Annotations versehen
Mobile App Development
79
ORM
• Todo Beispiel
@DatabaseTable(tableName="todo")"
public class Todo {"
@DatabaseTable(tableName="todolist")"
public class TodoList {"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
!
!
!
@DatabaseField(generatedId=true)"
private int id;"
@DatabaseField"
private long date;"
@DatabaseField "
private String title; "
@DatabaseField"
private String description;"
@DatabaseField(foreign=true)"
private TodoList parent;"
"
public Todo() {"
" "
}"
!
" // setter & getter"
}"
!
!
!
// setter & getter"
}
Mobile App Development
@DatabaseField(generatedId=true)"
private int id;"
@DatabaseField"
private String name;"
@ForeignCollectionField"
private Collection<Todo> todos;"
"
public TodoList() {"
" "
}"
80
ORM
• OrmDbHelper
public class OrmDbHelper extends OrmLiteSqliteOpenHelper {"
!
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
"
}
public static final String LOG = OrmDbHelper.class.getName();"
public static final String DB_NAME = "todo.db";"
public static final int DB_VERSION = 1;"
"
public OrmDbHelper(Context context) {"
" super(context, DB_NAME, null, DB_VERSION);"
}"
"
@Override"
public void onCreate(SQLiteDatabase db, ConnectionSource source) {"
// Registrierung der Klassen beim ORM Framework"
" try {"
" " TableUtils.createTable(source, Todo.class);"
TableUtils.createTable(source, TodoList.class);"
" } catch (SQLException ex) {"
" " Log.e(LOG, "error creating tables", ex);"
" }"
}"
Mobile App Development
81
ORM
• Data Access Objects
• Der Zugriff auf die Entitäten, da heißt
Suchen, Einfügen, Aktualisieren und
Löschen, wird über sog. DAOs realisiert
• Die DAOs kapseln die eigentlichen
Datenbankzugriffe (SELECT, INSERT, ...)
• Für jede Entität wird ein eigenes DAO
erstellt
Mobile App Development
82
ORM
• Data Access Object erstellen
public class OrmDbHelper extends OrmLiteSqliteOpenHelper {"
!
!
"
"
"
"
"
"
"
"
!
// ... onCreate() ..."
// Erzeugung des Data Access Objects"
public Dao<Todo, Integer> createTodoDAO() {"
" try {"
" " return DaoManager.createDao(connectionSource, Todo.class);"
" } catch (SQLException ex) {"
" " Log.e(LOG, "error creating DAO for Todo class", ex);"
" }"
" return null;"
}"
}
Mobile App Development
83
ORM
• Daten suchen und manipulieren
!
private void handleTodos() throws SQLException {"
// Data Access Object für Todo erstellen "
final Dao<Todo, Integer> todoDAO = dbHelper.createTodoDAO();"
!
// query all todos from db"
final List<Todo> allTodos = todoDAO.queryForAll();"
!
// create new todo"
Todo todo = new Todo(new Date(), "Test title", "Test description");"
todoDAO.create(todo);"
!
// delete todo element with id=5"
todoDAO.deleteById(5);"
!
// update todo element"
todoDAO.update(todo);"
}"
Mobile App Development
84
Literatur
I. Mark L. Murphy: The Busy Coder‘s Guide
To Android Development,Version 4.3,
2012
II. Thomas Künneth: Android 3 - Apps
entwickeln mit dem Android SDK
Galileo Press, 2011
III. http://developer.android.com/guide/topics/
data/data-storage.html
85
Herunterladen