Back to project page Android-Lib-Database.
The source code is released under:
Apache License
If you think the Android project Android-Lib-Database listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
package android.lib.database; //from www . j a v a 2 s .com import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import android.content.Context; import android.content.pm.PackageManager.NameNotFoundException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.text.TextUtils; import android.util.Log; /** * Handles database table creation. */ public class DatabaseOpenHelper extends SQLiteOpenHelper { private static final String CREATE_TABLE = "CREATE TABLE IF NOT EXISTS %s (%s)"; //$NON-NLS-1$ private static final String CREATE_INDEX = "CREATE INDEX IF NOT EXISTS idx_%s_%s ON %s (%s)"; //$NON-NLS-1$ private static final String CREATE_INDEX_UNIQUE = "CREATE UNIQUE INDEX IF NOT EXISTS idx_%s_%s ON %s (%s)"; //$NON-NLS-1$ private static final String PRIMARY_KEY = "PRIMARY KEY"; //$NON-NLS-1$ private static final String AUTO_INCREMENT = "AUTOINCREMENT"; //$NON-NLS-1$ private static final String NOT_NULL = "NOT NULL"; //$NON-NLS-1$ private static final String UNIQUE = "UNIQUE"; //$NON-NLS-1$ private static final String COLUMN_SEPARATOR = ", "; //$NON-NLS-1$ private static final String SPACE = " "; //$NON-NLS-1$ private static final String TYPE_INTEGER = "INTEGER"; //$NON-NLS-1$ private static final String TYPE_REAL = "REAL"; //$NON-NLS-1$ private static final String TYPE_TEXT = "TEXT"; //$NON-NLS-1$ private static final String TYPE_BLOB = "BLOB"; //$NON-NLS-1$ private static final Comparator<CompositeIndex> COMPOSITE_INDEX_COMPARATOR = new Comparator<CompositeIndex>() { @Override public int compare(final CompositeIndex lhs, final CompositeIndex rhs) { return lhs.order() - rhs.order(); } }; private static final Comparator<UniqueCompositeIndex> UNIQUE_COMPOSITE_INDEX_COMPARATOR = new Comparator<UniqueCompositeIndex>() { @Override public int compare(final UniqueCompositeIndex lhs, final UniqueCompositeIndex rhs) { return lhs.order() - rhs.order(); } }; /** * The tables of the database to create in {@link #onCreate(SQLiteDatabase)}. */ protected final Class<?>[] tables; /** * Creates a new instance of {@link DatabaseOpenHelper} by invoking * its super-class {@link android.database.sqlite.SQLiteOpenHelper}, and optionally stores * the given <code>tables</code> for database table creation when {@link #onCreate(SQLiteDatabase)} is called. * @param context the context to use to open or create the database * @param name the file name of the database file, or null for an in-memory database * @param version the version number of the database (starting at 1); if the database is older, * {@link #onUpgrade(SQLiteDatabase, int, int)} will be used to upgrade the database; * if the database is newer, {@link #onDowngrade(SQLiteDatabase, int, int)} will be used to downgrade the database * @param tables the tables of the database to create in {@link #onCreate(SQLiteDatabase)}. * For subsequent call, <code>tables</code> can be <code>null</code>. */ protected DatabaseOpenHelper(final Context context, final String name, final int version, final Class<?>... tables) { super(context, name, null, version); this.tables = tables; } /** * Creates a new instance of {@link DatabaseOpenHelper} with the given <code>name</code> and <code>version</code>. * @param context the context to use to open or create the database * @param name the file name of the database file, or null for an in-memory database * @param version the version number of the database (starting at 1); if the database is older, * {@link #onUpgrade(SQLiteDatabase, int, int)} will be used to upgrade the database; * if the database is newer, {@link #onDowngrade(SQLiteDatabase, int, int)} will be used to downgrade the database * @param tables the tables of the database to create in {@link #onCreate(SQLiteDatabase)}. * For subsequent call, <code>tables</code> can be <code>null</code>. */ public static DatabaseOpenHelper newInstance(final Context context, final String name, final int version, final Class<?>... tables) { return new DatabaseOpenHelper(context, name, version, tables); } /** * Creates a new instance of {@link DatabaseOpenHelper} with the given <code>version</code>. * <p>The application package name will be used as the name of the database.</p> * @param context the context to use to open or create the database * @param version the version number of the database (starting at 1); if the database is older, * {@link #onUpgrade(SQLiteDatabase, int, int)} will be used to upgrade the database; * if the database is newer, {@link #onDowngrade(SQLiteDatabase, int, int)} will be used to downgrade the database * @param tables the tables of the database to create in {@link #onCreate(SQLiteDatabase)}. * For subsequent call, <code>tables</code> can be <code>null</code>. */ public static DatabaseOpenHelper newInstance(final Context context, final int version, final Class<?>... tables) { return new DatabaseOpenHelper(context, context.getPackageName(), version, tables); } /** * Creates a new instance of {@link DatabaseOpenHelper} with the given <code>name</code>. * <p>The application version code will be used as the database version.</p> * @param context the context to use to open or create the database * @param name the file name of the database file, or null for an in-memory database * @param tables the tables of the database to create in {@link #onCreate(SQLiteDatabase)}. * For subsequent call, <code>tables</code> can be <code>null</code>. */ public static DatabaseOpenHelper newInstance(final Context context, final String name, final Class<?>... tables) { try { return new DatabaseOpenHelper( context, name, context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionCode, tables); } catch (final NameNotFoundException e) { throw new RuntimeException(e); } } /** * Creates a new instance of {@link DatabaseOpenHelper}. * <p>The application package name and version code will be used as the name and version of the database respectively.</p> * @param context the context to use to open or create the database * @param tables the tables of the database to create in {@link #onCreate(SQLiteDatabase)}. * For subsequent call, <code>tables</code> can be <code>null</code>. */ public static DatabaseOpenHelper newInstance(final Context context, final Class<?>... tables) { try { return new DatabaseOpenHelper( context, context.getPackageName(), context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionCode, tables); } catch (final NameNotFoundException e) { throw new RuntimeException(e); } } @Override public void onCreate(final SQLiteDatabase database) { if (this.tables != null) { database.beginTransaction(); try { for (final Class<?> table : this.tables) { DatabaseOpenHelper.createTable(database, table); } database.setTransactionSuccessful(); } catch (final UnsupportedTypeException e) { Log.e(this.getClass().getName(), e.getMessage(), e); } finally { database.endTransaction(); } } } @Override public void onUpgrade(final SQLiteDatabase database, final int oldVersion, final int newVersion) { } private static void createTable(final SQLiteDatabase database, final Class<?> clazz) throws UnsupportedTypeException { final Table table = clazz.getAnnotation(Table.class); if (table != null) { final String tableName = TextUtils.isEmpty(table.value()) ? clazz.getSimpleName() : table.value(); final StringBuilder columnsBuilder = new StringBuilder(); final List<String> createIndexStatements = new ArrayList<String>(); final Set<Field> fields = new HashSet<Field>(); final Map<Field, String> fieldNames = new HashMap<Field, String>(); final Map<String, TreeMap<CompositeIndex, Field>> compositeIndexes = new TreeMap<String, TreeMap<CompositeIndex, Field>>(); final Map<String, TreeMap<UniqueCompositeIndex, Field>> uniqueCompositeIndexes = new TreeMap<String, TreeMap<UniqueCompositeIndex, Field>>(); for (final Field field : clazz.getFields()) { fields.add(field); } for (final Field field : clazz.getDeclaredFields()) { fields.add(field); } for (final Field field : fields) { field.setAccessible(true); final Column column = field.getAnnotation(Column.class); if (column != null) { if (columnsBuilder.length() > 0) { columnsBuilder.append(DatabaseOpenHelper.COLUMN_SEPARATOR); } columnsBuilder.append(DatabaseOpenHelper.getColumnDefinition(field)); final String fieldName = TextUtils.isEmpty(column.value()) ? field.getName() : column.value(); final Index index = field.getAnnotation(Index.class); fieldNames.put(field, fieldName); if (index != null) { createIndexStatements.add(String.format(index.unique() ? DatabaseOpenHelper.CREATE_INDEX_UNIQUE : DatabaseOpenHelper.CREATE_INDEX, tableName, fieldName, tableName, fieldName)); } // Searches for composite index annotation final CompositeIndex compositeIndex = field.getAnnotation(CompositeIndex.class); if (compositeIndex != null) { final String[] indexNames = compositeIndex.value() == null ? new String[] { fieldName } : compositeIndex.value(); for (final String indexName : indexNames) { final TreeMap<CompositeIndex, Field> indexes = compositeIndexes.containsKey(indexName) ? compositeIndexes.get(indexName) : new TreeMap<CompositeIndex, Field>(DatabaseOpenHelper.COMPOSITE_INDEX_COMPARATOR); indexes.put(compositeIndex, field); compositeIndexes.put(indexName, indexes); } } // Searches for unique composite index annotation final UniqueCompositeIndex uniqueCompositeIndex = field.getAnnotation(UniqueCompositeIndex.class); if (uniqueCompositeIndex != null) { final String[] indexNames = uniqueCompositeIndex.value() == null ? new String[] { fieldName } : uniqueCompositeIndex.value(); for (final String indexName : indexNames) { final TreeMap<UniqueCompositeIndex, Field> indexes = uniqueCompositeIndexes.containsKey(indexName) ? uniqueCompositeIndexes.get(indexName) : new TreeMap<UniqueCompositeIndex, Field>(DatabaseOpenHelper.UNIQUE_COMPOSITE_INDEX_COMPARATOR); indexes.put(uniqueCompositeIndex, field); uniqueCompositeIndexes.put(indexName, indexes); } } } } // Creates table database.execSQL(String.format(DatabaseOpenHelper.CREATE_TABLE, tableName, columnsBuilder.toString())); // Creates indexes for (final String createIndexStatement : createIndexStatements) { database.execSQL(createIndexStatement); } // Creates composite indexes if (compositeIndexes.size() > 0) { for (final Map.Entry<String, TreeMap<CompositeIndex, Field>> entry : compositeIndexes.entrySet()) { final StringBuilder indexBuilder = new StringBuilder(); for (final Map.Entry<CompositeIndex, Field> index : entry.getValue().entrySet()) { if (indexBuilder.length() > 0) { indexBuilder.append(DatabaseOpenHelper.COLUMN_SEPARATOR); } indexBuilder.append(fieldNames.get(index.getValue())); } database.execSQL(String.format(DatabaseOpenHelper.CREATE_INDEX, tableName, entry.getKey(), tableName, indexBuilder.toString())); } } // Creates unique composite indexes if (uniqueCompositeIndexes.size() > 0) { for (final Map.Entry<String, TreeMap<UniqueCompositeIndex, Field>> entry : uniqueCompositeIndexes.entrySet()) { final StringBuilder indexBuilder = new StringBuilder(); for (final Map.Entry<UniqueCompositeIndex, Field> index : entry.getValue().entrySet()) { if (indexBuilder.length() > 0) { indexBuilder.append(DatabaseOpenHelper.COLUMN_SEPARATOR); } indexBuilder.append(fieldNames.get(index.getValue())); } database.execSQL(String.format(DatabaseOpenHelper.CREATE_INDEX_UNIQUE, tableName, entry.getKey(), tableName, indexBuilder.toString())); } } } } private static String getColumnDefinition(final Field field) throws UnsupportedTypeException { final Column column = field.getAnnotation(Column.class); final StringBuilder builder = new StringBuilder(); builder.append(TextUtils.isEmpty(column.value()) ? field.getName() : column.value()); builder.append(DatabaseOpenHelper.SPACE); builder.append(DatabaseOpenHelper.getColumnType(field)); if (column.primaryKey()) { builder.append(DatabaseOpenHelper.SPACE); builder.append(DatabaseOpenHelper.PRIMARY_KEY); if (column.autoIncrement()) { builder.append(DatabaseOpenHelper.SPACE); builder.append(DatabaseOpenHelper.AUTO_INCREMENT); } } else { if (!column.nullable()) { builder.append(DatabaseOpenHelper.SPACE); builder.append(DatabaseOpenHelper.NOT_NULL); } } final Index index = field.getAnnotation(Index.class); if (index != null) { if (index.unique()) { builder.append(DatabaseOpenHelper.SPACE); builder.append(DatabaseOpenHelper.UNIQUE); } } return builder.toString(); } private static String getColumnType(final Field field) throws UnsupportedTypeException { final UseConverter converter = field.getAnnotation(UseConverter.class); final Class<?> type = converter == null ? field.getType() : converter.type(); if (type.equals(boolean.class) || type.equals(Boolean.class)) { return DatabaseOpenHelper.TYPE_INTEGER; } else if (type.equals(byte.class) || type.equals(Byte.class)) { return DatabaseOpenHelper.TYPE_INTEGER; } else if (type.equals(byte[].class) || type.equals(Byte[].class)) { return DatabaseOpenHelper.TYPE_BLOB; } else if (type.equals(double.class) || type.equals(Double.class)) { return DatabaseOpenHelper.TYPE_REAL; } else if (type.equals(float.class) || type.equals(Float.class)) { return DatabaseOpenHelper.TYPE_REAL; } else if (type.equals(int.class) || type.equals(Integer.class)) { return DatabaseOpenHelper.TYPE_INTEGER; } else if (type.equals(long.class) || type.equals(Long.class)) { return DatabaseOpenHelper.TYPE_INTEGER; } else if (type.equals(short.class) || type.equals(Short.class)) { return DatabaseOpenHelper.TYPE_INTEGER; } else if (type.equals(String.class)) { return DatabaseOpenHelper.TYPE_TEXT; } else { throw new UnsupportedTypeException(String.format("%s is not of SQLite-compatible type", field.getName())); //$NON-NLS-1$ } } }