Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge branch 'develop'

  • Loading branch information...
commit 2c808042b6768690086b60b10116de2aafee9759 2 parents dc0968c + 385138a
@casidiablo authored
View
14 pom.xml
@@ -25,7 +25,7 @@
<groupId>com.codeslap</groupId>
<artifactId>persistence</artifactId>
- <version>0.9.14-SNAPSHOT</version>
+ <version>0.9.16-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Codeslap Persistence</name>
<description>Core functionality provided by MarkdownJ.</description>
@@ -77,9 +77,9 @@
<scope>test</scope>
</dependency>
<dependency>
- <groupId>com.seventheye.android</groupId>
+ <groupId>com.codeslap</groupId>
<artifactId>robolectric-sqlite</artifactId>
- <version>1.0</version>
+ <version>1.0.1-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -108,14 +108,6 @@
<artifactId>maven-source-plugin</artifactId>
<version>2.1.2</version>
</plugin>
- <plugin>
- <groupId>com.atlassian.maven.plugins</groupId>
- <artifactId>maven-clover2-plugin</artifactId>
- <version>3.1.3</version>
- <configuration>
- <licenseLocation>/Users/cristian/Dropbox/clover.license</licenseLocation>
- </configuration>
- </plugin>
</plugins>
</build>
View
5 src/main/java/com/codeslap/persistence/Column.java
@@ -44,4 +44,9 @@
* @return the value to set to this element when no argument is assigned. This works for TEXT columns only.
*/
String defaultValue() default NULL;
+
+ /**
+ * @return true if the name of this column must be forced. Useful when handling primary keys whose name is not _id
+ */
+ boolean forceName() default false;
}
View
6 src/main/java/com/codeslap/persistence/Constraint.java
@@ -21,7 +21,7 @@
*/
public class Constraint {
private String mOrderBy;
- private int mLimit;
+ private Integer mLimit;
private String mGroupBy;
public Constraint() {
@@ -32,7 +32,7 @@ public Constraint orderBy(String column) {
return this;
}
- public Constraint limit(int limit) {
+ public Constraint limit(Integer limit) {
mLimit = limit;
return this;
}
@@ -46,7 +46,7 @@ String getOrderBy() {
return mOrderBy;
}
- int getLimit() {
+ Integer getLimit() {
return mLimit;
}
View
2  src/main/java/com/codeslap/persistence/ManyToMany.java
@@ -53,7 +53,7 @@ public ManyToMany(Class<?> classA, Class<?> classB) {
String getCreateTableStatement() {
StringBuilder builder = new StringBuilder();
builder.append("CREATE TABLE IF NOT EXISTS ").append(buildTableName(mClassA, mClassB));
- builder.append(" (").append(SQLHelper.PRIMARY_KEY).append(" AUTOINCREMENT, ");
+ builder.append(" (_id INTEGER PRIMARY KEY AUTOINCREMENT, ");
builder.append(getMainKey()).append(" TEXT NOT NULL, ");
builder.append(getSecondaryKey()).append(" TEXT NOT NULL");
builder.append(");");
View
8 src/main/java/com/codeslap/persistence/PrefsAdapterImpl.java
@@ -48,7 +48,7 @@
@Override
public <T> void store(T bean) {
- Class<? extends Object> theClass = bean.getClass();
+ Class<?> theClass = bean.getClass();
if (!PersistenceConfig.getPreference(mName).belongsToPreferences(theClass)) {
throw new IllegalStateException("This object is not associated with a preference persister");
}
@@ -104,7 +104,7 @@
}
field.set(bean, value);
}
- } catch (Exception e) {
+ } catch (Exception ignored) {
}
return bean;
}
@@ -150,12 +150,12 @@
} else if (field.getType() == String.class) {
editor.putString(keyName, String.valueOf(value));
}
- } catch (IllegalAccessException e) {
+ } catch (IllegalAccessException ignored) {
}
}
}
- private SharedPreferences getSharedPreferences(Class<? extends Object> theClass) {
+ private SharedPreferences getSharedPreferences(Class<?> theClass) {
String key = theClass.getSimpleName();
if (!mPreferences.containsKey(key)) {
SharedPreferences prefs;
View
89 src/main/java/com/codeslap/persistence/SQLHelper.java
@@ -28,8 +28,7 @@
static final String ID = "id";
static final String _ID = "_id";
- static final String PRIMARY_KEY = _ID + " INTEGER PRIMARY KEY";
- private static final String PRIMARY_KEY_TEXT = "_id TEXT PRIMARY KEY";
+ static final String PRIMARY_KEY = "%s INTEGER PRIMARY KEY";
private static final String HEXES = "0123456789ABCDEF";
private static final Map<Class<?>, String> INSERT_COLUMNS_CACHE = new HashMap<Class<?>, String>();
@@ -48,9 +47,9 @@ static String getCreateTableSentence(String dbName, Class clazz) {
for (Field field : declaredFields) {
String columnName = getColumnName(field);
if (isPrimaryKey(field)) {
- String primaryKeySentence = PRIMARY_KEY;
+ String primaryKeySentence = getCreatePrimaryKey(field);
if (field.getType() == String.class) {// what types are supported
- primaryKeySentence = PRIMARY_KEY_TEXT;
+ primaryKeySentence = primaryKeySentence.replace("INTEGER PRIMARY KEY", "TEXT PRIMARY KEY");
} else if (PersistenceConfig.getDatabase(dbName).isAutoincrement(clazz)) {
primaryKeySentence += " AUTOINCREMENT";
}
@@ -88,10 +87,10 @@ static String getCreateTableSentence(String dbName, Class clazz) {
Collections.sort(fieldSentences, new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
- if (s1.contains(PRIMARY_KEY)) {
+ if (s1.contains(String.format(PRIMARY_KEY, ""))) {
return -1;
}
- if (s2.contains(PRIMARY_KEY)) {
+ if (s2.contains(String.format(PRIMARY_KEY, ""))) {
return 1;
}
return 0;
@@ -182,7 +181,7 @@ private static String getFieldSentence(String name, Class<?> type, boolean notNu
conditions.add(String.format("%s LIKE '%s'", columnName,
String.valueOf(value).replace("'", "''")));
} else if (field.getType() == Boolean.class || field.getType() == boolean.class) {
- int intValue = ((Boolean) value).booleanValue() ? 1 : 0;
+ int intValue = (Boolean) value ? 1 : 0;
conditions.add(String.format("%s = '%d'", columnName, intValue));
} else {
conditions.add(String.format("%s = '%s'", columnName, value));
@@ -194,7 +193,7 @@ private static String getFieldSentence(String name, Class<?> type, boolean notNu
conditions.add(String.format("%s = ?", columnName));
}
if (field.getType() == Boolean.class || field.getType() == boolean.class) {
- value = ((Boolean) value).booleanValue() ? 1 : 0;
+ value = (Boolean) value ? 1 : 0;
}
args.add(String.valueOf(value));
}
@@ -243,7 +242,7 @@ private static String getFieldSentence(String name, Class<?> type, boolean notNu
boolean isBoolean = field.getType() == Boolean.class || field.getType() == boolean.class;
if (isBoolean || hasData(type, value)) {
if (isBoolean) {
- int intValue = ((Boolean) value).booleanValue() ? 1 : 0;
+ int intValue = (Boolean) value ? 1 : 0;
sets.add(String.format("%s = '%d'", getColumnName(field), intValue));
} else if (field.getType() == byte[].class || field.getType() == Byte[].class) {
String hex = getHex((byte[]) value);
@@ -325,8 +324,8 @@ static String getColumnName(Field field) {
if (COLUMN_NAMES_CACHE.containsKey(field)) {
return COLUMN_NAMES_CACHE.get(field);
}
- if (isPrimaryKey(field)) {
- return _ID;
+ if (isPrimaryKey(field) && !forcedName(field)) {
+ return getIdColumn(field);
}
Column column = field.getAnnotation(Column.class);
if (column != null) {
@@ -376,7 +375,7 @@ static String getColumnName(Field field) {
// build insert statement for the main object
if (values.size() == 0 && persistence.isAutoincrement(bean.getClass())) {
String hack = String.format("(SELECT seq FROM sqlite_sequence WHERE name = '%s')+1", getTableName(bean));
- return String.format("INSERT OR IGNORE INTO %s (%s) VALUES (%s);%s", getTableName(bean), _ID, hack, STATEMENT_SEPARATOR);
+ return String.format("INSERT OR IGNORE INTO %s (%s) VALUES (%s);%s", getTableName(bean), getIdColumn(getPrimaryKeyField(bean.getClass())), hack, STATEMENT_SEPARATOR);
}
return String.format("INSERT OR IGNORE INTO %s (%s) VALUES (%s);%s", getTableName(bean), columnsSet, join(values, ", "), STATEMENT_SEPARATOR);
}
@@ -389,7 +388,7 @@ static String getColumnName(Field field) {
Field[] fields = getDeclaredFields(theClass);
for (Field field : fields) {
// if the class has an autoincrement, ignore the ID
- if (persistence.isAutoincrement(theClass) && isPrimaryKey(field)) {
+ if (isPrimaryKey(field) && persistence.isAutoincrement(theClass)) {
continue;
}
try {
@@ -406,7 +405,7 @@ static String getColumnName(Field field) {
continue;
}
if (field.getType() == Boolean.class || field.getType() == boolean.class) {
- int intValue = ((Boolean) value).booleanValue() ? 1 : 0;
+ int intValue = (Boolean) value ? 1 : 0;
values.add(String.valueOf(intValue));
} else if (field.getType() == Byte[].class || field.getType() == byte[].class) {
if (value == null) {
@@ -417,7 +416,10 @@ static String getColumnName(Field field) {
}
} else if (value == null) {
Column columnAnnotation = field.getAnnotation(Column.class);
- boolean hasDefault = !columnAnnotation.defaultValue().equals(Column.NULL);
+ boolean hasDefault = false;
+ if (columnAnnotation != null) {
+ hasDefault = !columnAnnotation.defaultValue().equals(Column.NULL);
+ }
if (columnAnnotation != null && columnAnnotation.notNull() && !hasDefault) {
String msg = String.format("Field %s from class %s cannot be null. It was marked with the @Column not null annotation and it has not a default value", field.getName(), theClass.getSimpleName());
throw new IllegalStateException(msg);
@@ -462,7 +464,11 @@ private static boolean isPrimaryKey(Field field) {
if (field.isAnnotationPresent(PrimaryKey.class)) {
return true;
}
- return field.getName().equals(ID) || field.getName().equals(_ID);
+ return field.getName().equals(ID) || field.getName().equals(getIdColumn(field));
+ }
+
+ private static boolean forcedName(Field field) {
+ return field.isAnnotationPresent(Column.class) && field.getAnnotation(Column.class).forceName();
}
static String getTableName(Class<?> theClass) {
@@ -537,6 +543,17 @@ public static Field getPrimaryKeyField(Class<?> theClass) {
throw new IllegalStateException("Class " + theClass + " does not have a primary key");
}
+ static String getIdColumn(Field field) {
+ if (forcedName(field)) {
+ return getColumnName(field);
+ }
+ return _ID;
+ }
+
+ static String getCreatePrimaryKey(Field field) {
+ return String.format(PRIMARY_KEY, getIdColumn(field));
+ }
+
static <T, G> Cursor getCursorFindAllWhere(SQLiteDatabase db, String dbName, Class<? extends T> clazz, T sample, G attachedTo, Constraint constraint) {
String[] selectionArgs = null;
String where = null;
@@ -554,9 +571,47 @@ public static Field getPrimaryKeyField(Class<?> theClass) {
String groupBy = null;
if (constraint != null) {
orderBy = constraint.getOrderBy();
- limit = String.valueOf(constraint.getLimit());
+ if (constraint.getLimit() != null) {
+ limit = constraint.getLimit().toString();
+ }
groupBy = constraint.getGroupBy();
}
return db.query(getTableName(clazz), null, where, selectionArgs, groupBy, null, orderBy, limit);
}
+
+ static <T> String getFastInsertSqlHeader(T bean, SqlPersistence persistence) {
+ ArrayList<String> values = new ArrayList<String>();
+ ArrayList<String> columns = new ArrayList<String>();
+ populateColumnsAndValues(bean, null, values, columns, persistence);
+
+ StringBuilder result = new StringBuilder();
+
+ result.append("INSERT OR IGNORE INTO ").append(getTableName(bean.getClass())).append(" ");
+ // set insert columns
+ result.append("(");
+ result.append(join(columns, ", "));
+ result.append(")");
+ // add first insertion body
+ result.append(" SELECT ");
+
+ ArrayList<String> columnsAndValues = new ArrayList<String>();
+ for (int i = 0, valuesSize = values.size(); i < valuesSize; i++) {
+ String column = columns.get(i);
+ String value = values.get(i);
+ StringBuilder columnAndValue = new StringBuilder();
+ columnAndValue.append(value).append(" AS ").append(column);
+ columnsAndValues.add(columnAndValue.toString());
+ }
+ result.append(join(columnsAndValues, ", "));
+ return result.toString();
+ }
+
+ static <T> String getUnionInsertSql(T bean, SqlPersistence persistence) {
+ ArrayList<String> values = new ArrayList<String>();
+ populateColumnsAndValues(bean, null, values, null, persistence);
+ StringBuilder builder = new StringBuilder();
+ builder.append(" UNION SELECT ");
+ builder.append(join(values, ", "));
+ return builder.toString();
+ }
}
View
43 src/main/java/com/codeslap/persistence/SqlAdapter.java
@@ -67,6 +67,8 @@
/**
* Updates one of more records in the database
+ * <p/>
+ * <b>Note:</b> You must clean the variable <code>where</code>, for instance: <code>entry = 'don't'</code> should be <code>'dont''t'</code>
*
* @param object the object to insert into the database
* @param where the sample object
@@ -77,6 +79,8 @@
/**
* Updates one of more records in the database
+ * <p/>
+ * <b>Note:</b> You must clean the variables <code>where</code> and <code>whereArgs</code>, for instance: <code>entry = 'don't'</code> should be <code>'dont''t'</code>
*
* @param object the object to insert into the database
* @param where a SQL query. It is recommended to use wildcards like: <code>something = ? AND another = ?</code>
@@ -88,6 +92,8 @@
/**
* Retrieves an object from the database
+ * <p/>
+ * <b>Note:</b> You must clean the variable <code>where</code>, for instance: <code>entry = 'don't'</code> should be <code>'dont''t'</code>
*
* @param where sample object
* @param <T> object type. Must be already registered using {@link SqlPersistence#match(Class[])}
@@ -97,8 +103,10 @@
/**
* Retrieves an object from the database
+ * <p/>
+ * <b>Note:</b> You must clean the variables <code>where</code> and <code>whereArgs</code>, for instance: <code>entry = 'don't'</code> should be <code>'dont''t'</code>
*
- * @param theClass the type of the object to retrieve
+ * @param theClass the type of the object to retrieve
* @param where a SQL query. It is recommended to use wildcards like: <code>something = ? AND another = ?</code>
* @param whereArgs the list of values used in the wildcards
* @param <T> object type. Must be already registered using {@link SqlPersistence#match(Class[])}
@@ -110,13 +118,15 @@
* Retrieves all elements from the database of the specified type
*
* @param theClass the class of the object that we want to retrieve
- * @param <T> object type. Must be already registered using {@link SqlPersistence#match(Class[])}
+ * @param <T> object type. Must be already registered using {@link SqlPersistence#match(Class[])}
* @return a list of T objects
*/
<T> List<T> findAll(Class<T> theClass);
/**
* Retrieves all elements from the database that matches the specified sample
+ * <p/>
+ * <b>Note:</b> You must clean the variable <code>where</code>, for instance: <code>entry = 'don't'</code> should be <code>'dont''t'</code>
*
* @param where sample object
* @param <T> object type. Must be already registered using {@link SqlPersistence#match(Class[])}
@@ -126,6 +136,8 @@
/**
* Retrieves all elements from the database that matches the specified sample. And follows the specified constraint.
+ * <p/>
+ * <b>Note:</b> You must clean the variable <code>where</code>, for instance: <code>entry = 'don't'</code> should be <code>'dont''t'</code>
*
* @param where sample object
* @param constraint constrains for this query
@@ -136,6 +148,8 @@
/**
* Retrieves all elements from the database that are attached to the specified object
+ * <p/>
+ * <b>Note:</b> You must clean the variable <code>where</code>, for instance: <code>entry = 'don't'</code> should be <code>'dont''t'</code>
*
* @param where the sample object
* @param attachedTo the object that is attached to the sample object
@@ -146,8 +160,10 @@
/**
* Retrieves all elements from the database that matches the specified sample
+ * <p/>
+ * <b>Note:</b> You must clean the variables <code>where</code> and <code>whereArgs</code>, for instance: <code>entry = 'don't'</code> should be <code>'dont''t'</code>
*
- * @param theClass the class to find all items
+ * @param theClass the class to find all items
* @param where a SQL query. It is recommended to use wildcards like: <code>something = ? AND another = ?</code>
* @param whereArgs the list of values used in the wildcards
* @param <T> object type. Must be already registered using {@link SqlPersistence#match(Class[])}
@@ -157,6 +173,8 @@
/**
* Deletes one or more elements from the database
+ * <p/>
+ * <b>Note:</b> You must clean the variable <code>where</code>, for instance: <code>entry = 'don't'</code> should be <code>'dont''t'</code>
*
* @param where sample object
* @param <T> object type. Must be already registered using {@link SqlPersistence#match(Class[])}
@@ -166,6 +184,9 @@
/**
* Deletes one or more elements from the database
+ * <p/>
+ * <p/>
+ * <b>Note:</b> You must clean the variable <code>where</code>, for instance: <code>entry = 'don't'</code> should be <code>'dont''t'</code>
*
* @param where sample object
* @param onCascade true if it must delete relations on cascade
@@ -176,8 +197,10 @@
/**
* Deletes one or more elements from the database
+ * <p/>
+ * <b>Note:</b> You must clean the variables <code>where</code> and <code>whereArgs</code>, for instance: <code>entry = 'don't'</code> should be <code>'dont''t'</code>
*
- * @param theClass the type of the object to delete
+ * @param theClass the type of the object to delete
* @param where a SQL query. It is recommended to use wildcards like: <code>something = ? AND another = ?</code>
* @param whereArgs the list of values used in the wildcards
* @param <T> object type. Must be already registered using {@link SqlPersistence#match(Class[])}
@@ -187,8 +210,10 @@
/**
* Deletes one or more elements from the database
+ * <p/>
+ * <b>Note:</b> You must clean the variables <code>where</code> and <code>whereArgs</code>, for instance: <code>entry = 'don't'</code> should be <code>'dont''t'</code>
*
- * @param theClass the type of the object to delete
+ * @param theClass the type of the object to delete
* @param where a SQL query. It is recommended to use wildcards like: <code>something = ? AND another = ?</code>
* @param onCascade true if it must delete relations on cascade
* @param whereArgs the list of values used in the wildcards
@@ -206,6 +231,8 @@
/**
* Counts how many items there are in the database and match the specified condition
+ * <p/>
+ * <b>Note:</b> You must clean the variables <code>where</code>, for instance: <code>entry = 'don't'</code> should be <code>'dont''t'</code>
*
* @param where the sample object
* @param <T> object type. Must be already registered using {@link SqlPersistence#match(Class[])}
@@ -215,8 +242,10 @@
/**
* Counts how many items there are in the database and match the specified condition
+ * <p/>
+ * <b>Note:</b> You must clean the variables <code>where</code> and <code>whereArgs</code>, for instance: <code>entry = 'don't'</code> should be <code>'dont''t'</code>
*
- * @param theClass the class of the object that we want to count
+ * @param theClass the class of the object that we want to count
* @param where a SQL query. It is recommended to use wildcards like: <code>something = ? AND another = ?</code>
* @param whereArgs the list of values used in the wildcards
* @param <T> object type. Must be already registered using {@link SqlPersistence#match(Class[])}
@@ -228,7 +257,7 @@
* Counts how many items there are in the database
*
* @param theClass the class of the object that we want to count
- * @param <T> object type. Must be already registered using {@link SqlPersistence#match(Class[])}
+ * @param <T> object type. Must be already registered using {@link SqlPersistence#match(Class[])}
* @return number of elements in the table of the specified object
*/
<T> int count(Class<T> theClass);
View
164 src/main/java/com/codeslap/persistence/SqliteAdapterImpl.java
@@ -170,21 +170,57 @@
if (listener != null) {
listener.onProgressChange(0);
}
- int progress;
- int all = collection.size() + 1; // 1 == commit phase
- for (int i = 0, collectionSize = collection.size(); i < collectionSize; i++) {
- T object = collection.get(i);
- String sqlStatement = getSqlStatement(object, new Node(object.getClass()), attachedTo);
- if (sqlStatement == null) {
- continue;
+ SqlPersistence.Relationship relationship = SqlPersistence.Relationship.UNKNOWN;
+ if (!collection.isEmpty()) {
+ T object = collection.get(0);
+ relationship = mPersistence.getRelationship(object.getClass());
+ }
+ // if there is no listener, attached object, collection is too small or objects in the list have inner
+ // relationships: insert them in a normal way, in which there will be a sql execution per object
+ if (listener != null || attachedTo != null || collection.size() <= 1 || relationship != SqlPersistence.Relationship.UNKNOWN) {
+ int progress;
+ int all = collection.size() + 1; // 1 == commit phase
+ for (int i = 0, collectionSize = collection.size(); i < collectionSize; i++) {
+ T object = collection.get(i);
+ String sqlStatement = getSqlStatement(object, new Node(object.getClass()), attachedTo);
+ if (sqlStatement == null) {
+ continue;
+ }
+ String[] statements = sqlStatement.split(SQLHelper.STATEMENT_SEPARATOR);
+ for (String statement : statements) {
+ mDbHelper.getDatabase().execSQL(statement);
+ }
+ if (listener != null) {
+ progress = i * 100 / all;
+ listener.onProgressChange(progress);
+ }
}
- String[] statements = sqlStatement.split(SQLHelper.STATEMENT_SEPARATOR);
- for (String statement : statements) {
- mDbHelper.getDatabase().execSQL(statement);
+ } else {
+ // if it reaches here, we can insert collection in a faster way by creating few sql statements
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0, collectionSize = collection.size(), newItems = 0; i < collectionSize; i++) {
+ T bean = collection.get(i);
+ String updateStatement = getUpdateStatementIfPossible(bean);
+ if (!TextUtils.isEmpty(updateStatement)) {
+ String[] statements = updateStatement.split(SQLHelper.STATEMENT_SEPARATOR);
+ for (String statement : statements) {
+ mDbHelper.getDatabase().execSQL(statement);
+ }
+ continue;
+ }
+ if (newItems % 400 == 0) {
+ if (newItems > 0) {
+ mDbHelper.getDatabase().execSQL(builder.append(";").toString());
+ builder = new StringBuilder();
+ }
+ builder.append(SQLHelper.getFastInsertSqlHeader(bean, mPersistence));
+ } else {
+ builder.append(SQLHelper.getUnionInsertSql(bean, mPersistence));
+ }
+ newItems++;
}
- if (listener != null) {
- progress = i * 100 / all;
- listener.onProgressChange(progress);
+ if (builder.length() > 0) {
+ mDbHelper.getDatabase().execSQL(builder.append(";").toString());
}
}
mDbHelper.getDatabase().execSQL("COMMIT;");
@@ -220,7 +256,7 @@
// not a big deal
}
}
- storeCollection(collection, null);
+ storeCollection(collection, listener);
}
@Override
@@ -415,10 +451,23 @@ public void close() {
}
private <T, G> String getSqlStatement(T bean, Node tree, G attachedTo) {
- // first try to find the bean by id (if its id is not autoincrement)
- // and if it exists, do not insert it, update it
+ String updateStatement = getUpdateStatementIfPossible(bean);
+ if (!TextUtils.isEmpty(updateStatement)) {
+ return updateStatement;
+ }
+ String mainInsertStatement = SQLHelper.getInsertStatement(bean, attachedTo, mPersistence);
+ try {
+ mainInsertStatement += getSqlInsertForChildrenOf(bean, tree);
+ } catch (IllegalAccessException ignored) {
+ }
+ return mainInsertStatement;
+ }
+
+ private <T> String getUpdateStatementIfPossible(T bean) {
+ String result = null;
+ // try to find the bean by id and if it exists, do not insert it, update it
Class<T> theClass = (Class<T>) bean.getClass();
- // get its ID
+ // get its ID and make sure primary key is not null
Field theId = SQLHelper.getPrimaryKeyField(theClass);
theId.setAccessible(true);
if (theId.getType() == String.class ||
@@ -445,23 +494,15 @@ public void close() {
if (match != null) {
// if they are the same, do nothing...
if (bean.equals(match)) {
- return null;
+ result = null;
}
// update the bean using the just create sample
- return SQLHelper.buildUpdateStatement(bean, match);
+ result = SQLHelper.buildUpdateStatement(bean, match);
}
}
-
} catch (Exception ignored) {
}
-
-
- String mainInsertStatement = SQLHelper.getInsertStatement(bean, attachedTo, mPersistence);
- try {
- mainInsertStatement += getSqlInsertForChildrenOf(bean, tree);
- } catch (IllegalAccessException ignored) {
- }
- return mainInsertStatement;
+ return result;
}
private <T> String getSqlInsertForChildrenOf(T bean, Node tree) throws IllegalAccessException {// bodom
@@ -583,15 +624,25 @@ public void close() {
if (tree.addChild(node)) {
switch (mPersistence.getRelationship(theClass, collectionClass)) {
case MANY_TO_MANY: {
+ Field collectionId = SQLHelper.getPrimaryKeyField(collectionClass);
// build a query that uses the joining table and the joined object
- long id = query.getLong(query.getColumnIndex(SQLHelper._ID));
String collectionTableName = SQLHelper.getTableName(collectionClass);
- String sql = "SELECT * FROM " + SQLHelper.getTableName(collectionClass) +
- " WHERE " + SQLHelper._ID + " IN (SELECT " + collectionTableName + "_id FROM " +
- ManyToMany.buildTableName(theClass, collectionClass) +
- " WHERE " + SQLHelper.getTableName(theClass) + "_id = ?)";
+ String sql = new StringBuilder().append("SELECT * FROM ")
+ .append(SQLHelper.getTableName(collectionClass))
+ .append(" WHERE ")
+ .append(SQLHelper.getIdColumn(collectionId))
+ .append(" IN (SELECT ")
+ .append(collectionTableName)
+ .append(SQLHelper._ID)
+ .append(" FROM ")
+ .append(ManyToMany.buildTableName(theClass, collectionClass))
+ .append(" WHERE ")
+ .append(SQLHelper.getTableName(theClass))
+ .append(SQLHelper._ID)
+ .append(" = ?)").toString();
// execute the query
String[] selectionArgs = new String[1];
+ long id = query.getLong(query.getColumnIndex(SQLHelper._ID));
selectionArgs[0] = String.valueOf(id);
Cursor join = mDbHelper.getDatabase().rawQuery(sql, selectionArgs);
// set the result to the current field
@@ -612,8 +663,13 @@ public void close() {
Field throughField = belongsTo.getThroughField();
Object foreignValue = getValueFromCursor(throughField.getType(), belongsTo.getThroughColumnName(), query);
if (foreignValue != null) {
- String sql = "SELECT * FROM " + SQLHelper.getTableName(collectionClass) +
- " WHERE " + belongsTo.getForeignKey() + " = '" + foreignValue + "'";
+ String sql = new StringBuilder().append("SELECT * FROM ")
+ .append(SQLHelper.getTableName(collectionClass))
+ .append(" WHERE ")
+ .append(belongsTo.getForeignKey())
+ .append(" = '")
+ .append(foreignValue)
+ .append("'").toString();
// execute the query and set the result to the current field
Cursor join = mDbHelper.getDatabase().rawQuery(sql, null);
List listValue = new ArrayList();
@@ -646,24 +702,28 @@ public void close() {
}
private Object getValueFromCursor(Class<?> type, String name, Cursor query) {
- // get the column index
- int columnIndex = query.getColumnIndex(name);
- // get an object value depending on the type
- Object value = null;
- if (type == int.class || type == Integer.class) {
- value = query.getInt(columnIndex);
- } else if (type == long.class || type == Long.class) {
- value = query.getLong(columnIndex);
- } else if (type == boolean.class || type == Boolean.class) {
- value = query.getInt(columnIndex) == 1;
- } else if (type == float.class || type == Float.class || type == double.class || type == Double.class) {
- value = query.getFloat(columnIndex);
- } else if (type == String.class) {
- value = query.getString(columnIndex);
- } else if (type == byte[].class || type == Byte[].class) {
- value = query.getBlob(columnIndex);
+ try {
+ // get the column index
+ int columnIndex = query.getColumnIndex(name);
+ // get an object value depending on the type
+ Object value = null;
+ if (type == int.class || type == Integer.class) {
+ value = query.getInt(columnIndex);
+ } else if (type == long.class || type == Long.class) {
+ value = query.getLong(columnIndex);
+ } else if (type == boolean.class || type == Boolean.class) {
+ value = query.getInt(columnIndex) == 1;
+ } else if (type == float.class || type == Float.class || type == double.class || type == Double.class) {
+ value = query.getFloat(columnIndex);
+ } else if (type == String.class) {
+ value = query.getString(columnIndex);
+ } else if (type == byte[].class || type == Byte[].class) {
+ value = query.getBlob(columnIndex);
+ }
+ return value;
+ } catch (Exception e) {
+ throw new IllegalStateException("Error getting column " + name, e);
}
- return value;
}
}
View
1  src/main/java/com/codeslap/persistence/SqliteDb.java
@@ -72,6 +72,7 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
db.execSQL("DELETE FROM sqlite_sequence");
onCreate(db);
+ c.close();
}
}
}
View
64 src/test/java/com/codeslap/test/persistence/CollectionInsertionTest.java
@@ -0,0 +1,64 @@
+package com.codeslap.test.persistence;
+
+import com.codeslap.persistence.SqlAdapter;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+public class CollectionInsertionTest extends SqliteTest {
+ @Test
+ public void testCollectionInsertionTest() {
+ // create list to save
+ List<ExampleAutoincrement> collection = new ArrayList<ExampleAutoincrement>();
+ Random random = new Random();
+ for (int i = 0; i < 50000; i++) {
+ ExampleAutoincrement foo = new ExampleAutoincrement();
+ foo.name = "Foo Bar " + random.nextInt();
+ foo.number = random.nextInt();
+ foo.decimal = random.nextFloat();
+ foo.bool = random.nextBoolean();
+ foo.blob = foo.name.getBytes();
+ collection.add(foo);
+ }
+
+ // save collection using progress listener
+ getNormalAdapter().storeCollection(collection, new SqlAdapter.ProgressListener() {
+ @Override
+ public void onProgressChange(int percentage) {
+ }
+ });
+ performTesting(collection);
+
+ // save collection without progress listener to speed things up
+ getNormalAdapter().delete(ExampleAutoincrement.class, null, null);
+ getNormalAdapter().storeCollection(collection, null);
+ performTesting(collection);
+ }
+
+ private void performTesting(List<ExampleAutoincrement> collection) {
+ // it should have stored all items
+ assertEquals(collection.size(), getNormalAdapter().findAll(ExampleAutoincrement.class, null, null).size());
+ assertEquals(collection.size(), getNormalAdapter().count(ExampleAutoincrement.class));
+ assertEquals(collection.size(), getNormalAdapter().count(ExampleAutoincrement.class, null, null));
+
+ // now let's see if it stored everything
+ int i = 0;
+ for (ExampleAutoincrement exampleAutoincrement : collection) {
+ exampleAutoincrement.id = 0;
+ ExampleAutoincrement found = getNormalAdapter().findFirst(exampleAutoincrement);
+ assertNotNull(found);
+ exampleAutoincrement.id = found.id;
+ assertEquals(exampleAutoincrement, found);
+ i++;
+ if (i > 1000) {
+ // just check first 1000
+ break;
+ }
+ }
+ }
+}
View
16 src/test/java/com/codeslap/test/persistence/RawQueryTest.java
@@ -47,15 +47,27 @@ public void findAllByClassTest() {
getNormalAdapter().storeCollection(collection, null);
RawQuery rawQuery = Persistence.getRawQuery(new Activity());
- Cursor cursor = rawQuery.findAll(ExampleAutoincrement.class);
+ Cursor cursor = rawQuery.findAll("automatic", null, null, null, null, null, "number ASC", null);
assertNotNull(cursor);
assertEquals(100, cursor.getCount());
assertTrue(cursor.moveToFirst());
+ Collections.sort(collection, new Comparator<ExampleAutoincrement>() {
+ @Override
+ public int compare(ExampleAutoincrement foo, ExampleAutoincrement bar) {
+ if (foo.number < 0 && bar.number >= 0) {
+ return -1;
+ }
+ if (foo.number >= 0 && bar.number < 0) {
+ return 1;
+ }
+ return foo.number - bar.number;
+ }
+ });
+
int i = 0;
do {
ExampleAutoincrement item = collection.get(i);
- assertEquals(i + 1, cursor.getLong(cursor.getColumnIndex("_id")));
assertEquals(item.name, cursor.getString(cursor.getColumnIndex("name")));
assertEquals(item.number, cursor.getInt(cursor.getColumnIndex("number")));
assertEquals(item.decimal, cursor.getFloat(cursor.getColumnIndex("decimal")), 0.0f);
Please sign in to comment.
Something went wrong with that request. Please try again.