in the previous blog, "Build Android ORM Framework Opendroid (VI)-CASCADE query" We talked about the implementation principle of opendroid last function query. Today we will be opendroid a play, but also the last of this series of blog-Database upgrade program.
Said database upgrade, I have a headache, why? Because the previous project did not consider the database upgrade scenario at all, it would drop the table directly, resulting in the result that "the previous data has disappeared". The amount ... It is really not a very small thing to disappear from thin air, if the data is not important, the important data? That disappears? User upgraded a bit of software, the results of the data are all gone ... That's one more thing about hanging silk.
Opendroid provides a way to upgrade the database, which is certainly not perfect. There must be a better plan, if you find that you have a good solution, please do not hesitate to enlighten.
OK, let's get down to the chase. First of all, let's talk about the principle of my plan: in fact, it is simple to query the data before drop table and save it to the collection, and after creating the new table, try to insert the data. The idea of the principle is so simple that I have always thought that this scheme is too bad, but I did not think of a better outcome plan, it can only be the first.
As we all know, the Android Sqliteopenhelper class provides an abstract method Onupgrade () to allow users to flexibly customize the database upgrade scheme, the simplest way is I mentioned directly drop table. Since upgrade's rights are in our hands, why don't we take Onupgrade ()?
Take a look at the code first:
@Override public void Onupgrade (sqlitedatabase db, int oldversion, int newversion) { System.out.println (" Upgrade Database "); Upgrade (db); }
In the Onupgrade in addition to a print, in fact, actually useful for a code, of course, is also called a method of our custom, then we start from the upgrade () This method began to say:
/** * Upgrade database * @param DB database link */private <t extends opendroid> void upgrade (SQLite Database db) {try {xmlpullparser pullparser = Xml.newpullparser (); InputStream InputStream = DroidApplication.sContext.getAssets (). Open ("Open_droid.xml"); Pullparser.setinput (InputStream, "utf-8"); int type = Pullparser.geteventtype (); while (type = xmlpullparser.end_document) {if (type = = Xmlpullparser.start_tag) {//Get Mappi ng if (Pullparser.getname (). Equals (opendroidhelper.tag_mapping)) {DumpData (db, PULLP Arser); }} type = Pullparser.next (); }} catch (Exception e) {e.printstacktrace (); }//Execute CREATE DATABASE onCreate (db); }
7~9 can see we're going to parse open_. Droid.xml file, and we usually parse, using a while loop, observing while loop, we get useful information in the 15~17 row, if the current tag is mapping or, we go to call the DumpData, which Xmlpullparser will be as a second Parameters passed past.
At the end of the method, we call the overloaded OnCreate method to create a new table, and of course the recovery of the data. We'll analyze this later, and then we'll look at the DumpData method.
/** * Dumps data from the database into the program * @param DB Database connection * @param pullparser * @throws Exception */private <t extends OPENDROID&G T void DumpData (Sqlitedatabase db, Xmlpullparser Pullparser) throws Exception {class<opendroid> Klass = (class<opendroid>) Class.forName (Pullparser.getattributevalue (NULL, "class")); String tableName = Klass.getsimplename (); Table name Cursor cursor = db.rawquery ("SELECT * from" + tableName, NULL); T T; Method m; String MethodName; String ColumnName; Traverse Cursor for (Cursor.movetofirst ();! Cursor.isafterlast (); Cursor.movetonext ()) {t = (t) klass.newinstance (); Instantiate by reflection final int columnCount = Cursor.getcolumncount (); for (int i=0;i<columncount;i++) {columnName = Cursor.getcolumnname (i);//Get field name//try once, if not The field corresponds to the method that digests the exception and continues the try {switch (Cursor.gettype (i)) {case Cursor.field_typE_integer:methodname = Columnname.equals ("_id")? "SetId": Crud.getmethodname (Cursor.getcolumnname (i)); m = Klass.getmethod (MethodName, Int.class); The Reflective method M.invoke (T, Cursor.getint (i)); Execution method break; Case Cursor.FIELD_TYPE_FLOAT:methodName = Crud.getmethodname (Cursor.getcolumnname (i)); m = Klass.getmethod (MethodName, Float.class); M.invoke (t, cursor.getfloat (i)); Break Default:methodname = Crud.getmethodname (Cursor.getcolumnname (i)); m = Klass.getmethod (MethodName, String.class); M.invoke (t, cursor.getstring (i)); Break }} catch (Exception e) {e.printstacktrace (); }} molddata.add (t); } Cursor.close (); Db.execsql ("drop table if exists" + tableName); Delete the old database}
This approach is long and critical, and our database upgrade scenario will end here.
Line 9th, we get a class by class.forname, what mapping is it based on? Take a look at our Open_droid.xml file for a glance.
<open-droid> <version value= "6"/> <name value= "school"/> <mapping class= " Org.loader.opendroid.Student "/> <mapping class=" Org.loader.opendroid.Grade "/> </open-droid >
In this XML, we are going to map a class through org.loader.opendroid.Student.
Line 10th, we get the class name of the class, of course, we have to manipulate the name of the table, eh? Why just a table? Take a closer look at where this method is called, we call it in a loop, which is to iterate through the XML nodes in the loop, and each time we get to the mapping node, we call this method.
11 rows, we execute a SELECT statement, the current table all the data query out, the query out of the data we how to deal with it?
To answer this question, we have to go to the following for loop to find the answer.
In the For loop, 20 rows, which instantiate the above class through reflection, why instantiate it in a loop? Because each row of data we need to use an object to save.
21 rows, gets the number of all columns in the current row.
Next there is a for loop, which we are the columns of each row in the loop, in which the data for each column is taken.
26 lines, into a switch statement, according to convention, we only analyze a case statement.
In the first case, if the field being changed is an integer type, 28 rows, we assemble a setter as we said before, and of course, if it is _id, we define it as setid directly.
30 lines, reflect this method, wait for the following to execute.
Of course, 31 rows we are going to implement this method, we all know that setter method is required parameters, parameters are of course we query out the forefront of the data.
48 lines, we put the strength of this object into a set.
When we finish querying the current table, the table is useless because we have saved the data, so in 51 rows, the table is deleted.
At this point, we've all queried the data from the old version of the database. Let's go back to the OnCreate method to see if the new table is created and how the data is restored.
@Override public void OnCreate (Sqlitedatabase db) {for (String Sql:OpenDroidHelper.getDBInfoBean (). GETSQLS ( ) { db.execsql (SQL); } Restore Data if (!molddata.isempty ()) {for (opendroid item:molddata) { item.save (db);}} }
In the first few lines of code, we have explained in the "Build Android ORM Framework Opendroid (ii)-auto-Create DATABASE", here does not repeat, we focus on the blog omitted in the few lines of code, it is these lines of code, the implementation of the old data to the new table transfer.
8 lines, first to determine whether the Molddata is an empty collection, because the OnCreate method is not only to perform when the database upgrade.
Next, the entire collection is traversed, and the Save method of each item is called to save the data to the new table, and of course we reuse the Save method in the Opendroid class, because it is all insert. From here we can also see that this molddata set of generics is definitely opendroid.
Private arraylist<opendroid> Molddata = new arraylist<opendroid> ();
Well, at this point, we opendroid provide a simple database upgrade program is done, and our opendroid also introduced almost, the rest of the things are auxiliary things. Oh, right, here also to mention: Careful friend may have found, opendroid in the operation of the database is not the default to shut down the database, but the egg pain provides open and release two methods, do not believe you can see the code:
/** * Open Database * /public static void open () { if (ssqlitedatabase = = null) { ssqlitedatabase = Sopenhelper.getwritabledatabase (); } } /** * Release database */public static void release () { if (ssqlitedatabase! = null && Ssqlitedatabase.isopen ()) { ssqlitedatabase.close (); } Ssqlitedatabase = null; }
What is this for? In fact, I did it by default, but when I write to the database upgrade to recover data, because save is executed in a loop, the database may be turned on/off multiple times in a very short time, so Android throws an exception, in a fury, I have transformed the code into this way, and if there is a better solution for the great God, please enlighten me.
Well, this is the end of our Opendroid series blog, of course, to make an ORM framework itself is not important, it is important to learn how to do an ORM framework, what others can do, why can't we? Well, as a programmer, we have to work hard to be a "creator" instead of simply staying on a "user".
Finally, Opendroid's Open source address:http://git.oschina.net/qibin/OpenDroid
Build Android ORM Framework Opendroid (VII)--Database upgrade scheme