2012-02-26 17 views
5

utilizo el siguiente código para insertar nuevas líneas de manera eficiente en un DB:Android - SQLiteStatement (inserción múltiple) ejecutar falla con el Nivel API 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(); } 

} 

Funciona hasta Android 4.x donde consigo ese error:

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) 

Parece que se bloquea en stmt.execute() cuando la inserción de la segunda línea en la tabla.

¿Alguna pista?

- EDICIÓN -

El esquema de la tabla es la siguiente:

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+")); "; 

Este código funciona como un encanto en Android 2.2 (pero no en Android 4.0).

de impresión de las dos primeras líneas inserto (se bloquea cuando se trata de insertar el segundo):

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

Bueno, parece que estás violando una restricción. ¿Cuál es tu esquema de mesa? – Mat

+0

Estoy de acuerdo con @Mat, debe publicar su declaración de tabla de creación. – dmon

+0

Hola Mat. Edité mi pregunta con el esquema de la tabla. – Gilbou

Respuesta

10

Encontrado. Las diferentes versiones de Android no se comportan de la misma manera. En Android 4.x, todas las líneas 'bindXXX' deben colocarse en el ciclo 'while'. Incluso si es repetitivo.

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

Excelente, gracias por esto. Nunca hubiera pensado volver a vincular los parámetros ** ALL ** en 4.x –

+1

Cómo 'bind'' Boolean' values.? –

+0

@Pratik: No estoy seguro de si ha encontrado su respuesta todavía, pero SQLite no tiene un tipo booleano. Necesitas usar uno de los otros tipos. Uso un entero 0/1 y convierto en entrada/salida. –

1

Esto es perfecto Gilbou! También hice su solución de enlace de datos para mi tarea: importación de archivos csv a la base de datos SQLite. Hubo alrededor de 25,000 filas en el archivo csv, e importarlo e insertarlo en SQLite me llevó unos 5 segundos (¡antes de 5 minutos sin datos vinculados!)

¡Muchas gracias!

También comparten la mía, tal vez puede ayuda a alguien, también (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; 
} 
Cuestiones relacionadas