From a7daf53ec53677130b379bada9e7d424f1a55450 Mon Sep 17 00:00:00 2001 From: zhuoxu Date: Thu, 2 Mar 2023 17:44:11 +0800 Subject: [PATCH 1/3] Fixed UpdateBuilder.update() / DeleteBuilder.delete() always return 0 when WAL enabled --- .../android/AndroidCompiledStatement.java | 47 ++++++++++++++++++- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/j256/ormlite/android/AndroidCompiledStatement.java b/src/main/java/com/j256/ormlite/android/AndroidCompiledStatement.java index df80b9d..c843218 100644 --- a/src/main/java/com/j256/ormlite/android/AndroidCompiledStatement.java +++ b/src/main/java/com/j256/ormlite/android/AndroidCompiledStatement.java @@ -1,5 +1,7 @@ package com.j256.ormlite.android; +import java.lang.reflect.Array; +import java.lang.reflect.Method; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; @@ -213,12 +215,18 @@ public String toString() { * Execute some SQL on the database and return the number of rows changed. */ static int execSql(SQLiteDatabase db, String label, String finalSql, Object[] argArray) throws SQLException { + int result = -1; try { - db.execSQL(finalSql, argArray); + result = exeSqlCompatibly(db, finalSql, argArray); } catch (android.database.SQLException e) { throw new SQLException("Problems executing " + label + " Android statement: " + finalSql, e); } - int result; + + // reflected sql method returns will >= 0 + if (result >= 0) { + return result; + } + SQLiteStatement stmt = null; try { // ask sqlite how many rows were just changed @@ -236,6 +244,41 @@ static int execSql(SQLiteDatabase db, String label, String finalSql, Object[] ar return result; } + /** + * Use reflection first, when reflection error, use origin method without return code + * @return modified rows count + */ + private static int exeSqlCompatibly(SQLiteDatabase db, String sql, Object[] bindArgs) { + try { + int result = invokeExecSqlMethod(db, sql, bindArgs); + return result; + } catch (android.database.SQLException e) { + db.execSQL(sql, bindArgs); + return -1; + } + } + + private static int invokeExecSqlMethod(SQLiteDatabase db, String sql, Object[] bindArgs) throws android.database.SQLException { + try { + Object result = hiddenExecSqlMethod().invoke(db, sql, bindArgs); + logger.trace("invokeExecSqlMethod result: {}", result); + return (int) result; + } catch (Exception e) { + throw new android.database.SQLException("reflect error", e); + } + } + + private static Method hiddenExecSqlMethod() { + Class clz = SQLiteDatabase.class; + Class objArrClass = Array.newInstance(Object.class, 0).getClass(); + try { + Method executeSqlMethod = clz.getMethod("executeSql", String.class, objArrClass); + return executeSqlMethod; + } catch (NoSuchMethodException e) { + return null; + } + } + private void isInPrep() throws SQLException { if (cursor != null) { throw new SQLException("Query already run. Cannot add argument values."); From 88aeff7d7f7a3b7b181560c673d3e2418d931e14 Mon Sep 17 00:00:00 2001 From: zhuoxu Date: Wed, 8 Mar 2023 15:46:38 +0800 Subject: [PATCH 2/3] Refactor logic by code review --- .../android/AndroidCompiledStatement.java | 52 +++++++++++-------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/j256/ormlite/android/AndroidCompiledStatement.java b/src/main/java/com/j256/ormlite/android/AndroidCompiledStatement.java index c843218..2355ce1 100644 --- a/src/main/java/com/j256/ormlite/android/AndroidCompiledStatement.java +++ b/src/main/java/com/j256/ormlite/android/AndroidCompiledStatement.java @@ -1,6 +1,6 @@ package com.j256.ormlite.android; -import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.sql.SQLException; import java.util.ArrayList; @@ -33,6 +33,8 @@ public class AndroidCompiledStatement implements CompiledStatement { private static final String[] NO_STRING_ARGS = new String[0]; private static final ApiCompatibility apiCompatibility = ApiCompatibilityUtils.getCompatibility(); + private static Method hiddenExecSqlMethod; + private final String sql; private final SQLiteDatabase db; private final StatementType type; @@ -44,6 +46,10 @@ public class AndroidCompiledStatement implements CompiledStatement { private Integer max; private CancellationHook cancellationHook; + static { + hiddenExecSqlMethod = hiddenExecSqlMethod(); + } + public AndroidCompiledStatement(String sql, SQLiteDatabase db, StatementType type, boolean cancelQueriesEnabled, boolean cacheStore) { this.sql = sql; @@ -215,17 +221,18 @@ public String toString() { * Execute some SQL on the database and return the number of rows changed. */ static int execSql(SQLiteDatabase db, String label, String finalSql, Object[] argArray) throws SQLException { - int result = -1; try { - result = exeSqlCompatibly(db, finalSql, argArray); + Integer result = execSqlCompatibly(db, finalSql, argArray); + // reflection sql method returns will >= 0, public API will return null + if (result != null && result >= 0) { + logger.trace("executing statement using reflection {} changed {} rows: {}", label, result, finalSql); + return result; + } } catch (android.database.SQLException e) { throw new SQLException("Problems executing " + label + " Android statement: " + finalSql, e); } - // reflected sql method returns will >= 0 - if (result >= 0) { - return result; - } + int result; SQLiteStatement stmt = null; try { @@ -248,32 +255,35 @@ static int execSql(SQLiteDatabase db, String label, String finalSql, Object[] ar * Use reflection first, when reflection error, use origin method without return code * @return modified rows count */ - private static int exeSqlCompatibly(SQLiteDatabase db, String sql, Object[] bindArgs) { + private static Integer execSqlCompatibly(SQLiteDatabase db, String sql, Object[] bindArgs) { try { - int result = invokeExecSqlMethod(db, sql, bindArgs); - return result; + return invokeHiddenExecSqlMethod(db, sql, bindArgs); } catch (android.database.SQLException e) { + logger.trace(e, "reflection failed, call origin method, sql {}", sql); db.execSQL(sql, bindArgs); - return -1; + return null; } } - private static int invokeExecSqlMethod(SQLiteDatabase db, String sql, Object[] bindArgs) throws android.database.SQLException { + private static int invokeHiddenExecSqlMethod(SQLiteDatabase db, String sql, Object[] bindArgs) throws android.database.SQLException { try { - Object result = hiddenExecSqlMethod().invoke(db, sql, bindArgs); - logger.trace("invokeExecSqlMethod result: {}", result); - return (int) result; - } catch (Exception e) { - throw new android.database.SQLException("reflect error", e); + if (hiddenExecSqlMethod != null) { + Object result = hiddenExecSqlMethod.invoke(db, sql, bindArgs); + logger.trace("invoke hidden execSql method result: {}", result); + return (int) result; + } else { + throw new android.database.SQLException("reflection get method error"); + } + } catch (InvocationTargetException | IllegalAccessException e) { + // reflection cannot work, set method null, no need invoke anymore + hiddenExecSqlMethod = null; + throw new android.database.SQLException("reflection invoke error", e); } } private static Method hiddenExecSqlMethod() { - Class clz = SQLiteDatabase.class; - Class objArrClass = Array.newInstance(Object.class, 0).getClass(); try { - Method executeSqlMethod = clz.getMethod("executeSql", String.class, objArrClass); - return executeSqlMethod; + return SQLiteDatabase.class.getMethod("executeSql", String.class, Object[].class); } catch (NoSuchMethodException e) { return null; } From 6897fb855e523651a1a9a39ab5e28f7e18ea9884 Mon Sep 17 00:00:00 2001 From: zhuoxu Date: Thu, 9 Mar 2023 15:23:46 +0800 Subject: [PATCH 3/3] Refactor logic by code review (2) --- .../android/AndroidCompiledStatement.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/j256/ormlite/android/AndroidCompiledStatement.java b/src/main/java/com/j256/ormlite/android/AndroidCompiledStatement.java index 2355ce1..bec1aad 100644 --- a/src/main/java/com/j256/ormlite/android/AndroidCompiledStatement.java +++ b/src/main/java/com/j256/ormlite/android/AndroidCompiledStatement.java @@ -256,28 +256,29 @@ static int execSql(SQLiteDatabase db, String label, String finalSql, Object[] ar * @return modified rows count */ private static Integer execSqlCompatibly(SQLiteDatabase db, String sql, Object[] bindArgs) { - try { - return invokeHiddenExecSqlMethod(db, sql, bindArgs); - } catch (android.database.SQLException e) { - logger.trace(e, "reflection failed, call origin method, sql {}", sql); + Integer result = invokeHiddenExecSqlMethod(db, sql, bindArgs); + if (result == null) { + logger.trace("reflection failed, call origin method, sql {}", sql); db.execSQL(sql, bindArgs); - return null; } + return result; } - private static int invokeHiddenExecSqlMethod(SQLiteDatabase db, String sql, Object[] bindArgs) throws android.database.SQLException { + private static Integer invokeHiddenExecSqlMethod(SQLiteDatabase db, String sql, Object[] bindArgs) { try { if (hiddenExecSqlMethod != null) { Object result = hiddenExecSqlMethod.invoke(db, sql, bindArgs); logger.trace("invoke hidden execSql method result: {}", result); return (int) result; } else { - throw new android.database.SQLException("reflection get method error"); + logger.trace("reflection get method error"); + return null; } } catch (InvocationTargetException | IllegalAccessException e) { // reflection cannot work, set method null, no need invoke anymore + logger.trace("reflection invoke error"); hiddenExecSqlMethod = null; - throw new android.database.SQLException("reflection invoke error", e); + return null; } }