2012-02-26 28 views
5

tôi sử dụng đoạn mã sau để chèn dòng mới một cách hiệu quả trong một DB:Android - SQLiteStatement (nhiều insert) thực hiện không thành công với API Level 14

@Override 
public void insertImpacts(HolderExpense expense, int[] shares, int[] amounts, HolderUser[] users) { 

    try { 

     mDb.beginTransaction(); // the insertion of the impacts of one expense are considered to be ONE block 

     String sql = "INSERT INTO "+DATABASE_TABLE_IMPACTS+" "+ 
     "("+IMPACT_USERROWID+", "+IMPACT_EXPENSEROWID+", "+IMPACT_TTROWID+", "+IMPACT_NUMBEROFPARTS+", "+IMPACT_FIXEDAMOUNT+") VALUES (?, ?, ?, ?, ?)"; 

     SQLiteStatement stmt = mDb.compileStatement(sql); 

     stmt.bindLong(2, expense.rowId); 
     stmt.bindLong(3, expense.tt.rowId); 

     int i = 0; 

     while (i < shares.length) { 

      if (users[i] != null) { 

       Log.v(TAG, "user " +i+": users[i].rowId:"+users[i].rowId+" expense.rowId:"+expense.rowId+" expense.tt.rowId:"+expense.tt.rowId+" shares[i]:"+shares[i]+" amounts[i]:"+amounts[i]+" "); 

       stmt.bindLong(1, users[i].rowId); 
       stmt.bindString(4, shares[i]+""); 
       stmt.bindString(5, amounts[i]+""); 

       stmt.execute(); 

      } 
      i++; 
     } 

     stmt.close(); 

     mDb.setTransactionSuccessful(); 

    } 
    catch (Exception e) { e.printStackTrace(); throw new RuntimeException("insertImpacts() failed"); } 
    finally { mDb.endTransaction(); } 

} 

Nó hoạt động cho đến khi 4.x android nơi tôi nhận được lỗi rằng:

02-26 14:27:46.179: W/System.err(937): android.database.sqlite.SQLiteConstraintException: error code 19: constraint failed 

02-26 14:27:46.179: W/System.err(937): at android.database.sqlite.SQLiteStatement.native_execute(Native Method) 

02-26 14:27:46.219: W/System.err(937): at android.database.sqlite.SQLiteStatement.executeUpdateDelete(SQLiteStatement.java:92) 

02-26 14:27:46.219: W/System.err(937): at android.database.sqlite.SQLiteStatement.execute(SQLiteStatement.java:70) 

Dường như nó gặp sự cố tại stmt.execute() khi chèn dòng thứ hai vào bảng.

Bất kỳ đầu mối nào?

- EDITION -

Giản đồ của bảng như sau:

private static final String DATABASE_CREATE_TABLE_IMPACTS = 
     "create table " + DATABASE_TABLE_IMPACTS + " (" 
     + IMPACT_ROWID + " integer primary key autoincrement, " 
     + IMPACT_USERROWID + " integer not null, " 
     + IMPACT_EXPENSEROWID + " integer not null, " 
     + IMPACT_TTROWID + " integer not null, " 
     + IMPACT_NUMBEROFPARTS + " integer not null, " 
     + IMPACT_FIXEDAMOUNT + " integer not null, " 
     + "constraint i_cstr1 unique ("+IMPACT_USERROWID+", "+IMPACT_EXPENSEROWID+")); "; 

Mã này hoạt động giống như một nét duyên dáng trên Android 2.2 (nhưng thất bại trên Android 4.0).

In của hai dòng đầu tiên tôi chèn (nó bị treo khi cố gắng chèn giây):

02-26 14:27:46.069: E/DatabaseAdapter.java(937): user 0: users[i].rowId:7 expense.rowId:2 expense.tt.rowId:2 shares[i]:1 amounts[i]:-1 
02-26 14:27:46.069: E/DatabaseAdapter.java(937): user 1: users[i].rowId:5 expense.rowId:2 expense.tt.rowId:2 shares[i]:1 amounts[i]:-1 
+2

Vâng, có vẻ như bạn đang vi phạm một hạn chế. Lược đồ bảng của bạn là gì? – Mat

+0

Tôi đồng ý với @Mat, bạn nên đăng câu lệnh tạo bảng của mình. – dmon

+0

Hi Mat. Tôi đã chỉnh sửa câu hỏi của mình bằng lược đồ của bảng. – Gilbou

Trả lời

10

Tìm thấy. Các phiên bản khác nhau của Android không hoạt động theo cùng một cách. Trên Android 4.x, tất cả các dòng 'bindXXX' phải được đặt trong vòng lặp 'while'. Ngay cả khi nó lặp đi lặp lại.

while (i < shares.length) { 

    if (users[i] != null) { 

     stmt.bindLong(1, users[i].rowId); 
     stmt.bindLong(2, expense.rowId); 
     stmt.bindLong(3, expense.tt.rowId); 
     stmt.bindString(4, String.valueOf(shares[i])); 
     stmt.bindString(5, String.valueOf(amounts[i])); 

     stmt.execute(); 
    } 

    i++; 

} 
+0

Tuyệt vời - cảm ơn vì điều này. Tôi sẽ không bao giờ có suy nghĩ để rebind ** ALL ** tham số theo 4.x –

+1

Làm thế nào để 'bind'' Boolean' giá trị.? –

+0

@Pratik: Không chắc bạn đã tìm thấy câu trả lời chưa nhưng SQLite không có kiểu boolean. Bạn cần sử dụng một trong các loại khác. Tôi sử dụng một số nguyên 0/1 và chuyển đổi trên đầu vào/đầu ra. –

1

Đây là Gilbou hoàn hảo! Tôi cũng đã thực hiện giải pháp ràng buộc dữ liệu của bạn cho tác vụ của mình: tệp csv nhập vào cơ sở dữ liệu SQLite. Có khoảng 25.000 hàng trong tệp csv và việc nhập nó và chèn vào SQLite mất khoảng 5 giây (trước khi mất 5 phút mà không cần ràng buộc dữ liệu !!)

Cảm ơn bạn rất nhiều!

Tôi cũng chia sẻ của tôi, có lẽ nó có thể giúp cho ai đó, quá (Android 4.0):

public boolean updateFromCsv() { 

    boolean ok = true; 
    String line = ""; 
    BufferedReader br = null; 

    try { 
     FileInputStream fis = new FileInputStream(Environment 
       .getExternalStorageDirectory().getAbsolutePath() 
       + "/Servantes/Be/leltariv_export.csv"); 
     br = new BufferedReader(new InputStreamReader(fis)); 
    } catch (FileNotFoundException e) { 
     ok = false; 
     e.printStackTrace(); 
    }  
    try { 

     database.beginTransaction(); 

     String sql = "INSERT INTO "+LeltarTable.TABLE_LELTAR_NEV+" "+ 
     "("+LeltarTable.COLUMN_ID+", "+LeltarTable.COLUMN_LSZ+", "+LeltarTable.COLUMN_MEGN+", "+LeltarTable.COLUMN_HELY+", "+LeltarTable.COLUMN_DARAB+") VALUES (?, ?, ?, ?, 0)"; 

     SQLiteStatement stmt = database.compileStatement(sql); 

     while ((line = br.readLine()) != null) { 
      String[] colums = line.split(";"); 
      stmt.bindAllArgsAsStrings(colums); 
      stmt.execute(); 
} 
     stmt.close(); 

     database.setTransactionSuccessful(); 

    }catch (Exception e) { 
     e.printStackTrace(); 
     ok = false; 
    } 
    finally { 
     try { 
      br.close(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
      ok = false; 
     } 
     database.endTransaction(); 
     } 

    return ok; 
} 
Các vấn đề liên quan