Build an android ORM framework opendroid (2) -- automatically create a database
In the previous blog "Create an android ORM framework opendroid (1)-use An ORM framework", I believe you have learned about the use of opendroid. From this blog, we officially went to opendroid's source code analysis to build our own ORM framework!
Before the official start, you need to ensure that there is a copy of opendroid source code in hand, if you have not downloaded opendroid, please download the source code of opendroid in the http://git.oschina.net/qibin/OpenDroid.
All database operations start with database creation. Today, let's take a look at how opendroid helps us automatically create databases. Do you still remember how to configure the link ing? In the open-droid.xml, by configuring the mapping node to tell opendroid the java bean we need to map. So when will database operations start? In the insert statement, the save () method inherited from OpenDroid is called! Before that, we didn't have any database operations, so we started with the save () method to see how opendroid created the database.
/*** Insert data ** @ return the last inserted id */public long save () {try {Class
Klass = (Class
) GetClass (); ContentValues cv = new ContentValues (); generateData (klass, cv); return CRUD. insert (klass. getSimpleName (), cv, sSqliteDatabase);} catch (Exception e) {e. printStackTrace ();} return-1 ;}
Row 3 stores data in the database by calling the CRUD static method insert. The last insert parameter sSqliteDatabas is of concern to us. Let's take a look at its definition:
private static SQLiteDatabase sSqliteDatabase = sOpenHelper.getWritableDatabase();
SSqliteDatabase is returned by calling getWriteableDatabase () through sOpenHelper. I believe everyone is familiar with it. Let's take a look at the definition of sOpenHelper:
private static CreateDB sOpenHelper = new CreateDB();
Here, a new CreateDB is created. Through the class name, we can fully understand that CreateDB is the key to creating a database. Let's take a look at CreateDB:
public class CreateDB extends SQLiteOpenHelper {public CreateDB() {super(DroidApplication.sContext, OpenDroidHelper.getDBInfoBean().getName(),null, OpenDroidHelper.getDBInfoBean().getVersion(), new DefaultDatabaseErrorHandler());}@Overridepublic void onCreate(SQLiteDatabase db) {for(String sql : OpenDroidHelper.getDBInfoBean().getSqls()) {db.execSQL(sql);}}}
Here I only intercept the Code related to database creation. As you can see, CreateDB inherits from SQLiteOpenHelper. You must be familiar with it here. Let's start with the constructor method, the constructor of the parent class is called directly in the constructor. The first parameter is a context object, which is very simple in DroidApplication. It is called getApplicationContext () in onCreate () assign a value to the sContext static variable in DroidApplication. No code is posted here. You can find it in the source code. Check the following parameters through OpenDroidHelper. getDBInfoBean. Now let's look at the SQL statement that we found in onCreate to retrieve the table creation by traversing OpenDroidHelper. getDBInfoBean (). getSqls (). Now let's go to OpenDroidHelper.
Public class OpenDroidHelper {public static final String TAG_DROID = open-droid; public static final String TAG_VERSION = version; public static final String TAG_NAME = name; public static final String TAG_MAPPING = mapping; private static DBBean sDBBean; public static DBBean getDBInfoBean () {if (sDBBean = null) {generateDBInfoBean ();} return sDBBean ;} /*** parse the open_droid.xml file under the Asserts directory and generate DBInfoBean */p Rivate static void generateDBInfoBean () {try {XmlPullParser pullParser = Xml. newPullParser (); InputStream inputStream = DroidApplication. sContext. getAssets (). open (open_droid.xml); pullParser. setInput (inputStream, UTF-8); int type = pullParser. getEventType (); String tagName = null; while (type! = XmlPullParser. END_DOCUMENT) {if (type = XmlPullParser. START_TAG) {tagName = pullParser. getName (); if (tagName. equals (TAG_DROID) {sDBBean = new DBBean ();} else if (tagName. equals (TAG_VERSION) {// obtain the version sDBBean. setVersion (Integer. parseInt (pullParser. getAttributeValue (null, value);} else if (tagName. equals (TAG_NAME) {// obtain the database name sDBBean. setName (pullParser. getAttributeValue (null, value);} else if (tagName. equals (TAG_MAPPING) {// obtain all table creation statements sDBBean. addSql (generateSql (pullParser);} type = pullParser. next () ;}} catch (Exception e) {e. printStackTrace ();}} /*** generate the table creation SQL statement * @ param pullParser * @ return * @ throws ClassNotFoundException * @ throws XmlPullParserException * @ throws IOException */private static String generateSql (XmlPullParser pullParser) throws ClassNotFoundException, XmlPullParserException, IOException {// obtain classClass through reflection
Klass = (Class
) Class. forName (pullParser. getAttributeValue (null, class); StringBuilder SQL = new StringBuilder (create table); // get the class name, getSimpleName get the class name, getName () Get the package name + class name SQL. append (klass. getSimpleName ()). append (); // automatically creates a _ idsql statement. append (_ id integer primary key autoincrement,); // obtain all fields Field [] fields = klass. getDeclaredFields (); for (Field field: fields) {// if it is public, it indicates that it is not a table Field if (field. isAccessible () {continue;} // obtain the field name String name = field. getName (); SQL. append (name ). append (); // obtains the Class of the field type.
FieldType = field. getType (); if (fieldType = String. class) {// For Stringsql. append (text,);} else if (fieldType = Integer. class | fieldType = int. class) {SQL. append (integer,);} else if (fieldType = Long. class | fieldType = long. class) {SQL. append (integer,);} else if (fieldType = Boolean. class | fieldType = boolean. class) {SQL. append (boolean,);} else if (fieldType = Float. class | fieldType = float. class) {SQL. append (float,) ;}} SQL. replace (SQL. length ()-1, SQL. length (),); SQL. append (); return SQL. toString ();}}
The Code is a little long. Let's take a look. First, let's take a look at the previously called getDBInfoBean (). This method is very simple, that is, a DBBean object is returned. However, it also calls the generateDBInfoBean () method, which can be seen by the method name, it is used to generate DBBean.
/*** Parse the open_droid.xml file under the Asserts directory and generate DBInfoBean */private static void generateDBInfoBean () {try {XmlPullParser pullParser = Xml. newPullParser (); InputStream inputStream = DroidApplication. sContext. getAssets (). open (open_droid.xml); pullParser. setInput (inputStream, UTF-8); int type = pullParser. getEventType (); String tagName = null; while (type! = XmlPullParser. END_DOCUMENT) {if (type = XmlPullParser. START_TAG) {tagName = pullParser. getName (); if (tagName. equals (TAG_DROID) {sDBBean = new DBBean ();} else if (tagName. equals (TAG_VERSION) {// obtain the version sDBBean. setVersion (Integer. parseInt (pullParser. getAttributeValue (null, value);} else if (tagName. equals (TAG_NAME) {// obtain the database name sDBBean. setName (pullParser. getAttributeValue (null, value);} else if (tagName. equals (TAG_MAPPING) {// obtain all table creation statements sDBBean. addSql (generateSql (pullParser);} type = pullParser. next () ;}} catch (Exception e) {e. printStackTrace ();}}
Well, in the generateDBInfoBean method, we are familiar with the XMLPullParser code, the role is to parse the open-droid.xml file, get the database name, database version and data table information. Although it is very long, it is very simple. In the 20 rows, we obtained the database version number and saved it to DBBean. In the same 23 rows, we obtained the database name. Pay attention to row 26th, we want to add SQL statements to DBBean. What SQL statements should we add? It must be an SQL statement used to create a table. Let's take a look at the generateSql () method.
/*** Generate the table creation SQL statement * @ param pullParser * @ return * @ throws ClassNotFoundException * @ throws XmlPullParserException * @ throws IOException */private static String generateSql (XmlPullParser pullParser) throws ClassNotFoundException, XmlPullParserException, IOException {// obtain classClass through reflection
Klass = (Class
) Class. forName (pullParser. getAttributeValue (null, class); StringBuilder SQL = new StringBuilder (create table); // get the class name, getSimpleName get the class name, getName () Get the package name + class name SQL. append (klass. getSimpleName ()). append (); // automatically creates a _ idsql statement. append (_ id integer primary key autoincrement,); // obtain all fields Field [] fields = klass. getDeclaredFields (); for (Field field: fields) {// if it is public, it indicates that it is not a table Field if (field. isAccessible () {continue;} // obtain the field name String name = field. getName (); SQL. append (name ). append (); // obtains the Class of the field type.
FieldType = field. getType (); if (fieldType = String. class) {// For Stringsql. append (text,);} else if (fieldType = Integer. class | fieldType = int. class) {SQL. append (integer,);} else if (fieldType = Long. class | fieldType = long. class) {SQL. append (integer,);} else if (fieldType = Boolean. class | fieldType = boolean. class) {SQL. append (boolean,);} else if (fieldType = Float. class | fieldType = float. class) {SQL. append (float,) ;}} SQL. replace (SQL. length ()-1, SQL. length (),); SQL. append (); return SQL. toString ();}
GenerateSql () contains all reflection code. If you are not familiar with reflection, we recommend that you first check java reflection because the reflection mechanism is widely used in opendroid,
12 rows, get the class we want to map through reflection, and then 14 ~ Row 18 is an SQL statement used to initialize and create a table. opendroid automatically adds a _ id field to the table. Therefore, we do not need to define the bean again when defining the bean.
Row 21 obtains all fields defined in this class and cyclically traverses these fields in Row 22.
14 ~ The 26 rows show that if the field is public, ignore it. Therefore, if you do not want to map a field to the database, it is defined as public.
29 ~ Row 30: append the table name to the SQL statement used to create the table.
33 ~ Row 44: this field type is determined to append the type of this field to the SQL statement used to create the table.
Row 46 is used to delete the last ","
Then, the SQL statement for creating the table is completed.
So far, we have analyzed the process of automatically creating a database with opendroid. Here we will summarize:
1. We use SQLiteOpenHelper of android API to create a database.
2. In onCreate of SQLiteOpenHelper, traverse the automatically generated table creation statements and execute them.
3. How to generate a table creation statement? In OpenDroidHelper, the class name and field name of the class are obtained through reflection, and the table creation statement is pieced together through StringBuilder.
4. For convenience, we put the parsed data name, database version, and table creation statement into DBBean in OpenDroidHelper, in the CreateDB class, we can use DBBean to conveniently obtain database information.
OK. This is the general process for automatic database creation. In the following blog, we will introduce the CRUD operation of opendroid and Its Database Upgrade mechanism one by one.