【问题标题】:How to access an SQLite database to query in Android Studio如何访问 SQLite 数据库以在 Android Studio 中进行查询
【发布时间】:2022-06-22 06:41:11
【问题描述】:

所以我在网上找到了一个视频https://www.youtube.com/watch?v=sq45s9gggsw,它会引导您完成简单显示 SQLite 数据库的整个过程,一旦我把它弄下来,我就可以继续前进,但我似乎无法让它工作。每次说找不到我正在查询的表时,程序都会崩溃,但似乎所有的 try catch 语句都被忽略了,因为我从资产文件夹中删除了数据库,但它仍然给我相同的响应.我已经研究了好几个小时了,看不出她发生了什么是我的代码

主要活动

package com.example.testdatabase;

import androidx.appcompat.app.AppCompatActivity;
import android.widget.TextView;
import android.os.Bundle;


public class MainActivity extends AppCompatActivity {

    TextView lst;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        lst = (TextView) findViewById(R.id.list);
        MyDBHandler dbHandler = new MyDBHandler(this,null,null,2);
        lst.setText(dbHandler.loadHandler());
    }
}

数据库处理程序

package com.example.testdatabase;


import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;


import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;

public class MyDBHandler extends SQLiteOpenHelper{
    public static final int DB_VERSION = 2;
    private static final String DB_NAME = "student.db";
    private static String DB_PATH = "/data/user/0/com.example.testdatabase/databases/";
    SQLiteDatabase myDataBase;
    private final Context mContext;


    public MyDBHandler(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, DB_NAME, factory, DB_VERSION);
        this.mContext = context;
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int i, int i1) {
    }

    private boolean checkDatabase(){
        try {
            final String mPath = DB_PATH + DB_NAME;
            final File file = new File(mPath);
            if (file.exists())
                return true;
                else
                    return false;
            }   catch (SQLiteException e){
                e.printStackTrace();
                return false;
        }
    }
    private void copyDatabase() throws IOException{
        try{
            InputStream mInputStream = mContext.getAssets().open(DB_NAME);
            String outFileName = DB_NAME;
            OutputStream mOutputStream = new FileOutputStream(outFileName);

            byte[] buffer = new byte[1024];
            int length;
            while((length = mInputStream.read(buffer)) > 0){
                mOutputStream.write(buffer,0,length);
            }
            mOutputStream.flush();
            mOutputStream.close();
        }
        catch (Exception e){
            e.printStackTrace();
        }
    }
    public void createDatabase() throws IOException{
        boolean mDatabaseExist = checkDatabase();
        if (!mDatabaseExist){
            this.getReadableDatabase();
            this.close();
            try{
                copyDatabase();
            }
            catch(IOException mIOException){
                mIOException.printStackTrace();
                throw new Error("Error copying Database");
            } finally {
                this.close();
            }
        }
    }
    @Override
    public synchronized  void close(){
        if (myDataBase != null)
            myDataBase.close();
        SQLiteDatabase.releaseMemory();
        super.close();
    }
    public String loadHandler(){
        try{
            createDatabase();
        }
        catch (IOException e){
            e.printStackTrace();
        }
        String result = "";
        SQLiteDatabase db = this.getReadableDatabase();

        Cursor c = db.rawQuery("select * from Student", null);

        while(c.moveToNext()){
            int result_id = c.getInt(0);
            String result_name = c.getString(1);
            result += String.valueOf(result_id) + " " + result_name + System.getProperty("line.separator");
        }
        c.close();
        db.close();

        return result;
    }
}

视频还让你创建了一个我看不到的学生班级

package com.example.testdatabase;

public class Student {
    private int _id;
    private String _studentName;

    public Student(int id, String studentName){
        this._id = id;
        this._studentName = studentName;
    }
    public void setID(int id){
        this._id = id;
    }
    public int getID(){
        return this._id;
    }
    public void setStudentName(String studentName){
        this._studentName = studentName;
    }
    public String getStudentName(){
        return this._studentName;
    }
}

我在调试中不断得到 E/SQLiteLog 的结果:(1) 没有这样的表:学生 在调用 SQL 查询的 loadHandler 方法中出现错误。似乎它正在跳过所有的 try/catch 语句来检查数据库是否存在。

任何帮助将不胜感激

【问题讨论】:

    标签: android sqlite android-studio android-sqlite


    【解决方案1】:

    这是您复制的代码无法应对更高版本的 Android 的问题。

    当数据库不存在时(第一次运行)。然后它使用this.getReadableDatabase(); 来解决 databases 文件夹不存在的问题。没有该文件夹,复制将失败。

    • this.getReadableDatabase(); 确实获得了一个空数据库并在此过程中创建了数据库文件夹。

    但在 Android 9+ 上,默认的日志记录模式是 WAL(日志模式是以前的默认值)。发生的情况是this.getReadableDatabase(); 创建了一个数据库,并为新创建的数据库创建了 WAL 文件 (_wal)。

    然后新创建的数据库会被副本覆盖,但 WAL 文件仍然存在。

    当尝试打开数据库时,它发现 WAL 文件不属于复制的数据库。异常被捕获并打开创建一个数据库,以便它可以完成它的任务。因此数据库是空的。

    当您只需获取 DB_PATH 的父级并执行 mkDirs() 时,使用 getReadableDatabase(); 是错误的。

    所以我建议使用:-

    private boolean checkDatabase(){
        boolean rv = true;
        final String mPath = DB_PATH + DB_NAME;
        File file = new File(mPath);
        if (!file.exists()) {
            rv = false;
            if (!(file.getParentFile()).exists()) {
                (file.getParentFile()).mkdirs();
            }
        }
        return rv;
    }
    
    • 因此,如果 DB_PATH 不存在,这将创建目录。

    结合:-

    public void createDatabase() throws IOException{
        boolean mDatabaseExist = checkDatabase();
        if (!mDatabaseExist){
            //this.getReadableDatabase(); /*<<<<<<<<<< COMMENTED OUT*/ 
            //this.close();  /*<<<<<<<<<< COMMENTED OUT*/ 
            try{
                copyDatabase();
            }
            catch(IOException mIOException){
                mIOException.printStackTrace();
                throw new Error("Error copying Database");
            } finally {
                this.close();
            }
        }
    }
    
    • 所以这不会使用getReadable 数据库,而是会创建数据库文件夹。

    工作示例

    这是一个基于您的代码但经过大量修改的工作示例。

    MyDBHandler

    public class MyDBHandler extends SQLiteOpenHelper {
       public static final int DB_VERSION = 2;
       private static final String DB_NAME = "student.db";
       private static String DB_FULL_PATH;
       private static MyDBHandler INSTANCE;
       private SQLiteDatabase myDataBase;
    
    
       private MyDBHandler(Context context) {
          super(context, DB_NAME, null, DB_VERSION);
          boolean databaseCopied;
          DB_FULL_PATH = context.getDatabasePath(DB_NAME).getPath();
          try {
            databaseCopied = createDatabase(context);
          } catch (IOException e) {
             e.printStackTrace();
             throw new RuntimeException("Failed to copy Database from asset File");
          }
          myDataBase = this.getWritableDatabase();
          if (databaseCopied) {
             logDbSchema();
          }
       }
    
       public static MyDBHandler getInstance(Context context) {
          if (INSTANCE == null) {
             INSTANCE = new MyDBHandler(context);
          }
          return INSTANCE;
       }
    
       @Override
       public void onCreate(SQLiteDatabase sqLiteDatabase) {
       }
    
       @Override
       public void onUpgrade(SQLiteDatabase db, int i, int i1) {
       }
    
       /* CHANGED */
       private boolean checkDatabase(){
          boolean rv = true;
          File dbfile = new File(DB_FULL_PATH);
          if (!dbfile.exists()) {
             rv = false;
             if (!dbfile.getParentFile().exists()) {
                dbfile.getParentFile().mkdirs();
             }
          }
          return rv;
       }
       private void copyDatabase(Context context) throws IOException {
          InputStream assetFile;
          OutputStream dbFile;
          try{
             assetFile = context.getAssets().open(DB_NAME);
             dbFile = new FileOutputStream(new File(DB_FULL_PATH));
    
             byte[] buffer = new byte[1024 * 8];
             int length;
             while((length = assetFile.read(buffer)) > 0){
                dbFile.write(buffer,0,length);
             }
             dbFile.flush();
             dbFile.close();
             assetFile.close();
          }
          catch (Exception e){
             e.printStackTrace();
          }
       }
    
       /* Amended to return boolean that indicates whether ro not ass was copied(attempted) */
       /* true then database did not exist and copy was attempted */
       public boolean createDatabase(Context context) throws IOException{
          boolean rv = !checkDatabase();
          if (rv){
             try{
                copyDatabase(context);
             }
             catch(IOException mIOException){
                mIOException.printStackTrace();
                throw new Error("Error copying Database");
             } finally {
                this.close();
             }
          }
          return rv;
       }
    
       /* Should really only close the database as a last resort
          opening again and again it is resource intensive
        */
       @Override
       public synchronized  void close(){
          if (myDataBase != null)
             myDataBase.close();
          SQLiteDatabase.releaseMemory();
          super.close();
       }
    
       /* Added for debugging */
       private void logDbSchema() {
          Cursor c = myDataBase.query("sqlite_master",null,null,null,null,null,null);
          DatabaseUtils.dumpCursor(c);
          c.close();
       }
    }
    

    注意事项

    • 已使用单例方法,因此构造函数已私有。相反,您可以通过 getInstance 方法实例化一个 MyDBHandler,该方法将检索现有实例,或者如果现有实例为空,则获取一个新实例。
    • 在获取实例时进行数据库是否存在的测试。
    • loadHandler 已被取消。
    • 数据库路径不是硬编码的,而是通过应用程序的上下文确定的(未来证明应用程序应该更改路径指定)
    • checkDatabase 方法返回一个布尔值,如果需要创建数据库则返回 true,否则返回 false。然后将结果用于提取和转储数据库架构(用于调试)。
    • 强烈建议您不要过度使用 close 方法(即使曾经使用过)。

    演示MainActivity:-

    public class MainActivity extends AppCompatActivity {
    
        MyDBHandler dbHandler;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            dbHandler = MyDBHandler.getInstance(this);
            Cursor testdb = dbHandler.getWritableDatabase().query("sqlite_master",null,null,null,null,null,null);
            DatabaseUtils.dumpCursor(testdb);
            testdb.close();
        }
    }
    

    和资产数据库:-

    回复评论

    感谢您的回复我仍然遇到同样的问题,我想知道您所说的是否是这种情况,那么 public String loadHandler(){ try{ createDatabase(); } catch (IOException e){ e.printStackTrace(); } String result = ""; SQLiteDatabase db = this.getReadableDatabase(); 行可能仍然是问题所在。我会用什么来代替它?

    上面的工作示例取消了 loadHandler。但是,您会注意到 getInstance 方法实际上确实使用了 getWritableDatabase(getReadableDatabase 实际上获得了可写数据库,除非存在无法写入数据库的问题)。

    但是,我怀疑您持续存在的问题可能是您不知道一旦数据库存在,无论它是否为空表,那么如果存在并且资产中的副本将不替换数据库。你必须删除数据库,最简单的方法是卸载应用程序并重新运行应用程序。

    【讨论】:

    • 感谢您的回复我仍然遇到同样的问题,我想知道您所说的是否是这种情况,然后行 ``` public String loadHandler(){ try{ createDatabase (); } catch (IOException e){ e.printStackTrace(); } 字符串结果 = ""; SQLiteDatabase db = this.getReadableDatabase(); ``` 可能仍然是这个问题。我会用什么代替它?
    • @Stephen 添加了一个工作示例,它有一些额外的改进。但是,我怀疑您的问题是基于这样一个事实,即如果您只是重新运行,空数据库将不会被替换。您必须删除要重做副本的数据库。最简单的方法是卸载App,然后重新运行App。
    • 感谢您的努力和帮助。这确实允许数据库显示一个列,尽管之前的 loadHandler 允许我在代码中运行 SQL 查询,这是朝着这个项目的目标迈出的一步。我在这里有点不知所措,因为在不使用 rawQuery 的情况下实现标准 SQL 查询并使用代码使其工作。我觉得我可能无法胜任手头的任务,但我下定决心要完成这项工作。再次感谢您提供的帮助
    【解决方案2】:

    所以我最终采用了另一种效果很好的方法。这就是我所做的:

    
    import android.content.Context;
    import android.database.Cursor;
    import android.database.sqlite.SQLiteDatabase;
    import android.database.sqlite.SQLiteException;
    
    import com.readystatesoftware.sqliteasset.SQLiteAssetHelper;
    
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    
    
    public class DBHelper extends SQLiteAssetHelper {
    
        public static final String DBNAME = "example.db"; //<<<< must be same as file name
        public static final int DBVERSION = 1;
        SQLiteDatabase myDataBase;
        private final Context mContext;
    
        public DBHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
            super(context, DBNAME,null, DBVERSION);
            this.mContext = context;
        }
    
    
        @Override
        public void onUpgrade(SQLiteDatabase db, int i, int i1) {}
    
        private boolean checkDatabase(){
            try {
                final String mPath = DBNAME;
                final File file = new File(mPath);
                if (file.exists())
                    return true;
                else
                    return false;
            }   catch (SQLiteException e){
                e.printStackTrace();
                return false;
            }
        }
        private void copyDatabase() throws IOException {
            try{ InputStream mInputStream = mContext.getAssets().open(DBNAME);
                String outFileName = DBNAME;
                OutputStream mOutputStream = new FileOutputStream(outFileName);
    
                byte[] buffer = new byte[1024];
                int length;
                while((length = mInputStream.read(buffer)) > 0){
                    mOutputStream.write(buffer,0,length);
                }
                mOutputStream.flush();
                mOutputStream.close();
            }
            catch (Exception e){
                e.printStackTrace();
            }
        }
        public void createDatabase() throws IOException{
            boolean mDatabaseExist = checkDatabase();
            if (!mDatabaseExist){
    
                try{
                    copyDatabase();
                }
                catch(IOException mIOException){
                    mIOException.printStackTrace();
                    throw new Error("Error copying Database");
                } finally {
                    this.close();
                }
            }
        }
        @Override
        public synchronized  void close(){
            if (myDataBase != null)
                myDataBase.close();
            SQLiteDatabase.releaseMemory();
            super.close();
        }
        public String loadHandler(String sqlinput){
            try{
                createDatabase();
            }
            catch (IOException e){
                e.printStackTrace();
            }
            String result = "";
            SQLiteDatabase db = this.getReadableDatabase();
    
            Cursor c = db.rawQuery("select * from Appliances where appliance_id IN (" + sqlinput + ")", null);
    
            while(c.moveToNext()){
                int result_id = c.getInt(0);
                String result_name = c.getString(1);
                Number result_va = c.getDouble(2);
                Number result_w = c.getDouble(3);
    
                result += String.valueOf(result_id) + " " + result_name + " " + result_va + " " + result_w + System.getProperty("line.separator");
            }
            c.close();
            db.close();
    
            return result;
        }
    }
    

    独特之处在于我正在使用 导入 com.readystatesoftware.sqliteasset.SQLiteAssetHelper; 这让我的程序可以完全按照我的期望去做,让我可以从其他类的代码中查询代码。

    【讨论】:

      猜你喜欢
      • 2015-09-07
      • 1970-01-01
      • 1970-01-01
      • 2023-03-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多