diff --git a/src/com/activeandroid/DatabaseHelper.java b/src/com/activeandroid/DatabaseHelper.java index 7158c5bb6..d138655e2 100644 --- a/src/com/activeandroid/DatabaseHelper.java +++ b/src/com/activeandroid/DatabaseHelper.java @@ -23,15 +23,20 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; +import java.util.Iterator; import java.util.List; - +import java.util.Map; +import java.util.Set; +import android.content.ContentValues; import android.content.Context; +import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.text.TextUtils; - import com.activeandroid.util.IOUtils; import com.activeandroid.util.Log; import com.activeandroid.util.NaturalOrderComparator; @@ -39,6 +44,30 @@ import com.activeandroid.util.SqlParser; public final class DatabaseHelper extends SQLiteOpenHelper { + /** + * Table name in database. + */ + public static final String TABLE_NAME = "table_schema"; + + /** + * The name column in table_schema. + */ + public static final String COLUMN_NAME = "name"; + + /** + * The type column in table_schema. + */ + public static final String COLUMN_TYPE = "type"; + + /** + * Constant for normal table. + */ + public static final int NORMAL_TABLE = 0; + + /** + * Constant for intermediate join table. + */ + public static final int INTERMEDIATE_JOIN_TABLE = 1; ////////////////////////////////////////////////////////////////////////////////////// // PUBLIC CONSTANTS ////////////////////////////////////////////////////////////////////////////////////// @@ -76,13 +105,16 @@ public void onCreate(SQLiteDatabase db) { executeCreate(db); executeMigrations(db, -1, db.getVersion()); executeCreateIndex(db); + updateDataTableSchema(db); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { executePragmas(db); executeCreate(db); - executeMigrations(db, oldVersion, newVersion); + updateTableSchema(db);//增加表记录 + dropTables(db); + updateTables(db); } ////////////////////////////////////////////////////////////////////////////////////// @@ -162,6 +194,345 @@ private void executeCreate(SQLiteDatabase db) { } } + private void updateDataTableSchema(SQLiteDatabase db){ + db.beginTransaction(); + try { + for (TableInfo tableInfo : Cache.getTableInfos()) { + if(!tableInfo.getTableName().equalsIgnoreCase(TABLE_NAME)){ + db.execSQL(SQLiteUtils.createTableDefinition(tableInfo)); + ContentValues values = new ContentValues(); + values.put(COLUMN_NAME, tableInfo.getTableName()); + values.put(COLUMN_TYPE, 0); + db.insert(TABLE_NAME, null, values); + } + } + db.setTransactionSuccessful(); + } + finally { + db.endTransaction(); + } + } + + private void dropTables(SQLiteDatabase db){ + List droptables = findTablesToDrop(db); + if(droptables==null || droptables.isEmpty())return; + db.beginTransaction(); + try { + for(int i=0,j=droptables.size();i removeColumnNames = new ArrayList(); + Map dbColumnsMap = tableModelDB.getColumns(); + Set dbColumnNames = dbColumnsMap.keySet(); + for (String dbColumnName : dbColumnNames) { + if (!tableModel.mFiledNames.containsKey(dbColumnName) && !dbColumnName.equalsIgnoreCase("ID")) { + removeColumnNames.add(dbColumnName); + } + } + + if (removeColumnNames != null && !removeColumnNames.isEmpty()) { + + String alterToTempTableSQL = "alter table "+tableName+" rename to "+tableName + "_temp"; + String createNewTableSQL = generateCreateNewTableSQL(removeColumnNames, tableName, db); + String dataMigrationSQL = generateDataMigrationSQL(removeColumnNames, tableName, db); + String dropTempTableSQL = "drop table if exists " + tableName + "_temp"; + String[] sqls = { alterToTempTableSQL, createNewTableSQL, dataMigrationSQL, dropTempTableSQL }; + + db.beginTransaction(); + try { + if (sqls != null) { + for (String sql : sqls) { + db.execSQL(sql); + } + } + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } + + + //添加数据库没有的列 + List newColumnsList =new ArrayList(); + for (String columnName : tableModel.mFiledNames.keySet()) { + boolean isNewColumn = true; + for (String dbColumnName : getTableModel(tableModel.getTableName(),db).getColumnNames()) { + if (columnName.equalsIgnoreCase(dbColumnName)) { + isNewColumn = false; + break; + } + } + if (isNewColumn) { + if (!columnName.equalsIgnoreCase("id")) { + newColumnsList.add(SQLiteUtils.createColumnDefinition(tableModel,tableModel.mFiledNames.get(columnName))); + } + } + } + + if(newColumnsList !=null && !newColumnsList.isEmpty()){ + db.beginTransaction(); + try { + for (String columnName : newColumnsList) { + StringBuilder addColumnSQL = new StringBuilder(); + addColumnSQL.append("alter table ").append(tableModel.getTableName()).append(" add column ").append(columnName).append(" "); + Log.d("add column sql is >> " + addColumnSQL); + db.execSQL(addColumnSQL.toString()); + } + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } + // changeColumnsType(findColumnTypesToChange()); + } + } + + + //增加新表 + private String generateCreateNewTableSQL(Collection removeColumnNames, String tableName,SQLiteDatabase db) { + TableModel tableModelDB = getTableModel(tableName,db); + for (String removeColumnName : removeColumnNames) { + tableModelDB.removeColumnIgnoreCases(removeColumnName); + } + Map columnsMap = tableModelDB.getColumns(); + Set columnNames = columnsMap.keySet(); + removeId(columnNames); + StringBuilder createTableSQL = new StringBuilder("create table "); + createTableSQL.append(tableName).append(" ("); + createTableSQL.append("Id integer primary key autoincrement,"); + Iterator i = columnNames.iterator(); + if (!i.hasNext()) { + createTableSQL.deleteCharAt(createTableSQL.length() - 1); + } + boolean needSeparator = false; + while (i.hasNext()) { + if (needSeparator) { + createTableSQL.append(", "); + } + needSeparator = true; + String columnName = i.next(); + createTableSQL.append(columnName).append(" ").append(columnsMap.get(columnName)); + } + createTableSQL.append(")"); + Log.d("add column sql is >> " + createTableSQL); + return createTableSQL.toString(); + } + + //合成数据 + private String generateDataMigrationSQL(Collection removeColumnNames, String tableName,SQLiteDatabase db) { + List columnNames = new ArrayList(); + for (String columnName : getTableModel(tableName,db).getColumnNames()) { + if (!removeColumnNames.contains(columnName)) { + columnNames.add(columnName); + } + } + if (!columnNames.isEmpty()) { + StringBuilder sql = new StringBuilder(); + sql.append("insert into ").append(tableName).append("("); + boolean needComma = false; + for (String columnName : columnNames) { + if (needComma) { + sql.append(", "); + } + needComma = true; + sql.append(columnName); + } + sql.append(") "); + sql.append("select "); + needComma = false; + for (String columnName : columnNames) { + if (needComma) { + sql.append(", "); + } + needComma = true; + sql.append(columnName); + } + sql.append(" from ").append(tableName + "_temp"); + return sql.toString(); + } else { + return null; + } + } + + private void updateTableSchema(SQLiteDatabase db) { + List putTableSchemaNames = new ArrayList(); + List tablesDB = getTablesDB(db); + + Collection tableInfos = Cache.getTableInfos(); + for (Iterator iterator = tableInfos.iterator(); iterator.hasNext();) { + TableInfo tableInfo = (TableInfo) iterator.next(); + String tableName = tableInfo.getTableName(); + if(shouldCreateThisTable(tablesDB,tableName) && !tableName.equalsIgnoreCase(TABLE_NAME)){ + db.beginTransaction(); + try { + ContentValues values = new ContentValues(); + values.put(COLUMN_NAME, tableName); + values.put(COLUMN_TYPE, 0); + db.insert(TABLE_NAME, null, values); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } + } + } + private boolean shouldCreateThisTable(List tablesDB,String tableName) { + boolean ishave = false; + for (Iterator iterator = tablesDB.iterator(); iterator.hasNext();) { + String string = (String) iterator.next(); + if(string.equalsIgnoreCase(tableName)){ + ishave = true; + break; + } + } + return !ishave; + } + + + private void removeId(Set columnNames) { + String idName = ""; + for (String columnName : columnNames) { + if (columnName.equalsIgnoreCase("ID")) { + idName = columnName; + break; + } + } + if (!TextUtils.isEmpty(idName)) { + columnNames.remove(idName); + } + } + + public TableModel getTableModel(String tableName, SQLiteDatabase db) { + if (!isTableExists(tableName, db)) { + return null; + } + TableModel tableModelDB = new TableModel(); + tableModelDB.setTableName(tableName); + String checkingColumnSQL = "pragma table_info(" + tableName + ")"; + Cursor cursor = null; + try { + cursor = db.rawQuery(checkingColumnSQL, null); + if (cursor.moveToFirst()) { + do { + String name = cursor.getString(cursor.getColumnIndexOrThrow("name")); + String type = cursor.getString(cursor.getColumnIndexOrThrow("type")); + tableModelDB.addColumn(name, type); + } while (cursor.moveToNext()); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (cursor != null) { + cursor.close(); + } + } + return tableModelDB; + } + + public boolean isTableExists(String tableName, SQLiteDatabase db) { + boolean exist = false; + try { + List tableNames = getTablesDB(db); + for (String string : tableNames) { + if(tableName.equalsIgnoreCase(string)){ + exist = true; + break; + } + } + } catch (Exception e) { + e.printStackTrace(); + exist = false; + } + return exist; + } + + private List findTablesToDrop(SQLiteDatabase db) { + List dropTableNames = new ArrayList(); + Cursor cursor = null; + try { + cursor = db.query(TABLE_NAME, null, null, null, null, null, null); + if (cursor.moveToFirst()) { + do { + String tableName = cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_NAME)); + int tableType = cursor.getInt(cursor.getColumnIndexOrThrow(COLUMN_TYPE)); + if (shouldDropThisTable(tableName, tableType) && !tableName.equalsIgnoreCase(TABLE_NAME)) { + Log.d("need to drop " + tableName); + dropTableNames.add(tableName); + } + } while (cursor.moveToNext()); + } + } catch (Exception ex) { + ex.printStackTrace(); + } finally { + if (cursor != null) { + cursor.close(); + } + } + return dropTableNames; + } + + private List getTablesDB(SQLiteDatabase db) { + List tableNames = new ArrayList(); + Cursor cursor = null; + try { + cursor = db.query(TABLE_NAME, null, null, null, null, null, null); + if (cursor.moveToFirst()) { + do { + String tableName = cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_NAME)); + int tableType = cursor.getInt(cursor.getColumnIndexOrThrow(COLUMN_TYPE)); + if(tableType == NORMAL_TABLE){ + tableNames.add(tableName); + } + } while (cursor.moveToNext()); + } + } catch (Exception ex) { + ex.printStackTrace(); + } finally { + if (cursor != null) { + cursor.close(); + } + } + return tableNames; + } + + private boolean shouldDropThisTable(String tableName, int tableType) { + return !containsIgnoreCases(Cache.getTableInfos(), tableName)&& tableType == NORMAL_TABLE; + } + + public boolean containsIgnoreCases(Collection collection, String string) { + if (collection == null) { + return false; + } + if (string == null) { + return collection.contains(null); + } + boolean contains = false; + for (TableInfo element : collection) { + if (string.equalsIgnoreCase(element.getTableName())) { + contains = true; + break; + } + } + return contains; + } + private boolean executeMigrations(SQLiteDatabase db, int oldVersion, int newVersion) { boolean migrationExecuted = false; try { diff --git a/src/com/activeandroid/Table_Schema.java b/src/com/activeandroid/Table_Schema.java new file mode 100644 index 000000000..8366fcb02 --- /dev/null +++ b/src/com/activeandroid/Table_Schema.java @@ -0,0 +1,25 @@ +package com.activeandroid; + +import com.activeandroid.Model; +import com.activeandroid.annotation.Column; +import com.activeandroid.annotation.Table; + +@Table(name = "Table_Schema") +public class Table_Schema extends Model{ + @Column(name = "name") + private String name; + @Column(name = "type") + private int type; + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } + public int getType() { + return type; + } + public void setType(int type) { + this.type = type; + } +}