android - 无法在事务中附加数据库

标签 android sqlite android-sqlite

我正在开发一个必须使用两个数据库的应用程序。我想将新数据库表中的记录添加到旧数据库表中,我已将新数据库放在我项目的 Assets 文件夹中,我可以使用新数据库和旧数据库进行查询。

问题是,当我尝试使用 onUpgrade() 方法将新数据库附加到旧数据库时,发生了一个错误,即无法在事务中附加数据库

我发现很多链接都有这个问题,我已经尝试了很多但都没有成功。我是 SQLite 和 Android 的新手,请告诉我如何在代码中将数据库附加到另一个数据库。

数据库助手类:DatabaseHelper.java

public class DatabaseHelper extends SQLiteOpenHelper {
Context context;
SQLiteDatabase sourceDatabase;
private String TAG = this.getClass().getSimpleName();

private static final String DATABASE_NAME = "namesdemo_db";
private static final String SOURCE_DB_NAME = "namesdemo_db_s";
private static final int DATABASE_VERSION = 23;

private String DATABASE_PATH;
File dbFile;

// TABLE NAMES
private static final String TABLE_EMPLOYEE = "employee";
private static final String TABLE_FAVORITE = "fevorite";

// KEYS of Table Employee
private static final String KEY_ID = "id";
private static final String KEY_NAME = "name";
private static final String KEY_ADDRESS = "address";

// Keys of Table Fevorite
private static final String KEY_FEV_ID = "fev_id";
// private static final String KEY_ID = "id";

private static final String CREATE_TBL_EMPLOYEE = "CREATE TABLE " + TABLE_EMPLOYEE + "(" + KEY_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + KEY_NAME
        + " TEXT," + KEY_ADDRESS + " TEXT)";
private static final String CREATE_TBL_FEVORITE = "CREATE TABLE " + TABLE_FAVORITE + "(" + KEY_FEV_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + KEY_ID
        + " INTEGER )";

public DatabaseHelper(Context context) {
    super(context, DATABASE_NAME, null, DATABASE_VERSION);
    // TODO Auto-generated constructor stub
    this.context = context;
    DATABASE_PATH = "/data/data/" + context.getApplicationContext().getPackageName() + "/databases/";

    Log.v(TAG, " DB Path " + DATABASE_PATH);
}

@Override
public void onCreate(SQLiteDatabase db) {
    // TODO Auto-generated method stub

    Log.v(TAG, CREATE_TBL_EMPLOYEE);

    db.execSQL(CREATE_TBL_EMPLOYEE);

    db.execSQL(CREATE_TBL_FEVORITE);

    Log.v(TAG, " OnCreate Executed");

}

private void copyDataBase(String dbPath) {
    try {
        InputStream assestDB = context.getAssets().open(SOURCE_DB_NAME);

        OutputStream appDB = new FileOutputStream(dbPath + SOURCE_DB_NAME, false);

        byte[] buffer = new byte[1024];
        int length;
        while ((length = assestDB.read(buffer)) > 0) {
            appDB.write(buffer, 0, length);
        }

        appDB.flush();
        appDB.close();
        assestDB.close();
    } catch (IOException e) {
        e.printStackTrace();
    }

    Log.v(TAG, " copyDataBase() called");

}

public long createName(String name, String address) {
    long count = 0;
    SQLiteDatabase db = getWritableDatabase();
    ContentValues values = new ContentValues();
    values.put(KEY_NAME, name);
    values.put(KEY_ADDRESS, address);
    count = db.insert(TABLE_EMPLOYEE, null, values);
    db.close();
    Log.v(TAG, +count + " row(s) inserted!");
    return count;
}

public long createFevorite(int id) {
    long count = 0;
    SQLiteDatabase db = getWritableDatabase();
    ContentValues values = new ContentValues();
    values.put(KEY_ID, id);
    count = db.insert(TABLE_FAVORITE, null, values);
    db.close();
    Log.v(TAG, +count + " row(s) inserted in " + TABLE_FAVORITE);
    return count;
}

public List<Employee> getAllNames() {
    List<Employee> nameList = new ArrayList<Employee>();
    String query = "Select * from " + TABLE_EMPLOYEE;

    SQLiteDatabase db = getReadableDatabase();
    Cursor c = db.rawQuery(query, null);
    if (c.moveToFirst()) {
        do {
            Employee e = new Employee();
            e.setID(c.getInt(c.getColumnIndex(KEY_ID)));
            e.setName(c.getString(c.getColumnIndex(KEY_NAME)));
            e.setAddress(c.getString(c.getColumnIndex(KEY_ADDRESS)));
            nameList.add(e);

        } while (c.moveToNext());
    }
    return nameList;
}

public List<Employee> getAllNames(SQLiteDatabase db) {
    List<Employee> nameList = new ArrayList<Employee>();
    String query = "Select * from " + TABLE_EMPLOYEE;

    Cursor c = db.rawQuery(query, null);
    if (c.moveToFirst()) {
        do {
            Employee e = new Employee();
            e.setID(c.getInt(c.getColumnIndex(KEY_ID)));
            e.setName(c.getString(c.getColumnIndex(KEY_NAME)));
            e.setAddress(c.getString(c.getColumnIndex(KEY_ADDRESS)));
            nameList.add(e);

            Log.v(TAG, "ID: " + e.getID());
            Log.v(TAG, "NAme: " + e.getName());
            Log.v(TAG, "Address" + e.getAddress());

        } while (c.moveToNext());
    }

    return nameList;
}

private boolean checkDatabase() {
    // SQLiteDatabase checkdb = null;
    boolean checkdb = false;
    try {
        String myPath = DATABASE_PATH + SOURCE_DB_NAME;
        File dbfile = new File(myPath);
        // checkdb =
        // SQLiteDatabase.openDatabase(myPath,null,SQLiteDatabase.OPEN_READWRITE);
        checkdb = dbfile.exists();
    } catch (SQLiteException e) {
        System.out.println("Database doesn't exist");
    }
    return checkdb;
}

public void openDatabase() throws SQLException {
    String path = DATABASE_PATH + "namesdemo_db_s";
    sourceDatabase = SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.OPEN_READWRITE);
    Log.v(TAG, " openDatabase() called ");
}

public synchronized void closeDatabase() {
    if (sourceDatabase != null) {
        sourceDatabase.close();
        Log.v(TAG, " closeDatabase() called ");
    }
    super.close();
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    // TODO Auto-generated method stub

    String drop_tbl = "DROP TABLE " + TABLE_EMPLOYEE;
    String attach_db = "ATTACH DATABASE '" + SOURCE_DB_NAME + "' AS 'temp_db'";
    String create_table = "CREATE TABLE " + TABLE_EMPLOYEE + "(" + KEY_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + KEY_NAME + " TEXT," + KEY_ADDRESS
            + " TEXT)";
    String insert = "INSERT INTO " + TABLE_EMPLOYEE + "(" + KEY_ID + "," + KEY_NAME + "," + KEY_ADDRESS + ") SELECT * FROM temp_db." + TABLE_EMPLOYEE;

    boolean dbExists = checkDatabase();
    if (dbExists) {

        Log.v(TAG, " DB  exists");

        // getAllNames(sourceDatabase);
    } else {
        Log.v(TAG, " DB does not exists");
        copyDataBase(DATABASE_PATH);
        // openDatabase();
        // getAllNames(sourceDatabase);
    }
    db.execSQL(attach_db);
    openDatabase();
    //db.endTransaction();
    db.execSQL(drop_tbl);
    db.execSQL(create_table);

    db.execSQL(insert);

    Log.v(TAG, " onUpgrade() Executed");

    db.close();
    sourceDatabase.close();

}

最佳答案

我找到了 2 种附加数据库的方法。

  1. 不要在 onCreateonUpgrade 中“附加数据库”。它会因为“无法在事务中附加数据库”而失败。 但是您可以在初始化数据库时附加数据库,然后调用 getReadableDatabase() 以确保创建和升级数据库。 然后你可以运行附加命令。

    sqLiteHelper = new SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION); // new SQLiteOpenHelper
    SQLiteDatabase db = sqLiteHelper.getReadableDatabase(); // make sure the database will be created
    db.execSQL("ATTACH DATABASE '/databases/xx.db' AS xx;"); // do attach database
    
  2. 但是如果您想在 onUpgrade() 之前附加,请尝试以下方式:

在 onCreate() 中结束事务,然后附加,然后开始事务

    public void onCreate(SQLiteDatabase db) {
        this.createTables(db); // do the normal onCreate() stuff

        db.setTransactionSuccessful();
        db.endTransaction();  // end transaction, so that you can do attach

        db.execSQL("ATTACH DATABASE '/databases/xx.db' AS xx;"); // do attach database
        db.execSQL("DETACH DATABASE xx;"); // detach database

        db.beginTransaction(); // begin transation again
    }

原因:

看了SQLiteOpenHelper.java的函数,发现onCreate()和onUpgrade()是在transaction的时候一起调用的。 enter image description here

关于android - 无法在事务中附加数据库,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28211644/

相关文章:

java - 安卓 SQLite : Is there a way to encrypt the entire data-base?

SQL查询三个表

android - 如何检查设备上的 sqlite 数据库表?

c# - SQLite net PCL - 简单选择

带有日期时间计算字符串的 Python SQLITE3 SELECT 查询不起作用

android - 在 Android 中备份 SQLite 数据库

java - 如何有条件地处理 Observable 链中的错误?

java - ActionBar 选项卡内容重叠

java - Android 应用程序 : Facebook LoginManager gives me "publish_actions" permission but app never goes through callbackManager callback( Facebook SDK 4. 18.0)?

android - 如何使用 Volley 解析 JSON