是否曾调用过onUpgrade方法?

时间:2010-07-02 07:07:11

标签: java android android-sqlite

onUpgradeSQLiteOpenHelper方法是否曾被调用过?如果是这样,什么时候被召唤?如果它没有被开发人员调用,那为什么呢?这个功能到底发生了什么?我已经看到了删除所有表的示例,但随后评论说放弃所有表并不是你应该做的。有什么建议?

6 个答案:

答案 0 :(得分:36)

对于那些想要了解调用onUpgrade()的确切时刻的人,可能是在调用getReadableDatabase()getWriteableDatabase()时。

对于那些不清楚如何确保它被触发的人,答案是:当提供给SqLiteOpenHelper的构造函数的数据库版本更新时,会触发它。这是一个例子

public class dbSchemaHelper extends SQLiteOpenHelper {

private String sql;
private final String D_TAG = "FundExpense";
//update this to get onUpgrade() method of sqliteopenhelper class called
static final int DB_VERSION = 2; 
static final String DB_NAME = "fundExpenseManager";

public dbSchemaHelper(Context context) {
    super(context, DB_NAME, null, DB_VERSION);
    // TODO Auto-generated constructor stub
}

现在...... onUpgrade()

@Override
public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {
    sql = "ALTER TABLE " + fundExpenseSchema.Expense.TABLE_NAME + " ADD COLUMN " + fundExpenseSchema.Expense.FUNDID + " INTEGER";
    arg0.execSQL(sql);
}

答案 1 :(得分:28)

如果您正在使用SQLiteOpenHelper,则每当您更改数据库版本时都会调用onUpgrade。 还有一个额外的要求。数据库名称必须保持不变。

Old Version:
dbName = "mydb.db"
dbVersion = 1

New Version:
dbName = "mydb.db"
dbVersion = 2

在内容提供程序的onCreate中,您创建了一个采用这些参数的SQLiteOpenHelper实例。您的SQLiteOpenHelper实现如下所示:

public static final class MySQLiteOpenHelper extends SQLiteOpenHelper {

        public MySQLiteOpenHelper(Context context, int dbVersion, String dbName) {
            super(context, dbName, null, dbVersion);
        }

        @Override
        public void onCreate(SQLiteDatabase db) {
            //Code to create your db here
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            // Code to upgrade your db here
        }

}

答案 2 :(得分:21)

在构造版本比打开的数据库版本更新的SQLiteOpenHelper时调用它。做什么取决于旧版本和新版本之间的数据库更改。如果不删除已更改的表,则唯一的情况是更改的位置超过添加的列。然后,您可以使用ALTER TABLE语句将新列添加到表签名。

答案 3 :(得分:4)

查看所有帖子并运行调试代码时,我仍然不清楚何时会看到onUpgrade被调用。我开始认为Android有一个严重的缺陷..

此页面上的信息让我得出了最终解决方案。非常感谢所有贡献者!

这为我解决了......

public class DatabaseHelper extends SQLiteOpenHelper {
    public static String TAG = DatabaseHelper.class.getName();
    private static final int DATABASE_VERSION = 42;
    private static final String DATABASE_NAME = "app_database";
    private static final String OLD_TABLE = "old_and_useless";

    public DatabaseHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion ) {
        if( newVersion > oldVersion) {
            Log.d( TAG, "cool! you noticed." );

            db.execSQL( "DROP TABLE IF EXISTS " + OLD_TABLE );
            // other calls like onCreate if necessary

        } else {
            Log.d( TAG, "Hey! didn't you see me?" );
        }

    }

    public void checkDatabaseVersion() {
        SQLiteDatabase db = this.getWritableDatabase();

        // if the DATABASE_VERSION is newer
        //    onUpgrade is called before this is reached
    }


    // other code removed for readability...
}

getWritableDatabase()和getReadableDatabase()确实会导致onUpgrade调用。我没有检查其他方法,因为这些符合我的需求。

继续阅读,踢球者即将到来......

当我终于意识到我的调试期间db版本正在更新时,我的初始Activity中的代码启发了我... ugh!

DatabaseHelper dbHelper = new DatabaseHelper( this );
dbHelper.checkDatabaseVersion();

注意:调用DatabaseHelper构造函数会更新db版本

在构造函数调用之后,db被标记为新版本。在调用getWritableDatabase()或getReadableDatabase()之前杀死应用程序并且您使用的是新版本。此后,新的执行永远不会调用onUpgrade方法,直到再次增加DATABASE_VERSION。 (叹息!现在看起来很荒谬:)

我的建议是在应用的早期阶段添加某种“checkDatabaseVersion()”。或者,如果您创建一个SQLiteOpenHelper对象,请确保在应用程序死亡之前调用其中一个方法(getWritableDatabase(),getReadableDatabase()等)。

我希望这可以节省其他人同样的头脑刮伤!...:p

答案 4 :(得分:2)

查看// don't forget to add a doctype. since you didn't i'm picking HTML5 <!DOCTYPE html> <html> <head> // also add a character set <meta charset='utf-8'> <title>DeportesRulz.com</title> <script> function AJAX() { try{ xmlHttp=new XMLHttpRequest(); // Firefox, Opera 8.0+, Safari return xmlHttp; } catch (e) { try{ xmlHttp=new ActiveXObject('Msxml2.XMLHTTP'); // Internet Explorer return xmlHttp; } catch (e) { try{ xmlHttp=new ActiveXObject('Microsoft.XMLHTTP'); return xmlHttp; } catch (e) { alert('Your browser does not support AJAX.'); return false; } } } } // Timestamp for preventing IE caching the GET request (common function) function fetch_unix_timestamp() { return parseInt(new Date().getTime().toString().substring(0, 10)) } //takes a div element as a parameter. the data-rownum on that div will be used as the rownumber passed to file1.php function refreshLiveStatus(targetDiv) { // access the data-rownum attribute of the targetDiv var rowNum = targetDiv.dataset['rownum']; // if you don't need the square brackets, remove them var url = './file1.php?row=[' + rowNum + ']'; var xmlHttp_three = AJAX(); // No cache var timestamp = fetch_unix_timestamp(); // you had `url+'?t='+timestamp` here before. you can't have multiple '?' in a url like that. you need to use `&` as below var nocacheurl = url+'&t='+timestamp; // The code... xmlHttp_three.onreadystatechange = function() { if(xmlHttp_three.readyState==4) { targetDiv.innerHTML=xmlHttp_three.responseText; // start the next refresh setTimeout(function() { refreshLiveStatus(targetDiv); },seconds); } } xmlHttp_three.open('GET',nocacheurl,true); xmlHttp_three.send(null); } var seconds = 5; // just set it here so you don't have to keep setting the variable (unless you want different refresh rates??) seconds *= 1000; // if they're seconds make them seconds and save yourself the trouble of having to remember to do `* 1000` everywhere // Start the refreshing process // function to replace window.onload below. It's not necessary to replace window.onload but this will let you easily add more than one function to the window.onload // console logs left in for science function ready(fn) { console.log('readying'); if (document.readyState != 'loading') { console.log('loading...'); fn(); } else { console.log('content not loaded'); document.addEventListener('DOMContentLoaded', fn); } } ready(function() { // querySelectorAll lets you use CSS selectors to select HTML elements. Note that the `liveStatus` class is added in the PHP code below. var divs = document.querySelectorAll('div.liveStatus'); console.log(divs); // left here for you to what's selected for(var i = 0, imax = divs.length; i < imax; i++) { // the below is a closure. definitely read about these if you don't know what they are already (function(liveDiv) { refreshLiveStatus(liveDiv); })(divs[i]); } }); // style the .liveStatus div. leave it out and see what happens </script> <style type='text/css'> div.liveStatus { display: inline-block; } </style> </head> <body> <?php //Obtener fecha date_default_timezone_set('America/Caracas'); $fechaabrev = strftime("Sat, %b 17, %y"); //[Variables globales] $xml = simplexml_load_file('./MLB.xml'); $juegos = count($xml->EventType->Date); //Obtener todos los juegos // you can use `$i<$juegos` instead of `$i<=$juegos-1` and save having to do that subtraction for ($i=0;$i<$juegos;$i++) { //Obtener solo juegos del dia if ($xml->EventType->Date[$i]->attributes()->DTEXT == $fechaabrev) { $juegosdeldia = count($xml->EventType->Date[$i]->Event); //Bucle principal juegos del dia for ($k=0;$k<$juegosdeldia;$k++) { $juego_dia = $xml->EventType->Date[$i]->attributes()->DTEXT.' '; $juego_horaf = $xml->EventType->Date[$i]->Event[$k]->Time->attributes()->TTEXT.' '; $juego_horaf = explode(" ", $juego_horaf); $juego_hora = $juego_horaf[0]; $juego_live = $xml->EventType->Date[$i]->Event[$k]->attributes()->LIVE_ENABLED.' '; $juego_ligaf = $xml->EventType->Date[$i]->Event[$k]->NOTE.' '; $juego_ligaf = explode(" ", $juego_ligaf); if ($juego_ligaf[0] == 'AMERICAN') { $juego_liga = 'American League'; } else if ($juego_ligaf[0] == 'NATIONAL') { $juego_liga = 'National League'; } $equipos = count($xml->EventType->Date[$i]->Event[$k]->Competitor); //Bucle equipos for ($j=0;$j<=$equipos-1;$j++) { //Obtener equipo A if ($xml->EventType->Date[$i]->Event[$k]->Competitor[$j]->attributes()->NUM == 2) { $equipo_a = $xml->EventType->Date[$i]->Event[$k]->Competitor[$j]->attributes()->NAME; }//--Fin Obtener equipo A-- //Obtener equipo B if ($xml->EventType->Date[$i]->Event[$k]->Competitor[$j]->attributes()->NUM == 1) { $equipo_b = $xml->EventType->Date[$i]->Event[$k]->Competitor[$j]->attributes()->NAME; }//--Fin Obtener equipo B-- }//--Fin Bucle equipos-- //--------------------------------------------[Imprimiendo datos]---------------------------------------------------------// // note how we add the rownumber with `...[" . ($k+1) . "]...` // also see how we added the `liveStatus` class AND the data-rownum attribute to the "live" div echo " <table cellpadding='3' cellspacing='1' class='tablehead' style='background-color:white;'> <tbody> <tr class='stathead'> <td colspan='5' style='background-color:#0B3861;color:white;'> [" . ($k+1) . "] $equipo_a vs $equipo_b | [MLB] $juego_liga <div class='liveStatus' name='live[$k]' data-rownum='$k' id='live[$k]'></div> </td> </tr> <tr class='colhead' style='text-align:center;'> <td>$juego_hora</td> <td>A GANAR</td> <td>ALTA/BAJA (6.5)</td> <td>RUN LINE</td> <td> ALTA/BAJA (3.5 | 5to inning) <div id='odds'></div> </td> </tr> </tbody> </table>"; //------------------------------------------[Fin Imprimiendo datos]---------------------------------------------------------// }//--Fin Bucle principal juegos del dia-- }//--Fin Obtener solo juegos del dia-- }//--Fin Obtener todos los juegos-- ?> </body> </html> 源代码,我们可以通过SqliteOpenHelperonCreate()方法了解onUpgrade()onDowngradegetWritableDatabase()

getReadableDatabase()

答案 5 :(得分:1)

当您致电getReadableDatabasegetWritableDatabase时,实际上称为

深度潜水:

您在SQLiteOpenHelper的构造函数中传递版本号,该构造函数存储在名为mNewVersion的变量中。而已。此时没有任何事情发生。

每次调用getReadableDatabase或getWritableDatabase时,它都会调用名为getDatabaseLocked的方法。此方法将获取数据库的现有版本号,并将其与mNewVersion进行比较。

  1. 如果具有给定名称的数据库不存在,则会调用onCreate
  2. 如果新版本大于旧版本,则会调用onUpgrade
  3. 如果新版本低于现有版本,则会引发异常。
  4. 如果它们相等,它将继续打开数据库。
  5. 我应该在onCreate和onUpgrade上写什么?

    onCreate应该包含第一次创建架构的代码。

    您可以第一次将onUpgrade留空,因为第一次不会调用它。如果您想在以后更改表结构,那么该代码应该放在这里。

    SQLiteOpenHelper.java (源代码)

    public SQLiteDatabase getWritableDatabase() {
        synchronized (this) {
            return getDatabaseLocked(true);
        }
    }
    
     public SQLiteDatabase getReadableDatabase() {
        synchronized (this) {
            return getDatabaseLocked(false);
        }
    }
    
    private SQLiteDatabase getDatabaseLocked(boolean writable) {
       .
       .
    
         final int version = db.getVersion();
    
            if (version != mNewVersion) {
                if (db.isReadOnly()) {
                    throw new SQLiteException("Can't upgrade read-only database from version " +
                            db.getVersion() + " to " + mNewVersion + ": " + mName);
                }
    
                db.beginTransaction();
                try {
                    if (version == 0) {
                        onCreate(db);
                    } else {
                        if (version > mNewVersion) {
                            onDowngrade(db, version, mNewVersion);
                        } else {
                            onUpgrade(db, version, mNewVersion);
                        }
                    }
                    db.setVersion(mNewVersion);
                    db.setTransactionSuccessful();
                } finally {
                    db.endTransaction();
                }
           }
    
           onOpen(db);
     }