2010-10-31 49 views
60

Tôi đã đọc hướng dẫn về Lưu ý SQLite của Android tham chiếu tạo một lớp DB Adapter để tạo và truy cập một bảng DB. Khi giao dịch với một Cơ sở dữ liệu SQLite nhiều bảng, cách tốt nhất là tạo một Lớp bộ điều hợp khác cho mỗi bảng hoặc tạo một lớp DB Adapter duy nhất cho toàn bộ Ứng dụng Android?Nhiều Bảng SQLite DB Adapter (s) trong Android?

Ứng dụng của tôi sử dụng nhiều bảng và tôi đã hy vọng không cần phải có một lớp bộ điều hợp lớn duy nhất. vấn đề, tuy nhiên, là tôi có một lớp con lồng nhau của SQLiteOpenHelper mỗi Ví dụ NotePad trong mỗi bộ điều hợp. Khi bảng đầu tiên được truy cập, mọi thứ hoạt động tốt. Khi tôi cố gắng truy cập vào thứ hai (từ một hoạt động khác), ứng dụng của tôi gặp sự cố.

Lúc đầu, tôi cho rằng sự cố đã xảy ra do sự cố về phiên bản, nhưng cả hai bộ điều hợp giờ đây đều có cùng phiên bản cơ sở dữ liệu và nó vẫn bị lỗi.

Dưới đây là ví dụ về một trong các bộ điều hợp DB cho bảng. Các bộ điều hợp khác đều tuân theo cùng định dạng với các triển khai khác nhau.

public class InfoDBAdapter { 
    public static final String ROW_ID = "_id"; 
    public static final String NAME = "name"; 

    private static final String TAG = "InfoDbAdapter"; 
    private static final String DATABASE_NAME = "myappdb"; 
    private static final String DATABASE_TABLE = "usersinfo"; 
    private static final int DATABASE_VERSION = 1; 


    private static final String DATABASE_CREATE = "create table usersinfo (_id integer primary key autoincrement, " 
      + NAME 
      + " TEXT," + ");"; 

    private DatabaseHelper mDbHelper; 
    private SQLiteDatabase mDb; 

    private final Context mCtx; 

    private static class DatabaseHelper extends SQLiteOpenHelper { 

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

     @Override 
     public void onCreate(SQLiteDatabase db) { 

      db.execSQL(DATABASE_CREATE); 
     } 

     @Override 
     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 
      Log.w(TAG, "Upgrading database from version " + oldVersion + " to " //$NON-NLS-1$//$NON-NLS-2$ 
        + newVersion + ", which will destroy all old data"); //$NON-NLS-1$ 
      //db.execSQL("DROP TABLE IF EXISTS usersinfo"); //$NON-NLS-1$ 
      onCreate(db); 
     } 
    } 


    public InfoDBAdapter(Context ctx) { 
     this.mCtx = ctx; 
    } 


    public InfoDBAdapter open() throws SQLException { 
     this.mDbHelper = new DatabaseHelper(this.mCtx); 
     this.mDb = this.mDbHelper.getWritableDatabase(); 
     return this; 
    } 

    /** 
    * close return type: void 
    */ 
    public void close() { 
     this.mDbHelper.close(); 
    } 


    public long createUser(String name) { 
     ContentValues initialValues = new ContentValues(); 
     initialValues.put(NAME, name); 
     return this.mDb.insert(DATABASE_TABLE, null, initialValues); 
    } 


    public boolean deleteUser(long rowId) { 

     return this.mDb.delete(DATABASE_TABLE, ROW_ID + "=" + rowId, null) > 0; //$NON-NLS-1$ 
    } 


    public Cursor fetchAllUsers() { 

     return this.mDb.query(DATABASE_TABLE, new String[] { ROW_ID, 
       NAME}, null, null, null, null, null); 
    } 


    public Cursor fetchUser(long rowId) throws SQLException { 

     Cursor mCursor = 

     this.mDb.query(true, DATABASE_TABLE, new String[] { ROW_ID, NAME}, ROW_ID + "=" + rowId, null, //$NON-NLS-1$ 
       null, null, null, null); 
     if (mCursor != null) { 
      mCursor.moveToFirst(); 
     } 
     return mCursor; 

    } 


    public boolean updateUser(long rowId, String name) { 
     ContentValues args = new ContentValues(); 
     args.put(NAME, name); 
     return this.mDb 
       .update(DATABASE_TABLE, args, ROW_ID + "=" + rowId, null) > 0; //$NON-NLS-1$ 
    } 
} 

Khi bộ điều hợp đầu tiên, trong trường hợp này người dùng, được truy cập, mọi thứ hoạt động như mong đợi. Giả sử tôi có một bộ điều hợp khác cho thông tin bạn bè theo cùng cấu trúc như trên, khi nó được truy cập bởi một hoạt động khác, có vẻ như tôi rằng lớp con lồng nhau của SQLiteOpenHelper sẽ cố gắng tạo lại cơ sở dữ liệu. Rõ ràng có điều gì đó sai vì trong trường hợp đó, ứng dụng của tôi bị treo.

Vậy thực hành tiêu chuẩn trong Android là gì để tạo ra một bộ chuyển đổi đơn mút DB thay vì bộ điều hợp riêng lẻ trên mỗi bảng?

+2

+1: cũng đang cố gắng để có được tâm trí của tôi xung quanh bản thân mình. Chỉ có một hoặc hai bảng, không có vấn đề gì khi đặt tất cả trong cùng một lớp bộ điều hợp DB. Tuy nhiên, nếu DB phát triển với nhiều bảng, điều đó sẽ trở nên lộn xộn. Vì vậy, thực sự quan tâm nếu có ai có thể chỉ ra một số thực hành tốt nhất trong lĩnh vực này. – Nailuj

+0

Đây là câu hỏi tương tự nhưng với câu trả lời thực tế: http://stackoverflow.com/questions/3684678 – jcmcbeth

Trả lời

79

Đây là giải pháp cuối cùng tôi đã thực hiện. Đó là một loại mash-up từ thông tin thu được trong cuốn sách Commonsware, và một số công cụ trên web mà tôi muốn tôi đánh dấu trang vì tôi muốn cung cấp tín dụng:

Đối với mỗi loại dữ liệu mà tôi cần phải kéo từ db , Tôi tạo một lớp "bộ điều hợp" (không được phân lớp từ bất kỳ thứ gì). Các lớp bộ điều hợp này chứa tất cả các phương thức cần thiết để truy cập db cho phần thông tin đó. Ví dụ, nếu tôi có ba bảng trong db của tôi:

  1. Ô tô
  2. Thuyền
  3. Xe máy

tôi sẽ có ba adapter rằng sẽ trông tương tự như sau (tôi chỉ đưa vào một bản demo, nhưng ý tưởng là giống nhau cho mỗi bản trình diễn):

public class CarsDBAdapter { 
    public static final String ROW_ID = "_id"; 
    public static final String NAME = "name"; 
    public static final String MODEL = "model"; 
    public static final String YEAR = "year"; 

    private static final String DATABASE_TABLE = "cars"; 

    private DatabaseHelper mDbHelper; 
    private SQLiteDatabase mDb; 

    private final Context mCtx; 

    private static class DatabaseHelper extends SQLiteOpenHelper { 

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

     @Override 
     public void onCreate(SQLiteDatabase db) { 
     } 

     @Override 
     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 
     } 
    } 

    /** 
    * Constructor - takes the context to allow the database to be 
    * opened/created 
    * 
    * @param ctx 
    *   the Context within which to work 
    */ 
    public CarsDBAdapter(Context ctx) { 
     this.mCtx = ctx; 
    } 

    /** 
    * Open the cars database. If it cannot be opened, try to create a new 
    * instance of the database. If it cannot be created, throw an exception to 
    * signal the failure 
    * 
    * @return this (self reference, allowing this to be chained in an 
    *   initialization call) 
    * @throws SQLException 
    *    if the database could be neither opened or created 
    */ 
    public CarsDBAdapter open() throws SQLException { 
     this.mDbHelper = new DatabaseHelper(this.mCtx); 
     this.mDb = this.mDbHelper.getWritableDatabase(); 
     return this; 
    } 

    /** 
    * close return type: void 
    */ 
    public void close() { 
     this.mDbHelper.close(); 
    } 

    /** 
    * Create a new car. If the car is successfully created return the new 
    * rowId for that car, otherwise return a -1 to indicate failure. 
    * 
    * @param name 
    * @param model 
    * @param year 
    * @return rowId or -1 if failed 
    */ 
    public long createCar(String name, String model, String year){ 
     ContentValues initialValues = new ContentValues(); 
     initialValues.put(NAME, name); 
     initialValues.put(MODEL, model); 
     initialValues.put(YEAR, year); 
     return this.mDb.insert(DATABASE_TABLE, null, initialValues); 
    } 

    /** 
    * Delete the car with the given rowId 
    * 
    * @param rowId 
    * @return true if deleted, false otherwise 
    */ 
    public boolean deleteCar(long rowId) { 

     return this.mDb.delete(DATABASE_TABLE, ROW_ID + "=" + rowId, null) > 0; //$NON-NLS-1$ 
    } 

    /** 
    * Return a Cursor over the list of all cars in the database 
    * 
    * @return Cursor over all cars 
    */ 
    public Cursor getAllCars() { 

     return this.mDb.query(DATABASE_TABLE, new String[] { ROW_ID, 
       NAME, MODEL, YEAR }, null, null, null, null, null); 
    } 

    /** 
    * Return a Cursor positioned at the car that matches the given rowId 
    * @param rowId 
    * @return Cursor positioned to matching car, if found 
    * @throws SQLException if car could not be found/retrieved 
    */ 
    public Cursor getCar(long rowId) throws SQLException { 

     Cursor mCursor = 

     this.mDb.query(true, DATABASE_TABLE, new String[] { ROW_ID, NAME, 
       MODEL, YEAR}, ROW_ID + "=" + rowId, null, null, null, null, null); 
     if (mCursor != null) { 
      mCursor.moveToFirst(); 
     } 
     return mCursor; 
    } 

    /** 
    * Update the car. 
    * 
    * @param rowId 
    * @param name 
    * @param model 
    * @param year 
    * @return true if the note was successfully updated, false otherwise 
    */ 
    public boolean updateCar(long rowId, String name, String model, 
      String year){ 
     ContentValues args = new ContentValues(); 
     args.put(NAME, name); 
     args.put(MODEL, model); 
     args.put(YEAR, year); 

     return this.mDb.update(DATABASE_TABLE, args, ROW_ID + "=" + rowId, null) >0; 
    } 

} 

Vì vậy, nếu bạn tưởng tượng tôi có một trong các lớp học này "bộ điều hợp" cho mỗi bảng.

Khi màn hình ứng dụng splash của tôi bắt đầu, tôi sử dụng kỹ thuật này được trình bày Android For Beginners: Creating multiple SQLite Tables for Android

Vì vậy DBAdapter chính của tôi (đó là trách nhiệm tạo tất cả các bảng của tôi trong một db đơn) trông như thế này:

public class DBAdapter { 

    public static final String DATABASE_NAME = "stuffIOwn"; //$NON-NLS-1$ 

    public static final int DATABASE_VERSION = 1; 

    private static final String CREATE_TABLE_CARS = 
     "create table cars (_id integer primary key autoincrement, " //$NON-NLS-1$ 
    + CarsDBAdapter.NAME+ " TEXT," //$NON-NLS-1$ 
    + CarsDBAdapter.MODEL+ " TEXT," //$NON-NLS-1$ 
    + CarsDBAdapter.YEAR+ " TEXT" + ");"; //$NON-NLS-1$ //$NON-NLS-2$ 

    private static final String CREATE_TABLE_BOATS = "create table boats (_id integer primary key autoincrement, " //$NON-NLS-1$ 
    +BoatsDBAdapter.NAME+" TEXT," //$NON-NLS-1$ 
    +BoatsDBAdapter.MODEL+" TEXT," //$NON-NLS-1$ 
    +BoatsDBAdapter.YEAR+" TEXT"+ ");"; //$NON-NLS-1$ //$NON-NLS-2$ 

     private static final String CREATE_TABLE_CYCLES = "create table cycles (_id integer primary key autoincrement, " //$NON-NLS-1$ 
    +CyclesDBAdapter.NAME+" TEXT," //$NON-NLS-1$ 
    +CyclesDBAdapter.MODEL+" TEXT," //$NON-NLS-1$ 
    +CyclesDBAdapter.YEAR+" TEXT"+ ");"; //$NON-NLS-1$ //$NON-NLS-2$ 


    private final Context context; 
    private DatabaseHelper DBHelper; 
    private SQLiteDatabase db; 

    /** 
    * Constructor 
    * @param ctx 
    */ 
    public DBAdapter(Context ctx) 
    { 
     this.context = ctx; 
     this.DBHelper = new DatabaseHelper(this.context); 
    } 

    private static class DatabaseHelper extends SQLiteOpenHelper 
    { 
     DatabaseHelper(Context context) 
     { 
      super(context, DATABASE_NAME, null, DATABASE_VERSION); 
     } 

     @Override 
     public void onCreate(SQLiteDatabase db) 
     { 
      db.execSQL(CREATE_TABLE_CARS); 
      db.execSQL(CREATE_TABLE_BOATS); 
      db.execSQL(CREATE_TABLE_CYCLES);   
     } 

     @Override 
     public void onUpgrade(SQLiteDatabase db, int oldVersion, 
     int newVersion) 
     {    
      // Adding any table mods to this guy here 
     } 
    } 

    /** 
    * open the db 
    * @return this 
    * @throws SQLException 
    * return type: DBAdapter 
    */ 
    public DBAdapter open() throws SQLException 
    { 
     this.db = this.DBHelper.getWritableDatabase(); 
     return this; 
    } 

    /** 
    * close the db 
    * return type: void 
    */ 
    public void close() 
    { 
     this.DBHelper.close(); 
    } 
} 

Lớp DBAdapter chỉ được gọi khi ứng dụng lần đầu tiên bắt đầu và trách nhiệm duy nhất của nó là tạo/nâng cấp các bảng. Tất cả các quyền truy cập khác vào dữ liệu được thực hiện thông qua lớp "bộ điều hợp" riêng lẻ. Tôi đã tìm thấy rằng điều này hoạt động hoàn hảo và không tạo ra các vấn đề versioning mà tôi đã đề cập trước đó.

Hy vọng điều này sẽ hữu ích.

+3

nếu bạn cần tham gia các bảng thì sao? bạn làm được cái này không? –

+1

Tôi đoán bạn có quyền truy cập cơ sở dữ liệu toàn cục từ mỗi bộ điều hợp để bảng tham gia không phải là vấn đề. Tôi vẫn tự hỏi nếu có nhiều lớp con của SqliteOpenHelper trong mỗi adapter là một cách tiếp cận tốt ...? – Mirko

+19

Đây là một giải pháp tuyệt vời, nhưng nếu tôi có thể đưa ra một đề xuất để cải thiện thêm: Tôi sẽ có các bộ điều hợp bảng riêng lẻ 'mở rộng' lớp DBAdapter, do đó buộc cơ sở dữ liệu kiểm tra xem nó có cần nâng cấp khi bạn 'mở() không 'bất kỳ bộ điều hợp bảng nào. Nó cũng loại bỏ sự cần thiết phải khởi tạo DBAdapter khi ứng dụng đầu tiên bắt đầu. – Matt

9

Đã có cùng một vấn đề, đã thử nhiều giải pháp, finnaly tôi đã tạo một phương thức trừu tượng có cấu trúc cơ sở dữ liệu và đã mở rộng lớp cho các lớp bảng.

Đây là lớp cơ sở dữ liệu xây dựng của tôi và là Tóm tắt:

public abstract class dbAdapter { 
    public static String DATABASE_NAME = ""; 
    public static final int DATABASE_VERSION = 1; 
    public static final String DATABASE_TABLE1 = "ContactName"; 
    public static final String DATABASE_TABLE2 = "PhoneNumber"; 

    public static DbHelper ourHelper; 
    public static Context ourContext; 
    public static SQLiteDatabase ourDatabase; 

    boolean ourConstructorBool = false; 
    boolean ourDB = false; 

    public static final String ContactNameTable = "CREATE TABLE "+DATABASE_TABLE1+" (" + 
     ContactNameAdapter.KEY_ROWID+" INTEGER PRIMARY KEY AUTOINCREMENT, " + 
     ContactNameAdapter.KEY_NAME+" TEXT, " + 
     ContactNameAdapter.KEY_BIRTH_DATE+" TEXT);"; 

    public static final String PhoneNumberTable = "CREATE TABLE "+DATABASE_TABLE2+" (" + 
     PhoneNumberAdapter.KEY_NUMBER+" TEXT , " + 
     PhoneNumberAdapter.KEY_DESCRIPTION+" TEXT, " + 
     PhoneNumberAdapter.KEY_CONTACTID+" TEXT, " + 
     "FOREIGN KEY(" + PhoneNumberAdapter.KEY_CONTACTID +") REFERENCES " + 
     (ContactNameAdapter.DATABASE_TABLE)+"("+ContactNameAdapter.KEY_ROWID+") ON DELETE CASCADE"+ 
    ");"; 

    static class DbHelper extends SQLiteOpenHelper{ 
     public DbHelper(Context context) { 
      super(context, DATABASE_NAME, null, DATABASE_VERSION); 
     } 
     @Override 
     public void onCreate(SQLiteDatabase db) { 
      db.execSQL(ContactNameTable); 
      db.execSQL(PhoneNumberTable); 
     } 

     @Override 
     public void onUpgrade(SQLiteDatabase db, int arg1, int arg2) { 
      db.execSQL("DROP TABLE IF EXISTS " + ContactNameAdapter.DATABASE_TABLE); 
      db.execSQL("DROP TABLE IF EXISTS " + PhoneNumberAdapter.DATABASE_TABLE); 
      onCreate(db); 
     } 
    } 

    public dbAdapter(Activity a){ 
     if(!ourConstructorBool == true){ 
      ourContext = a; 
      DATABASE_NAME = a.getString(Asaf.com.contactsEX.R.string.DB_NAME); 
      ourConstructorBool = true; 
     } 
    } 

    public dbAdapter open() throws SQLException{ 
     if(!ourDB == true){ 
      ourHelper = new DbHelper(ourContext); 
      ourDB = true; 
     } 
     ourDatabase = ourHelper.getWritableDatabase(); 
     return this; 
    } 

    public void close(){ 
     if(ourDatabase.isOpen()) 
      ourHelper.close(); 
    } 
} 

Và đây là một trong các lớp học bàn của tôi, phần còn lại của các lớp học được thực hiện như nhau, chỉ cần thêm càng nhiều càng tốt thích:

public class PhoneNumberAdapter extends dbAdapter{ 

    public static final String KEY_NUMBER = "PhoneNumber"; 
    public static final String KEY_DESCRIPTION = "Description"; 
    public static final String KEY_CONTACTID = "ContactName_id"; 

    public static final String DATABASE_TABLE = "PhoneNumber"; 

    public PhoneNumberAdapter(Activity a){ 
     super(a); 
    } 

    public long createEntry(String number, String description,long id){ 
     // TODO Auto-generated method stub 
     ContentValues cv = new ContentValues(); 
     cv.put(KEY_NUMBER, number); 
     cv.put(KEY_DESCRIPTION, description); 
     cv.put(KEY_CONTACTID, id); 
     return ourDatabase.insert(DATABASE_TABLE, null,cv); 
    } 
} 

Hy vọng tôi đã giúp.

+2

Tại sao bạn chuyển thể hiện Hoạt động cho các nhà thầu của mình? Không nên là một bối cảnh? – Mirko

+0

Làm cách nào để 'phương thức 'ConstructorBool' của chúng ta có thể là' true' nếu nó không được khai báo là 'tĩnh'? – Ohmnibus

Các vấn đề liên quan