Repository navigation
Expand file tree
/
Copy pathOrmLiteSqliteOpenHelper.java
More file actions
348 lines (324 loc) · 12.9 KB
/
Copy pathOrmLiteSqliteOpenHelper.java
File metadata and controls
348 lines (324 loc) · 12.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
package com.j256.ormlite.android.apptools;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.sql.SQLException;
import com.j256.ormlite.android.AndroidConnectionSource;
import com.j256.ormlite.android.AndroidDatabaseConnection;
import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.dao.DaoManager;
import com.j256.ormlite.dao.RuntimeExceptionDao;
import com.j256.ormlite.logger.Logger;
import com.j256.ormlite.logger.LoggerFactory;
import com.j256.ormlite.misc.IOUtils;
import com.j256.ormlite.support.ConnectionSource;
import com.j256.ormlite.support.DatabaseConnection;
import com.j256.ormlite.table.DatabaseTableConfigLoader;
import android.content.Context;
import android.database.DatabaseErrorHandler;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
/**
* SQLite database open helper which can be extended by your application to help manage when the application needs to
* create or upgrade its database.
*
* @author kevingalligan, graywatson
*/
public abstract class OrmLiteSqliteOpenHelper extends SQLiteOpenHelper {
protected static Logger logger = LoggerFactory.getLogger(OrmLiteSqliteOpenHelper.class);
protected AndroidConnectionSource connectionSource = new AndroidConnectionSource(this);
protected boolean cancelQueriesEnabled;
private volatile boolean isOpen = true;
/**
* @param context
* Associated content from the application. This is needed to locate the database.
* @param databaseName
* Name of the database we are opening.
* @param factory
* Cursor factory or null if none.
* @param databaseVersion
* Version of the database we are opening. This causes {@link #onUpgrade(SQLiteDatabase, int, int)} to be
* called if the stored database is a different version.
*/
public OrmLiteSqliteOpenHelper(Context context, String databaseName, CursorFactory factory, int databaseVersion) {
super(context, databaseName, factory, databaseVersion);
logger.trace("{}: constructed connectionSource {}", this, connectionSource);
}
/**
* @param context
* Associated content from the application. This is needed to locate the database.
* @param databaseName
* Name of the database we are opening.
* @param factory
* Cursor factory or null if none.
* @param databaseVersion
* Version of the database we are opening. This causes {@link #onUpgrade(SQLiteDatabase, int, int)} to be
* called if the stored database is a different version.
* @param errorHandler
* The <a href=
* "https://developer.android.com/reference/android/database/DatabaseErrorHandler">DatabaseErrorHandler</a>
* to be used when sqlite reports database corruption, or null to use the default error handler.
*/
public OrmLiteSqliteOpenHelper(Context context, String databaseName, CursorFactory factory, int databaseVersion,
DatabaseErrorHandler errorHandler) {
super(context, databaseName, factory, databaseVersion, errorHandler);
logger.trace("{}: constructed connectionSource {}", this, connectionSource);
}
/**
* Same as the other constructor with the addition of a file-id of the table config-file. See
* {@link OrmLiteConfigUtil} for details.
*
* @param context
* Associated content from the application. This is needed to locate the database.
* @param databaseName
* Name of the database we are opening.
* @param factory
* Cursor factory or null if none.
* @param databaseVersion
* Version of the database we are opening. This causes {@link #onUpgrade(SQLiteDatabase, int, int)} to be
* called if the stored database is a different version.
* @param configFileId
* file-id which probably should be a R.raw.ormlite_config.txt or some static value.
*/
public OrmLiteSqliteOpenHelper(Context context, String databaseName, CursorFactory factory, int databaseVersion,
int configFileId) {
this(context, databaseName, factory, databaseVersion, openFileId(context, configFileId));
}
/**
* Same as the other constructor with the addition of a config-file. See {@link OrmLiteConfigUtil} for details.
*
* @param context
* Associated content from the application. This is needed to locate the database.
* @param databaseName
* Name of the database we are opening.
* @param factory
* Cursor factory or null if none.
* @param databaseVersion
* Version of the database we are opening. This causes {@link #onUpgrade(SQLiteDatabase, int, int)} to be
* called if the stored database is a different version.
* @param configFile
* Configuration file to be loaded.
*/
public OrmLiteSqliteOpenHelper(Context context, String databaseName, CursorFactory factory, int databaseVersion,
File configFile) {
this(context, databaseName, factory, databaseVersion, openFile(configFile));
}
/**
* Same as the other constructor with the addition of a input stream to the table config-file. See
* {@link OrmLiteConfigUtil} for details.
*
* @param context
* Associated content from the application. This is needed to locate the database.
* @param databaseName
* Name of the database we are opening.
* @param factory
* Cursor factory or null if none.
* @param databaseVersion
* Version of the database we are opening. This causes {@link #onUpgrade(SQLiteDatabase, int, int)} to be
* called if the stored database is a different version.
* @param stream
* Stream opened to the configuration file to be loaded. It will be closed when this method returns.
*/
public OrmLiteSqliteOpenHelper(Context context, String databaseName, CursorFactory factory, int databaseVersion,
InputStream stream) {
super(context, databaseName, factory, databaseVersion);
if (stream == null) {
return;
}
// if a config file-id was specified then load it into the DaoManager
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(stream), 4096);
stream = null;
DaoManager.addCachedDatabaseConfigs(DatabaseTableConfigLoader.loadDatabaseConfigFromReader(reader));
} catch (SQLException e) {
throw new IllegalStateException("Could not load object config file", e);
} finally {
IOUtils.closeQuietly(reader);
IOUtils.closeQuietly(stream);
}
}
/**
* What to do when your database needs to be created. Usually this entails creating the tables and loading any
* initial data.
*
* <p>
* <b>NOTE:</b> You should use the connectionSource argument that is passed into this method call or the one
* returned by getConnectionSource(). If you use your own, a recursive call or other unexpected results may result.
* </p>
*
* @param database
* Database being created.
* @param connectionSource
* To use get connections to the database to be created.
*/
public abstract void onCreate(SQLiteDatabase database, ConnectionSource connectionSource);
/**
* What to do when your database needs to be updated. This could mean careful migration of old data to new data.
* Maybe adding or deleting database columns, etc..
*
* <p>
* <b>NOTE:</b> You should use the connectionSource argument that is passed into this method call or the one
* returned by getConnectionSource(). If you use your own, a recursive call or other unexpected results may result.
* </p>
*
* @param database
* Database being upgraded.
* @param connectionSource
* To use get connections to the database to be updated.
* @param oldVersion
* The version of the current database so we can know what to do to the database.
* @param newVersion
* The version that we are upgrading the database to.
*/
public abstract void onUpgrade(SQLiteDatabase database, ConnectionSource connectionSource, int oldVersion,
int newVersion);
/**
* Get the connection source associated with the helper.
*/
public ConnectionSource getConnectionSource() {
if (!isOpen) {
// we don't throw this exception, but log it for debugging purposes
logger.warn(new IllegalStateException(), "Getting connectionSource was called after closed");
}
return connectionSource;
}
/**
* Satisfies the {@link SQLiteOpenHelper#onCreate(SQLiteDatabase)} interface method.
*/
@Override
public final void onCreate(SQLiteDatabase db) {
ConnectionSource cs = getConnectionSource();
/*
* The method is called by Android database helper's get-database calls when Android detects that we need to
* create or update the database. So we have to use the database argument and save a connection to it on the
* AndroidConnectionSource, otherwise it will go recursive if the subclass calls getConnectionSource().
*/
DatabaseConnection conn = cs.getSpecialConnection(null);
boolean clearSpecial = false;
if (conn == null) {
conn = new AndroidDatabaseConnection(db, true, cancelQueriesEnabled);
try {
cs.saveSpecialConnection(conn);
clearSpecial = true;
} catch (SQLException e) {
throw new IllegalStateException("Could not save special connection", e);
}
}
try {
onCreate(db, cs);
} finally {
if (clearSpecial) {
cs.clearSpecialConnection(conn);
}
}
}
/**
* Satisfies the {@link SQLiteOpenHelper#onUpgrade(SQLiteDatabase, int, int)} interface method.
*/
@Override
public final void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
ConnectionSource cs = getConnectionSource();
/*
* The method is called by Android database helper's get-database calls when Android detects that we need to
* create or update the database. So we have to use the database argument and save a connection to it on the
* AndroidConnectionSource, otherwise it will go recursive if the subclass calls getConnectionSource().
*/
DatabaseConnection conn = cs.getSpecialConnection(null);
boolean clearSpecial = false;
if (conn == null) {
conn = new AndroidDatabaseConnection(db, true, cancelQueriesEnabled);
try {
cs.saveSpecialConnection(conn);
clearSpecial = true;
} catch (SQLException e) {
throw new IllegalStateException("Could not save special connection", e);
}
}
try {
onUpgrade(db, cs, oldVersion, newVersion);
} finally {
if (clearSpecial) {
cs.clearSpecialConnection(conn);
}
}
}
/**
* Close any open connections.
*/
@Override
public void close() {
super.close();
connectionSource.close();
/*
* We used to set connectionSource to null here but now we just set the closed flag and then log heavily if
* someone uses getConectionSource() after this point.
*/
isOpen = false;
}
/**
* Return true if the helper is still open. Once {@link #close()} is called then this will return false.
*/
public boolean isOpen() {
return isOpen;
}
/**
* Get a DAO for our class. This uses the {@link DaoManager} to cache the DAO for future gets.
*
* <p>
* NOTE: This routing does not return Dao<T, ID> because of casting issues if we are assigning it to a custom
* DAO. Grumble.
* </p>
*/
public <D extends Dao<T, ?>, T> D getDao(Class<T> clazz) throws SQLException {
// special reflection fu is now handled internally by create dao calling the database type
Dao<T, ?> dao = DaoManager.createDao(getConnectionSource(), clazz);
@SuppressWarnings("unchecked")
D castDao = (D) dao;
return castDao;
}
/**
* Get a RuntimeExceptionDao for our class. This uses the {@link DaoManager} to cache the DAO for future gets.
*
* <p>
* NOTE: This routing does not return RuntimeExceptionDao<T, ID> because of casting issues if we are assigning
* it to a custom DAO. Grumble.
* </p>
*/
public <D extends RuntimeExceptionDao<T, ?>, T> D getRuntimeExceptionDao(Class<T> clazz) {
try {
Dao<T, ?> dao = getDao(clazz);
@SuppressWarnings({ "unchecked", "rawtypes" })
D castDao = (D) new RuntimeExceptionDao(dao);
return castDao;
} catch (SQLException e) {
throw new RuntimeException("Could not create RuntimeExcepitionDao for class " + clazz, e);
}
}
@Override
public String toString() {
return getClass().getSimpleName() + "@" + Integer.toHexString(super.hashCode());
}
private static InputStream openFileId(Context context, int fileId) {
InputStream stream = context.getResources().openRawResource(fileId);
if (stream == null) {
throw new IllegalStateException("Could not find object config file with id " + fileId);
}
return stream;
}
private static InputStream openFile(File configFile) {
try {
if (configFile == null) {
return null;
} else {
return new FileInputStream(configFile);
}
} catch (FileNotFoundException e) {
throw new IllegalArgumentException("Could not open config file " + configFile, e);
}
}
}