上一章,我们读完了Conguration这个类。最后我们发现,Conguration就是为了数据库的初始化。其中包含了内存大小、数据库名称、数据库版本、parser信息。
public static synchronized void initialize(Configuration configuration) { if (sIsInitialized) { Log.v("ActiveAndroid already initialized."); return; } sContext = configuration.getContext(); sModelInfo = new ModelInfo(configuration); sDatabaseHelper = new DatabaseHelper(configuration); // TODO: It would be nice to override sizeOf here and calculate the memory // actually used, however at this point it seems like the reflection // required would be too costly to be of any benefit. We‘ll just set a max // object size instead. sEntities = new LruCache<String, Model>(configuration.getCacheSize()); openDatabase(); sIsInitialized = true; Log.v("ActiveAndroid initialized successfully."); }
上一章读到了这里,需要初始化一个DatabaseHelper,作为数据库的辅助类。今天来阅读这个类:
public final class DatabaseHelper extends SQLiteOpenHelper { ////////////////////////////////////////////////////////////////////////////////////// // PUBLIC CONSTANTS ////////////////////////////////////////////////////////////////////////////////////// public final static String MIGRATION_PATH = "migrations"; ////////////////////////////////////////////////////////////////////////////////////// // PRIVATE FIELDS ////////////////////////////////////////////////////////////////////////////////////// private final String mSqlParser; ////////////////////////////////////////////////////////////////////////////////////// // CONSTRUCTORS ////////////////////////////////////////////////////////////////////////////////////// public DatabaseHelper(Configuration configuration) { super(configuration.getContext(), configuration.getDatabaseName(), null, configuration.getDatabaseVersion()); copyAttachedDatabase(configuration.getContext(), configuration.getDatabaseName()); mSqlParser = configuration.getSqlParser(); } …… }
由于DatabaseHelper这个类继承了SQLiteOpenHelper,构造时先构造父类:
public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version) { this(context, name, factory, version, null); }
这个构造方法需要输入context、name、version,这三个参数在Conguration类中都有。
将下来是copyAttachedDatabase方法
public void copyAttachedDatabase(Context context, String databaseName) { final File dbPath = context.getDatabasePath(databaseName); // If the database already exists, return if (dbPath.exists()) { return; } // Make sure we have a path to the file dbPath.getParentFile().mkdirs(); // Try to copy database file try { final InputStream inputStream = context.getAssets().open(databaseName); final OutputStream output = new FileOutputStream(dbPath); byte[] buffer = new byte[8192]; int length; while ((length = inputStream.read(buffer, 0, 8192)) > 0) { output.write(buffer, 0, length); } output.flush(); output.close(); inputStream.close(); } catch (IOException e) { Log.e("Failed to open file", e); } }
这个方法主要是 final InputStream inputStream = context.getAssets().open(databaseName); 获取assets中的数据库资源。如果在相同目录下,已经存在这个数据库就不会进行后面的操作。
然后,再往下看就是数据库的onCreate方法:
@Override public void onCreate(SQLiteDatabase db) { executePragmas(db); executeCreate(db); executeMigrations(db, -1, db.getVersion()); executeCreateIndex(db); }
一个一个方法看:
private void executePragmas(SQLiteDatabase db) { if (SQLiteUtils.FOREIGN_KEYS_SUPPORTED) { db.execSQL("PRAGMA foreign_keys=ON;"); Log.i("Foreign Keys supported. Enabling foreign key features."); } }
这个方法主要是开始外键可用。
关于外键,我身为一个野生伪程序员,看了下百度知道
关系型数据库中的一条记录中有若干个属性,若其中某一个属性组(注意是组)能唯一标识一条记录,该属性组就可以成为一个主键 比如 学生表(学号,姓名,性别,班级) 其中每个学生的学号是唯一的,学号就是一个主键 课程表(课程编号,课程名,学分) 其中课程编号是唯一的,课程编号就是一个主键 成绩表(学号,课程号,成绩) 成绩表中单一一个属性无法唯一标识一条记录,学号和课程号的组合才可以唯一标识一条记录,所以 学号和课程号的属性组是一个主键 成绩表中的学号不是成绩表的主键,但它和学生表中的学号相对应,并且学生表中的学号是学生表的主键,则称成绩表中的学号是学生表的外键 同理 成绩表中的课程号是课程表的外键 定义主键和外键主要是为了维护关系数据库的完整性,总结一下: 主键是能确定一条记录的唯一标识,比如,一条记录包括身份正号,姓名,年龄。身份证号是唯一能确定你这个人的,其他都可能有重复,所以,身份证号是主键。
继续看数据库onCreate方法中的第二个方法 executeCreate(db);:
private void executeCreate(SQLiteDatabase db) { db.beginTransaction(); try { for (TableInfo tableInfo : Cache.getTableInfos()) { db.execSQL(SQLiteUtils.createTableDefinition(tableInfo)); } db.setTransactionSuccessful(); } finally { db.endTransaction(); } }
这个方法用来创建表的一些,首先来看看Cache.getTableInfos()这个方法:
public static synchronized Collection<TableInfo> getTableInfos() { return sModelInfo.getTableInfos(); }
读到这里就断了,因为我们不知道ModelInfo是什么,在本章开头我们看到:
public static synchronized void initialize(Configuration configuration) { if (sIsInitialized) { Log.v("ActiveAndroid already initialized."); return; } sContext = configuration.getContext(); sModelInfo = new ModelInfo(configuration); sDatabaseHelper = new DatabaseHelper(configuration); // TODO: It would be nice to override sizeOf here and calculate the memory // actually used, however at this point it seems like the reflection // required would be too costly to be of any benefit. We‘ll just set a max // object size instead. sEntities = new LruCache<String, Model>(configuration.getCacheSize()); openDatabase(); sIsInitialized = true; Log.v("ActiveAndroid initialized successfully."); }
我们直接开始分析sDatabaseHelper = new DatabaseHelper(configuration);并没有阅读sModelInfo = new ModelInfo(configuration);导致信息的遗漏,因此我们只好倒过头来读ModelInfo这个类。这个失误有点伤,下次再来读:
Done