无法在事务中附加数据库

时间:2015-01-29 09:56:10

标签: android sqlite android-sqlite

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

问题是当我尝试在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();

}

}

3 个答案:

答案 0 :(得分:3)

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

  1. 请勿在{{1​​}}和onCreate中“附加数据库”。由于“无法在事务中附加数据库”,它将失败。 但是,您可以在初始化数据库时附加数据库,然后调用onUpgrade以确保将创建和升级数据库。 然后你可以运行attach命令。

    getReadableDatabase()
  2. 但如果你想在onUpgrade()之前做依恋,请尝试以下方式:

  3. onCreate()中的endTransaction,然后执行attach,然后是beginTransaction

    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
    

    <强>原因:

    阅读SQLiteOpenHelper.java的功能,我发现在事务期间一起调用onCreate()和onUpgrade()。 enter image description here

答案 1 :(得分:2)

要添加@ gramnation的答案,

如果您使用的是jdbc,则必须运行

BEGIN
{
    Function Set-ScriptVars()
    {
        Add-Type -AssemblyName System.Windows.Forms
    }

    Function Select-File($FileType)
    {
        ## Select file via selection dialog

        do {
            if($FileType -eq "xlsx")
            {
                Write-Host "`nPlease select the Excel file to import in the dialog"
            }
            elseif($FileType -eq "txt")
            {
                Write-Host "`nPlease select the Prescan or Postscan text file to import in the dialog"
            }

            Start-Sleep -Seconds 1
            $FileBrowser = New-Object System.Windows.Forms.OpenFileDialog -Property @{InitialDirectory = [Environment]::GetFolderPath('Desktop')}

            [void]$FileBrowser.ShowDialog()

            Write-Host "`nFile selected: " -NoNewline  
            Write-Host $FileBrowser.FileNames -ForegroundColor Yellow 

            $FileName = $FileBrowser.FileName
            if ($FileName.EndsWith(".$FileType"))
            {
                $selectionValid = $True
            }
            else
            {
                Write-Host "The file selected is not a .$FileType file."
                Write-Host "Restarting file selection loop."
                $selectionValid = $False
            }
        } until ($selectionValid -eq $True)

        if($FileType -eq "txt")
        {
            $Script:TextFile = $FileName
            $Script:TextParentPath = (Get-Item $FileName).Directory.FullName
        }
        elseif($FileType -eq "xlsx")
        {
            $Script:ExcelFile = $FileName
            $Script:ExcelParentPath = (Get-Item $FileName).Directory.FullName
        }
    }

    Function Open-Excel($Sheet)
    {
        $ExcelPath = $Script:ExcelFile
        $Script:Excel = New-Object -ComObject Excel.Application
        $Script:Excel.Visible = $False
        $Script:Excel.UserControl = $False
        $Script:Excel.Interactive = $True
        $Script:Excel.DisplayAlerts = $False

        $Script:ExcelWorkBook = $Script:Excel.Workbooks.Open($ExcelPath)
        $Script:ExcelWorkSheet = $Script:Excel.WorkSheets.item($Sheet)
        $Script:ExcelWorkSheet.Activate()
    }

    Function Get-TextContent()
    {
        $Script:TextContent = Get-Content $Script:TextFile 
    }

function Release-Ref ($ref) { 
    ([System.Runtime.InteropServices.Marshal]::ReleaseComObject( 
    [System.__ComObject]$ref) -gt 0) | Out-Null
    [System.GC]::Collect() 
    [System.GC]::WaitForPendingFinalizers() 
} 
    Function Copy-TextData()
    {       
        # create a CSV from the scan data

        $Script:TextContent = Get-Content $Script:TextFile

        $data = @()
        $row = New-Object PSObject

        foreach($line in $Script:TextContent)
        {
            if($line -eq "CSV was validated without errors." -or $line -eq "")
            {
                Out-Null
            }
            else
            {
                $i = 0
                $values = $line -split ","
                $result = [PSCustomObject]@{Server=$values[0];`
                                            Role=$values[1];`
                                            Object=$values[2];`
                                            Type=$values[3];`
                                            Path=$values[4]
                                           }

                [Array]$results = $results + $result
            }
        }
        $csvName = "scanData_" + "$(@(000..999) | Get-Random)"
        $results | Export-CSV -Path "$ENV:USERPROFILE\Desktop\$csvName.csv" -NoTypeInformation
        $csvPath = $(Get-Item $ENV:USERPROFILE\Desktop\$csvName.csv).VersionInfo.FileName

        # Remove header generated by hashtable
        # and skip the next two lines

        $tempContent = Get-Content $csvPath
        $replacementContent = $tempContent | Select -Skip 3
        Set-Content $csvPath -Value $replacementContent

        # create temporary workbook and save as xlsx
        $tempXL = New-Object -ComObject Excel.Application
        $tempXL.Visible = $False
        $tempXL.UserControl = $False
        $tempXL.Interactive = $True
        $tempXL.DisplayAlerts = $False

        $tempWB = $tempXL.WorkBooks.Open("$csvPath")
        $tempWS = $tempWB.WorkSheets

        $convertedName = $csvPath.Replace(".csv",".xlsx")
        $tempWB.SaveAs($convertedName,1)
        $tempWB.Saved = $True

        $tempRange = $tempWB.Worksheets.Item(1).UsedRange
        $tempRange.Copy()

        if($Script:logSelection -eq "Prescan")
        {
            $permRange = $Script:ExcelWorkBook.Worksheets.Item(2)
        }
        else
        {
            $permRange = $Script:ExcelWorkBook.Worksheets.Item(3)
        }

        $subRange = $permRange.Range("A2","E2")
        $permRange.Paste($subRange)
        $permRange.Columns.AutoFit()

        $Script:ExcelWorkBook.Save()
        $Script:ExcelWorkBook.Saved = $True
        $Script:Excel.Quit()

        $tempWB.Save()
        $tempWB.Saved = $True
        $tempXL.Quit()

        Release-Ref($Script:ExcelWorkSheet)
        Release-Ref($tempWS)

        Release-Ref($Script:ExcelWorkBook)
        Release-Ref($tempWB)

        Release-Ref($Script:Excel)
        Release-Ref($tempXL)

        Remove-Item $csvPath -Force
        Get-Item $convertedName | Remove-Item -Force
    }

    Function Prompt-ReRun
    {
        do
        {
            $openChoice = Read-Host "`nRun again? (y/n)"
            $openChoice = $openChoice.ToLower()
        } until($openChoice -eq "y" -or $openChoice -eq "n")

        if($openChoice -ne "y" -and $openChoice -ne "n")
        {
            Write-Host "Invalid entry"
        }
        elseif($openChoice -eq "y")
        {
            Run-Selection
        }
        else
        {
            Out-Null
        }
    }

    Function Run-Selection
    {
        Select-File -FileType "xlsx"
        Select-File -FileType "txt"
        if($Script:TextFile -match "Prescan")
        {
            Open-Excel -Sheet "Prescan"
            $Script:logSelection = "Prescan"
        }
        elseif($Script:TextFile -match "Postscan")
        {
            Open-Excel -Sheet "Postscan"
            $Script:logSelection = "Postscan"
        }

        Get-TextContent
        Copy-TextData
        Prompt-ReRun
    }
}

PROCESS
{
    Set-ScriptVars
    Run-Selection
}

END
{

}

因为当你将auto commit设置为false时,这将触发新事务的开始,并且你将遇到与线程相关的复杂问题。

请注意,当您使用SQLite 3运行connection.commit()时,实际上并未向磁盘提交任何内容 - 请参阅https://www.sqlite.org/lockingv3.html, 第7.0节(SQL级别的事务控制)

答案 2 :(得分:0)

您的库可能包含执行BEGIN TRANSACTION的错误(以提高性能和/或锁定数据库)但不执行END TRANSACTION以释放锁定。

示例:

> sqlite3 database.sqlite
SQLite version 3.6.20
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> ATTACH DATABASE 'database.sqlite.old' AS old_db;    -- works
sqlite> DETACH DATABASE old_db;
sqlite> BEGIN TRANSACTION;                                  -- lock db
sqlite> ATTACH DATABASE 'database.sqlite.old' AS old_db;    -- fails
Error: SQL logic error or missing database
sqlite> END TRANSACTION;                                    -- release lock
sqlite> ATTACH DATABASE 'database.sqlite.old' AS old_db;    -- works
sqlite> DETACH DATABASE old_db;