diff --git a/.project b/.project index 38905a6..f4e399a 100644 --- a/.project +++ b/.project @@ -1,6 +1,6 @@ - NonMavenAndroidSQLLiteOrm + AndroidSqLiteOrm diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 868f338..692f9f9 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -3,7 +3,7 @@ package="com.perfectworldprogramming.mobile.orm" android:versionCode="1" android:versionName="1.0"> - + @@ -12,15 +12,17 @@ + - - + + @@ -28,6 +30,5 @@ android:targetPackage="com.perfectworldprogramming.mobile.orm.test" android:name="android.test.InstrumentationTestRunner" /--> - \ No newline at end of file diff --git a/bin/.gitignore b/bin/.gitignore new file mode 100644 index 0000000..863e070 --- /dev/null +++ b/bin/.gitignore @@ -0,0 +1,8 @@ +/jarlist.cache +/NonMavenAndroidSQLLiteOrm.apk +/res +/AndroidSqLiteOrm.apk +/NonMavenAndroidSQLLiteOrmMerged.apk +/classes.dex +/resources.ap_ +/classes diff --git a/bin/classes.dex b/bin/classes.dex index 8e722c3..8ef409b 100644 Binary files a/bin/classes.dex and b/bin/classes.dex differ diff --git a/bin/classes/com/perfectworldprogramming/mobile/orm/AndroidSQLiteTemplate.class b/bin/classes/com/perfectworldprogramming/mobile/orm/AndroidSQLiteTemplate.class index fdb93f4..f328656 100644 Binary files a/bin/classes/com/perfectworldprogramming/mobile/orm/AndroidSQLiteTemplate.class and b/bin/classes/com/perfectworldprogramming/mobile/orm/AndroidSQLiteTemplate.class differ diff --git a/bin/classes/com/perfectworldprogramming/mobile/orm/CursorAdapter.class b/bin/classes/com/perfectworldprogramming/mobile/orm/CursorAdapter.class index f299679..af05d8c 100644 Binary files a/bin/classes/com/perfectworldprogramming/mobile/orm/CursorAdapter.class and b/bin/classes/com/perfectworldprogramming/mobile/orm/CursorAdapter.class differ diff --git a/bin/classes/com/perfectworldprogramming/mobile/orm/Main.class b/bin/classes/com/perfectworldprogramming/mobile/orm/Main.class index 245b4b5..cf3ee20 100644 Binary files a/bin/classes/com/perfectworldprogramming/mobile/orm/Main.class and b/bin/classes/com/perfectworldprogramming/mobile/orm/Main.class differ diff --git a/bin/classes/com/perfectworldprogramming/mobile/orm/annotations/Column.class b/bin/classes/com/perfectworldprogramming/mobile/orm/annotations/Column.class index 8474728..52a8e02 100644 Binary files a/bin/classes/com/perfectworldprogramming/mobile/orm/annotations/Column.class and b/bin/classes/com/perfectworldprogramming/mobile/orm/annotations/Column.class differ diff --git a/bin/classes/com/perfectworldprogramming/mobile/orm/annotations/ColumnType.class b/bin/classes/com/perfectworldprogramming/mobile/orm/annotations/ColumnType.class index bee0f0c..4f7448c 100644 Binary files a/bin/classes/com/perfectworldprogramming/mobile/orm/annotations/ColumnType.class and b/bin/classes/com/perfectworldprogramming/mobile/orm/annotations/ColumnType.class differ diff --git a/bin/classes/com/perfectworldprogramming/mobile/orm/creator/SQLLiteCreateStatementGenerator.class b/bin/classes/com/perfectworldprogramming/mobile/orm/creator/SQLLiteCreateStatementGenerator.class index cd0b797..7ed940e 100644 Binary files a/bin/classes/com/perfectworldprogramming/mobile/orm/creator/SQLLiteCreateStatementGenerator.class and b/bin/classes/com/perfectworldprogramming/mobile/orm/creator/SQLLiteCreateStatementGenerator.class differ diff --git a/bin/classes/com/perfectworldprogramming/mobile/orm/exception/DataAccessException.class b/bin/classes/com/perfectworldprogramming/mobile/orm/exception/DataAccessException.class index 51e23d3..285787d 100644 Binary files a/bin/classes/com/perfectworldprogramming/mobile/orm/exception/DataAccessException.class and b/bin/classes/com/perfectworldprogramming/mobile/orm/exception/DataAccessException.class differ diff --git a/bin/classes/com/perfectworldprogramming/mobile/orm/exception/EmptySQLStatementException.class b/bin/classes/com/perfectworldprogramming/mobile/orm/exception/EmptySQLStatementException.class index e0b2e23..98f3716 100644 Binary files a/bin/classes/com/perfectworldprogramming/mobile/orm/exception/EmptySQLStatementException.class and b/bin/classes/com/perfectworldprogramming/mobile/orm/exception/EmptySQLStatementException.class differ diff --git a/bin/classes/com/perfectworldprogramming/mobile/orm/exception/ExtraResultsException.class b/bin/classes/com/perfectworldprogramming/mobile/orm/exception/ExtraResultsException.class index 23b98bf..cd8b9a1 100644 Binary files a/bin/classes/com/perfectworldprogramming/mobile/orm/exception/ExtraResultsException.class and b/bin/classes/com/perfectworldprogramming/mobile/orm/exception/ExtraResultsException.class differ diff --git a/bin/classes/com/perfectworldprogramming/mobile/orm/exception/InvalidCursorExtractorException.class b/bin/classes/com/perfectworldprogramming/mobile/orm/exception/InvalidCursorExtractorException.class index 1447658..d5c7e71 100644 Binary files a/bin/classes/com/perfectworldprogramming/mobile/orm/exception/InvalidCursorExtractorException.class and b/bin/classes/com/perfectworldprogramming/mobile/orm/exception/InvalidCursorExtractorException.class differ diff --git a/bin/classes/com/perfectworldprogramming/mobile/orm/exception/InvalidQueryTypeException.class b/bin/classes/com/perfectworldprogramming/mobile/orm/exception/InvalidQueryTypeException.class index 7ce270a..fb9eda6 100644 Binary files a/bin/classes/com/perfectworldprogramming/mobile/orm/exception/InvalidQueryTypeException.class and b/bin/classes/com/perfectworldprogramming/mobile/orm/exception/InvalidQueryTypeException.class differ diff --git a/bin/classes/com/perfectworldprogramming/mobile/orm/exception/NoPrimaryKeyFieldException.class b/bin/classes/com/perfectworldprogramming/mobile/orm/exception/NoPrimaryKeyFieldException.class index 0ceb533..37ead19 100644 Binary files a/bin/classes/com/perfectworldprogramming/mobile/orm/exception/NoPrimaryKeyFieldException.class and b/bin/classes/com/perfectworldprogramming/mobile/orm/exception/NoPrimaryKeyFieldException.class differ diff --git a/bin/classes/com/perfectworldprogramming/mobile/orm/exception/NoRowsReturnedException.class b/bin/classes/com/perfectworldprogramming/mobile/orm/exception/NoRowsReturnedException.class index 0329342..40fbb9d 100644 Binary files a/bin/classes/com/perfectworldprogramming/mobile/orm/exception/NoRowsReturnedException.class and b/bin/classes/com/perfectworldprogramming/mobile/orm/exception/NoRowsReturnedException.class differ diff --git a/bin/classes/com/perfectworldprogramming/mobile/orm/helper/AndroidSQLLiteOpenHelper.class b/bin/classes/com/perfectworldprogramming/mobile/orm/helper/AndroidSQLLiteOpenHelper.class index 056708b..166bccf 100644 Binary files a/bin/classes/com/perfectworldprogramming/mobile/orm/helper/AndroidSQLLiteOpenHelper.class and b/bin/classes/com/perfectworldprogramming/mobile/orm/helper/AndroidSQLLiteOpenHelper.class differ diff --git a/bin/classes/com/perfectworldprogramming/mobile/orm/helper/DBHelper.class b/bin/classes/com/perfectworldprogramming/mobile/orm/helper/DBHelper.class index ba7703e..aa73cc5 100644 Binary files a/bin/classes/com/perfectworldprogramming/mobile/orm/helper/DBHelper.class and b/bin/classes/com/perfectworldprogramming/mobile/orm/helper/DBHelper.class differ diff --git a/bin/classes/com/perfectworldprogramming/mobile/orm/reflection/DomainClassAnalyzer.class b/bin/classes/com/perfectworldprogramming/mobile/orm/reflection/DomainClassAnalyzer.class index ce4c49c..f3daab2 100644 Binary files a/bin/classes/com/perfectworldprogramming/mobile/orm/reflection/DomainClassAnalyzer.class and b/bin/classes/com/perfectworldprogramming/mobile/orm/reflection/DomainClassAnalyzer.class differ diff --git a/bin/classes/com/perfectworldprogramming/mobile/orm/test/AndroidSQLiteTemplateTests.class b/bin/classes/com/perfectworldprogramming/mobile/orm/test/AndroidSQLiteTemplateTests.class index 7ac727a..3c60ce8 100644 Binary files a/bin/classes/com/perfectworldprogramming/mobile/orm/test/AndroidSQLiteTemplateTests.class and b/bin/classes/com/perfectworldprogramming/mobile/orm/test/AndroidSQLiteTemplateTests.class differ diff --git a/bin/classes/com/perfectworldprogramming/mobile/orm/test/creator/TableCreatorTest.class b/bin/classes/com/perfectworldprogramming/mobile/orm/test/creator/TableCreatorTest.class index d0a7454..76378b2 100644 Binary files a/bin/classes/com/perfectworldprogramming/mobile/orm/test/creator/TableCreatorTest.class and b/bin/classes/com/perfectworldprogramming/mobile/orm/test/creator/TableCreatorTest.class differ diff --git a/bin/classes/com/perfectworldprogramming/mobile/orm/test/domain/Account.class b/bin/classes/com/perfectworldprogramming/mobile/orm/test/domain/Account.class index a014621..4b2ae44 100644 Binary files a/bin/classes/com/perfectworldprogramming/mobile/orm/test/domain/Account.class and b/bin/classes/com/perfectworldprogramming/mobile/orm/test/domain/Account.class differ diff --git a/bin/classes/com/perfectworldprogramming/mobile/orm/test/domain/Address.class b/bin/classes/com/perfectworldprogramming/mobile/orm/test/domain/Address.class index 48cc6f8..1b16f1c 100644 Binary files a/bin/classes/com/perfectworldprogramming/mobile/orm/test/domain/Address.class and b/bin/classes/com/perfectworldprogramming/mobile/orm/test/domain/Address.class differ diff --git a/bin/classes/com/perfectworldprogramming/mobile/orm/test/domain/NoPKDomain.class b/bin/classes/com/perfectworldprogramming/mobile/orm/test/domain/NoPKDomain.class index 2ec7601..1cd901f 100644 Binary files a/bin/classes/com/perfectworldprogramming/mobile/orm/test/domain/NoPKDomain.class and b/bin/classes/com/perfectworldprogramming/mobile/orm/test/domain/NoPKDomain.class differ diff --git a/bin/classes/com/perfectworldprogramming/mobile/orm/test/domain/Person.class b/bin/classes/com/perfectworldprogramming/mobile/orm/test/domain/Person.class index 2358e41..d3c29f5 100644 Binary files a/bin/classes/com/perfectworldprogramming/mobile/orm/test/domain/Person.class and b/bin/classes/com/perfectworldprogramming/mobile/orm/test/domain/Person.class differ diff --git a/bin/classes/com/perfectworldprogramming/mobile/orm/test/helper/AndroidSQLLiteOpenHelperTest.class b/bin/classes/com/perfectworldprogramming/mobile/orm/test/helper/AndroidSQLLiteOpenHelperTest.class index 4d1b015..168cdae 100644 Binary files a/bin/classes/com/perfectworldprogramming/mobile/orm/test/helper/AndroidSQLLiteOpenHelperTest.class and b/bin/classes/com/perfectworldprogramming/mobile/orm/test/helper/AndroidSQLLiteOpenHelperTest.class differ diff --git a/bin/classes/com/perfectworldprogramming/mobile/orm/test/helper/DBHelperTests.class b/bin/classes/com/perfectworldprogramming/mobile/orm/test/helper/DBHelperTests.class index d3a5819..9cbeba7 100644 Binary files a/bin/classes/com/perfectworldprogramming/mobile/orm/test/helper/DBHelperTests.class and b/bin/classes/com/perfectworldprogramming/mobile/orm/test/helper/DBHelperTests.class differ diff --git a/bin/classes/com/perfectworldprogramming/mobile/orm/test/interfaces/AddressCursorExtractor.class b/bin/classes/com/perfectworldprogramming/mobile/orm/test/interfaces/AddressCursorExtractor.class index 4fb6508..1649de5 100644 Binary files a/bin/classes/com/perfectworldprogramming/mobile/orm/test/interfaces/AddressCursorExtractor.class and b/bin/classes/com/perfectworldprogramming/mobile/orm/test/interfaces/AddressCursorExtractor.class differ diff --git a/bin/classes/com/perfectworldprogramming/mobile/orm/test/interfaces/AddressCursorRowMapper.class b/bin/classes/com/perfectworldprogramming/mobile/orm/test/interfaces/AddressCursorRowMapper.class index 2e78a93..1659535 100644 Binary files a/bin/classes/com/perfectworldprogramming/mobile/orm/test/interfaces/AddressCursorRowMapper.class and b/bin/classes/com/perfectworldprogramming/mobile/orm/test/interfaces/AddressCursorRowMapper.class differ diff --git a/bin/classes/com/perfectworldprogramming/mobile/orm/test/interfaces/CursorExtractorTests.class b/bin/classes/com/perfectworldprogramming/mobile/orm/test/interfaces/CursorExtractorTests.class index 12123b1..78a372e 100644 Binary files a/bin/classes/com/perfectworldprogramming/mobile/orm/test/interfaces/CursorExtractorTests.class and b/bin/classes/com/perfectworldprogramming/mobile/orm/test/interfaces/CursorExtractorTests.class differ diff --git a/bin/classes/com/perfectworldprogramming/mobile/orm/test/interfaces/CursorRowMapperTests.class b/bin/classes/com/perfectworldprogramming/mobile/orm/test/interfaces/CursorRowMapperTests.class index ce0b1f7..2bccf2c 100644 Binary files a/bin/classes/com/perfectworldprogramming/mobile/orm/test/interfaces/CursorRowMapperTests.class and b/bin/classes/com/perfectworldprogramming/mobile/orm/test/interfaces/CursorRowMapperTests.class differ diff --git a/bin/classes/com/perfectworldprogramming/mobile/orm/test/interfaces/PersonCursorExtractor.class b/bin/classes/com/perfectworldprogramming/mobile/orm/test/interfaces/PersonCursorExtractor.class index 40cadfc..b3d6478 100644 Binary files a/bin/classes/com/perfectworldprogramming/mobile/orm/test/interfaces/PersonCursorExtractor.class and b/bin/classes/com/perfectworldprogramming/mobile/orm/test/interfaces/PersonCursorExtractor.class differ diff --git a/bin/classes/com/perfectworldprogramming/mobile/orm/test/interfaces/PersonCursorRowMapper.class b/bin/classes/com/perfectworldprogramming/mobile/orm/test/interfaces/PersonCursorRowMapper.class index 7e9b920..a71c94e 100644 Binary files a/bin/classes/com/perfectworldprogramming/mobile/orm/test/interfaces/PersonCursorRowMapper.class and b/bin/classes/com/perfectworldprogramming/mobile/orm/test/interfaces/PersonCursorRowMapper.class differ diff --git a/bin/classes/com/perfectworldprogramming/mobile/orm/test/interfaces/SampleDataHelper.class b/bin/classes/com/perfectworldprogramming/mobile/orm/test/interfaces/SampleDataHelper.class index 63a6295..a429b30 100644 Binary files a/bin/classes/com/perfectworldprogramming/mobile/orm/test/interfaces/SampleDataHelper.class and b/bin/classes/com/perfectworldprogramming/mobile/orm/test/interfaces/SampleDataHelper.class differ diff --git a/bin/classes/com/perfectworldprogramming/mobile/orm/test/reflection/DomainClassAnalyzerTests.class b/bin/classes/com/perfectworldprogramming/mobile/orm/test/reflection/DomainClassAnalyzerTests.class index 46356f2..fe768bd 100644 Binary files a/bin/classes/com/perfectworldprogramming/mobile/orm/test/reflection/DomainClassAnalyzerTests.class and b/bin/classes/com/perfectworldprogramming/mobile/orm/test/reflection/DomainClassAnalyzerTests.class differ diff --git a/bin/res/drawable-hdpi/icon.png b/bin/res/drawable-hdpi/icon.png deleted file mode 100644 index 882eb14..0000000 Binary files a/bin/res/drawable-hdpi/icon.png and /dev/null differ diff --git a/bin/res/drawable-ldpi/icon.png b/bin/res/drawable-ldpi/icon.png deleted file mode 100644 index 18689f6..0000000 Binary files a/bin/res/drawable-ldpi/icon.png and /dev/null differ diff --git a/bin/res/drawable-mdpi/icon.png b/bin/res/drawable-mdpi/icon.png deleted file mode 100644 index 02e96b9..0000000 Binary files a/bin/res/drawable-mdpi/icon.png and /dev/null differ diff --git a/bin/resources.ap_ b/bin/resources.ap_ index 2fcd961..5fd7219 100644 Binary files a/bin/resources.ap_ and b/bin/resources.ap_ differ diff --git a/gen/com/perfectworldprogramming/mobile/orm/R.java b/gen/com/perfectworldprogramming/mobile/orm/R.java index 3e1d7d0..b053f26 100644 --- a/gen/com/perfectworldprogramming/mobile/orm/R.java +++ b/gen/com/perfectworldprogramming/mobile/orm/R.java @@ -1,23 +1,23 @@ -/* AUTO-GENERATED FILE. DO NOT MODIFY. - * - * This class was automatically generated by the - * aapt tool from the resource data it found. It - * should not be modified by hand. - */ - -package com.perfectworldprogramming.mobile.orm; - -public final class R { - public static final class attr { - } - public static final class drawable { - public static final int icon=0x7f020000; - } - public static final class layout { - public static final int main=0x7f030000; - } - public static final class string { - public static final int app_name=0x7f040001; - public static final int hello=0x7f040000; - } -} +/* AUTO-GENERATED FILE. DO NOT MODIFY. + * + * This class was automatically generated by the + * aapt tool from the resource data it found. It + * should not be modified by hand. + */ + +package com.perfectworldprogramming.mobile.orm; + +public final class R { + public static final class attr { + } + public static final class drawable { + public static final int icon=0x7f020000; + } + public static final class layout { + public static final int main=0x7f030000; + } + public static final class string { + public static final int app_name=0x7f040001; + public static final int hello=0x7f040000; + } +} diff --git a/proguard.cfg b/proguard.cfg index 12dd039..4dc32b1 100644 --- a/proguard.cfg +++ b/proguard.cfg @@ -14,15 +14,15 @@ -keep public class * extends android.preference.Preference -keep public class com.android.vending.licensing.ILicensingService --keepclasseswithmembernames class * { +-keepclasseswithmembers class * { native ; } --keepclasseswithmembernames class * { +-keepclasseswithmembers class * { public (android.content.Context, android.util.AttributeSet); } --keepclasseswithmembernames class * { +-keepclasseswithmembers class * { public (android.content.Context, android.util.AttributeSet, int); } diff --git a/src/com/perfectworldprogramming/mobile/orm/AndroidSQLiteTemplate.java b/src/com/perfectworldprogramming/mobile/orm/AndroidSQLiteTemplate.java index 1a39051..ab7b457 100644 --- a/src/com/perfectworldprogramming/mobile/orm/AndroidSQLiteTemplate.java +++ b/src/com/perfectworldprogramming/mobile/orm/AndroidSQLiteTemplate.java @@ -2,14 +2,6 @@ import java.util.List; -import com.perfectworldprogramming.mobile.orm.exception.DataAccessException; -import com.perfectworldprogramming.mobile.orm.exception.EmptySQLStatementException; -import com.perfectworldprogramming.mobile.orm.exception.NoRowsReturnedException; -import com.perfectworldprogramming.mobile.orm.interfaces.CursorExtractor; -import com.perfectworldprogramming.mobile.orm.interfaces.CursorRowMapper; -import com.perfectworldprogramming.mobile.orm.interfaces.JdbcOperations; -import com.perfectworldprogramming.mobile.orm.reflection.DomainClassAnalyzer; - import android.content.ContentValues; import android.database.Cursor; import android.database.SQLException; @@ -18,310 +10,353 @@ import android.database.sqlite.SQLiteException; import android.database.sqlite.SQLiteStatement; +import com.perfectworldprogramming.mobile.orm.exception.DataAccessException; +import com.perfectworldprogramming.mobile.orm.exception.EmptySQLStatementException; +import com.perfectworldprogramming.mobile.orm.exception.NoRowsReturnedException; +import com.perfectworldprogramming.mobile.orm.interfaces.CursorExtractor; +import com.perfectworldprogramming.mobile.orm.interfaces.CursorRowMapper; +import com.perfectworldprogramming.mobile.orm.interfaces.JdbcOperations; +import com.perfectworldprogramming.mobile.orm.reflection.DomainClassAnalyzer; + /** - * For queries that return Cursors will use the CursorAdapter class to convert the Cursor into your Domain object, either via - * Annotations you put in your domain objects to map to the tables, or via CursorRowMapper or CursorExtractor implementations. - * Either approach, the CursorAdapter will close the Cursor after it has completed conversion. No need to close Cursors yourself. + * For queries that return Cursors will use the CursorAdapter class to convert + * the Cursor into your Domain object, either via Annotations you put in your + * domain objects to map to the tables, or via CursorRowMapper or + * CursorExtractor implementations. Either approach, the CursorAdapter will + * close the Cursor after it has completed conversion. No need to close Cursors + * yourself. * - * User: Mark Spritzler - * Date: 3/10/11 - * Time: 10:07 PM + * User: Mark Spritzler Date: 3/10/11 Time: 10:07 PM */ public class AndroidSQLiteTemplate implements JdbcOperations { - private SQLiteDatabase sqLiteDatabase; - private DomainClassAnalyzer domainClassAnalyzer = new DomainClassAnalyzer(); - private CursorAdapter cursorAdapter = new CursorAdapter(); + private SQLiteDatabase sqLiteDatabase; + protected final DomainClassAnalyzer domainClassAnalyzer = new DomainClassAnalyzer(); + private final CursorAdapter cursorAdapter = new CursorAdapter(); - public AndroidSQLiteTemplate(SQLiteDatabase sqLiteDatabase) { - this.sqLiteDatabase = sqLiteDatabase; - } + public AndroidSQLiteTemplate(SQLiteDatabase sqLiteDatabase) { + this.sqLiteDatabase = sqLiteDatabase; + } - @Override - public long insert(Object object) { - ContentValues values = domainClassAnalyzer.createContentValues(object); - long id = sqLiteDatabase.insert(object.getClass().getSimpleName(), - null, values); - // the insert method used above doesn't through an exception if it can't insert, but returns -1 as the result. - if (id != -1) { - domainClassAnalyzer.setIdToNewObject(object, id); - } else { - throw new DataAccessException("Unable to insert new record for object: " + object); - } - return id; - } + @Override + public long insert(Object object) { + ContentValues values = domainClassAnalyzer.createContentValues(object); + long id = sqLiteDatabase.insert(object.getClass().getSimpleName(), + null, values); + // the insert method used above doesn't throw an exception if it can't + // insert, but returns -1 as the result. + if (id != -1) { + domainClassAnalyzer.setIdToNewObject(object, id); + } else { + throw new DataAccessException( + "Unable to insert new record for object: " + object); + } + return id; + } - @Override - public long insert(String sql, Object... args) { - sql = replaceParametersInStatement(sql, args); - SQLiteStatement statement = null; - Long results; - try { - statement = sqLiteDatabase.compileStatement(sql); - results = statement.executeInsert(); - } catch (SQLException se) { - throw new DataAccessException(se.getMessage()); - } finally { - if (statement != null) { - statement.close(); - } - } - return results; - } + @Override + public long insert(String sql, Object... args) { + sql = replaceParametersInStatement(sql, args); + SQLiteStatement statement = null; + Long results; + try { + statement = sqLiteDatabase.compileStatement(sql); + results = statement.executeInsert(); + } catch (SQLException se) { + throw new DataAccessException(se.getMessage()); + } finally { + if (statement != null) { + statement.close(); + } + } + return results; + } - @Override - public long update(Object object) { - ContentValues values = domainClassAnalyzer.createContentValues(object); - String whereClause = domainClassAnalyzer.getPrimaryKeyFieldName(object.getClass()) + " = ?"; - String[] whereArgs = {String.valueOf(domainClassAnalyzer.getIdFromObject(object))}; + @Override + public long update(Object object) { + ContentValues values = domainClassAnalyzer.createContentValues(object); + String whereClause = domainClassAnalyzer.getPrimaryKeyFieldName(object + .getClass()) + " = ?"; + String[] whereArgs = { String.valueOf(domainClassAnalyzer + .getIdFromObject(object)) }; - return sqLiteDatabase.update(object.getClass().getSimpleName(), - values, whereClause, whereArgs); - } + return sqLiteDatabase.update(object.getClass().getSimpleName(), values, + whereClause, whereArgs); + } - @Override - public void update(String sql, Object... args) { - sql = replaceParametersInStatement(sql, args); - SQLiteStatement statement = null; - try { - statement = sqLiteDatabase.compileStatement(sql); - statement.execute(); - } catch (SQLException se) { - throw new DataAccessException(se.getMessage()); - } finally { - if (statement != null) { - statement.close(); - } - } - } + @Override + public void update(String sql, Object... args) { + sql = replaceParametersInStatement(sql, args); + SQLiteStatement statement = null; + try { + statement = sqLiteDatabase.compileStatement(sql); + statement.execute(); + } catch (SQLException se) { + throw new DataAccessException(se.getMessage()); + } finally { + if (statement != null) { + statement.close(); + } + } + } - @Override - public long delete(Object object) { - if (object == null) { - throw new DataAccessException("Please supply a non null domain object to delete(Object) method"); - } - String whereClause = domainClassAnalyzer.getPrimaryKeyFieldName(object.getClass()) + " = ?"; - String[] whereArgs = {String.valueOf(domainClassAnalyzer.getIdFromObject(object))}; - long results = -1; - try { - results = sqLiteDatabase.delete(object.getClass().getSimpleName(), - whereClause, whereArgs); - if (results == 0) { - throw new DataAccessException("unable to delete " + object.getClass().getName() + ". Most likely the Primary Key id value does not exist in the database."); - } - } catch (SQLiteException sle) { - - } - return results; - } + @Override + public long delete(Object object) { + if (object == null) { + throw new DataAccessException( + "Please supply a non null domain object to delete(Object) method"); + } + String whereClause = domainClassAnalyzer.getPrimaryKeyFieldName(object + .getClass()) + " = ?"; + String[] whereArgs = { String.valueOf(domainClassAnalyzer + .getIdFromObject(object)) }; + long results = -1; + try { + results = sqLiteDatabase.delete(object.getClass().getSimpleName(), + whereClause, whereArgs); + if (results == 0) { + throw new DataAccessException( + "unable to delete " + + object.getClass().getName() + + ". Most likely the Primary Key id value does not exist in the database."); + } + } catch (SQLiteException sle) { - @Override - public long delete(String table, String columnName, String columnValue) { - if (table == null || columnName == null || columnValue == null || "".equals(table)|| "".equals(columnName)|| "".equals(table)) { - throw new DataAccessException("Please supply tableName, columnName and columnValue in order to delete any data"); - } - String[] args = {columnValue}; - long results = -1; - try { - results = sqLiteDatabase.delete(table, "" + columnName + "=?", args); - } catch (SQLiteException sle) { - throw new DataAccessException("Call to delete(String, String, String) failed. Please check you are passing in correct tableName, columnName and value to this method"); - } - return results; - } - - @Override - public void delete(String sql, Object... args) { - update(sql, args); - } + } + return results; + } - @Override - public int queryForInt(String sql, Object... args) { - return (int)queryForLong(sql, args); - } + @Override + public long delete(String table, String columnName, String columnValue) { + if (table == null || columnName == null || columnValue == null + || "".equals(table) || "".equals(columnName) + || "".equals(table)) { + throw new DataAccessException( + "Please supply tableName, columnName and columnValue in order to delete any data"); + } + String[] args = { columnValue }; + long results = -1; + try { + results = sqLiteDatabase + .delete(table, "" + columnName + "=?", args); + } catch (SQLiteException sle) { + throw new DataAccessException( + "Call to delete(String, String, String) failed. Please check you are passing in correct tableName, columnName and value to this method"); + } + return results; + } - @Override - public long queryForLong(String sql, Object... args) { - sql = replaceParametersInStatement(sql, args); - SQLiteStatement statement = null; - long results; - try { - statement = sqLiteDatabase.compileStatement(sql); - results = statement.simpleQueryForLong(); - } catch (SQLiteDoneException se) { - throw new NoRowsReturnedException("Query For Long/Int did not return any rows for sql: " + sql); - } finally { - if (statement != null) { - statement.close(); - } - } - return results; - } + @Override + public void delete(String sql, Object... args) { + update(sql, args); + } - @Override - public String queryForString(String sql, Object... args) { - sql = replaceParametersInStatement(sql, args); - SQLiteStatement statement = null; - String results; - try { - statement = sqLiteDatabase.compileStatement(sql); - results = statement.simpleQueryForString(); - } catch (SQLiteDoneException se) { - throw new NoRowsReturnedException("Query For String did not return any rows for sql: " + sql); - } catch (SQLException e) { - throw new DataAccessException(e.getMessage()); - } finally { - if (statement != null) { - statement.close(); - } - } - return results; - } - - //** All Implementations below call their corresponding executeFor* private methods for a more central exception handling - @Override - public T queryForObject(String sql, CursorRowMapper cursorRowMapper, Object... args) { - if (cursorRowMapper == null) { - throw new IllegalArgumentException(); - } - sql = replaceParametersInStatement(sql, args); - return executeForSingleObject(sql, cursorRowMapper); - } + @Override + public int queryForInt(String sql, Object... args) { + return (int) queryForLong(sql, args); + } - @Override - public T queryForObject(String sql, Class clazz, Object... args) { - sql = replaceParametersInStatement(sql, args); - T t = executeForSingleObject(sql, clazz); - if (t == null) { - throw new DataAccessException("QueryForObject returned no results for query: " + sql); - } - return t; - } + @Override + public long queryForLong(String sql, Object... args) { + sql = replaceParametersInStatement(sql, args); + SQLiteStatement statement = null; + long results; + try { + statement = sqLiteDatabase.compileStatement(sql); + results = statement.simpleQueryForLong(); + } catch (SQLiteDoneException se) { + throw new NoRowsReturnedException( + "Query For Long/Int did not return any rows for sql: " + + sql); + } finally { + if (statement != null) { + statement.close(); + } + } + return results; + } - @Override - public T queryForObject(String sql, CursorExtractor cursorExtractor, Object... args) { - if (cursorExtractor == null) { - throw new IllegalArgumentException(); - } - sql = replaceParametersInStatement(sql, args); - return executeForSingleObject(sql, cursorExtractor); - } + @Override + public String queryForString(String sql, Object... args) { + sql = replaceParametersInStatement(sql, args); + SQLiteStatement statement = null; + String results; + try { + statement = sqLiteDatabase.compileStatement(sql); + results = statement.simpleQueryForString(); + } catch (SQLiteDoneException se) { + throw new NoRowsReturnedException( + "Query For String did not return any rows for sql: " + sql); + } catch (SQLException e) { + throw new DataAccessException(e.getMessage()); + } finally { + if (statement != null) { + statement.close(); + } + } + return results; + } - @Override - public T findById(Class clazz, Long id) { - String sql = generateStatementById(clazz, id); - return executeForSingleObject(sql, clazz); - } + // ** All Implementations below call their corresponding executeFor* private + // methods for a more central exception handling + @Override + public T queryForObject(String sql, CursorRowMapper cursorRowMapper, + Object... args) { + if (cursorRowMapper == null) { + throw new IllegalArgumentException(); + } + sql = replaceParametersInStatement(sql, args); + return executeForSingleObject(sql, cursorRowMapper); + } - @Override - public List query(String sql, Class clazz, Object... args) { - sql = replaceParametersInStatement(sql, args); - return executeForList(sql, clazz); - } + @Override + public T queryForObject(String sql, Class clazz, Object... args) { + sql = replaceParametersInStatement(sql, args); + T t = executeForSingleObject(sql, clazz); + if (t == null) { + throw new DataAccessException( + "QueryForObject returned no results for query: " + sql); + } + return t; + } - @Override - public List query(String sql, CursorRowMapper cursorRowMapper, Object... args) { - if (cursorRowMapper == null) { - throw new IllegalArgumentException(); - } - sql = replaceParametersInStatement(sql, args); - return executeForList(sql, cursorRowMapper); - } - - private T executeForSingleObject(String sql, CursorExtractor cursorExtractor) { - if (sql == null || "".equals(sql)) { - throw new EmptySQLStatementException(); - } - - if (cursorExtractor == null) { - throw new IllegalArgumentException(); - } - - Cursor cursor = null; - try { - cursor = sqLiteDatabase.rawQuery(sql, null); - return cursorAdapter.adaptFromCursor(cursor, cursorExtractor); - } catch (SQLiteException se) { - throw new DataAccessException(se.getMessage()); - } - } - - private T executeForSingleObject(String sql, CursorRowMapper cursorRowMapper) { - if (cursorRowMapper == null) { - throw new IllegalArgumentException(); - } - Cursor cursor = null; - try { - cursor = sqLiteDatabase.rawQuery(sql, null); - return cursorAdapter.adaptFromCursor(cursor, cursorRowMapper); - } catch (SQLiteException se) { - throw new DataAccessException(se.getMessage()); - } - } + @Override + public T queryForObject(String sql, CursorExtractor cursorExtractor, + Object... args) { + if (cursorExtractor == null) { + throw new IllegalArgumentException(); + } + sql = replaceParametersInStatement(sql, args); + return executeForSingleObject(sql, cursorExtractor); + } - private T executeForSingleObject(String sql, Class clazz) { - Cursor cursor = null; - try { - cursor = sqLiteDatabase.rawQuery(sql, null); - return cursorAdapter.adaptFromCursor(cursor, clazz); - } catch (SQLiteException se) { - throw new DataAccessException(se.getMessage()); - } - } - - private List executeForList(String sql, CursorRowMapper cursorRowMapper) { - if (cursorRowMapper == null) { - throw new IllegalArgumentException(); - } - Cursor cursor = null; - try { - cursor = sqLiteDatabase.rawQuery(sql, null); - return cursorAdapter.adaptListFromCursor(cursor, cursorRowMapper); - } catch (SQLiteException se) { - throw new DataAccessException(se.getMessage()); - } catch (Exception e) { - throw new DataAccessException("Unable to execute query. Please check your sql for incorrect sql grammer."); - } - } + @Override + public T findById(Class clazz, Long id) { + String sql = generateStatementById(clazz, id); + return executeForSingleObject(sql, clazz); + } - private List executeForList(String sql, Class clazz) { - Cursor cursor = null; - try { - cursor = sqLiteDatabase.rawQuery(sql, null); - return cursorAdapter.adaptListFromCursor(cursor, clazz); - } catch (SQLiteException se) { - throw new DataAccessException(se.getMessage()); - } - } + @Override + public List query(String sql, Class clazz, Object... args) { + sql = replaceParametersInStatement(sql, args); + return executeForList(sql, clazz); + } - private String generateStatementById(Class clazz, Long id) { - StringBuilder selectStatement = new StringBuilder("SELECT * FROM "); - selectStatement.append(clazz.getSimpleName()); - selectStatement.append(" WHERE "); - selectStatement.append(domainClassAnalyzer.getPrimaryKeyFieldName(clazz)); - selectStatement.append(" = "); - selectStatement.append(id); - - return selectStatement.toString(); + @Override + public List query(String sql, CursorRowMapper cursorRowMapper, + Object... args) { + if (cursorRowMapper == null) { + throw new IllegalArgumentException(); + } + sql = replaceParametersInStatement(sql, args); + return executeForList(sql, cursorRowMapper); + } + + public Object mapQueryParameter(Object value, Class clazz, String columnName) + { + return this.domainClassAnalyzer.mapQueryParameterByColumnName(value, clazz, columnName); } - private String replaceParametersInStatement(String sql, Object... args) { - if (sql == null || "".equals(sql)) { - throw new EmptySQLStatementException(); - } - - if (args != null) { - for (Object arg: args) { - if (arg != null) { - String replaceMe = "[/?]"; - sql = sql.replaceFirst(replaceMe, arg.toString()); - } - } - } - if (sql.contains("?")) { - throw new DataAccessException("Not all Query parameters have been set, please set all values for query to execute"); + private T executeForSingleObject(String sql, + CursorExtractor cursorExtractor) { + if (sql == null || "".equals(sql)) { + throw new EmptySQLStatementException(); } - - return sql; - } + + if (cursorExtractor == null) { + throw new IllegalArgumentException(); + } + + Cursor cursor = null; + try { + cursor = sqLiteDatabase.rawQuery(sql, null); + return cursorAdapter.adaptFromCursor(cursor, cursorExtractor); + } catch (SQLiteException se) { + throw new DataAccessException(se.getMessage()); + } + } + + private T executeForSingleObject(String sql, + CursorRowMapper cursorRowMapper) { + if (cursorRowMapper == null) { + throw new IllegalArgumentException(); + } + Cursor cursor = null; + try { + cursor = sqLiteDatabase.rawQuery(sql, null); + return cursorAdapter.adaptFromCursor(cursor, cursorRowMapper); + } catch (SQLiteException se) { + throw new DataAccessException(se.getMessage()); + } + } + + private T executeForSingleObject(String sql, Class clazz) { + Cursor cursor = null; + try { + cursor = sqLiteDatabase.rawQuery(sql, null); + return cursorAdapter.adaptFromCursor(cursor, clazz); + } catch (SQLiteException se) { + throw new DataAccessException(se.getMessage()); + } + } + + private List executeForList(String sql, + CursorRowMapper cursorRowMapper) { + if (cursorRowMapper == null) { + throw new IllegalArgumentException(); + } + Cursor cursor = null; + try { + cursor = sqLiteDatabase.rawQuery(sql, null); + return cursorAdapter.adaptListFromCursor(cursor, cursorRowMapper); + } catch (SQLiteException se) { + throw new DataAccessException(se.getMessage()); + } catch (Exception e) { + throw new DataAccessException( + "Unable to execute query. Please check your sql for incorrect sql grammer."); + } + } + + private List executeForList(String sql, Class clazz) { + Cursor cursor = null; + try { + cursor = sqLiteDatabase.rawQuery(sql, null); + return cursorAdapter.adaptListFromCursor(cursor, clazz); + } catch (SQLiteException se) { + throw new DataAccessException(se.getMessage()); + } + } + + private String generateStatementById(Class clazz, Long id) { + StringBuilder selectStatement = new StringBuilder("SELECT * FROM "); + selectStatement.append(clazz.getSimpleName()); + selectStatement.append(" WHERE "); + selectStatement.append(domainClassAnalyzer + .getPrimaryKeyFieldName(clazz)); + selectStatement.append(" = "); + selectStatement.append(id); + + return selectStatement.toString(); + } + + private String replaceParametersInStatement(String sql, Object... args) { + if (sql == null || "".equals(sql)) { + throw new EmptySQLStatementException(); + } + + if (args != null) { + for (Object arg : args) { + if (arg != null) { + String replaceMe = "[/?]"; + sql = sql.replaceFirst(replaceMe, arg.toString()); + } + } + } + if (sql.contains("?")) { + throw new DataAccessException( + "Not all Query parameters have been set, please set all values for query to execute"); + } + + return sql; + } } diff --git a/src/com/perfectworldprogramming/mobile/orm/CursorAdapter.java b/src/com/perfectworldprogramming/mobile/orm/CursorAdapter.java index 65c599a..b3c0bc8 100644 --- a/src/com/perfectworldprogramming/mobile/orm/CursorAdapter.java +++ b/src/com/perfectworldprogramming/mobile/orm/CursorAdapter.java @@ -1,21 +1,24 @@ package com.perfectworldprogramming.mobile.orm; import java.lang.reflect.Field; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; +import android.database.Cursor; + import com.perfectworldprogramming.mobile.orm.annotations.Column; import com.perfectworldprogramming.mobile.orm.annotations.ForeignKey; import com.perfectworldprogramming.mobile.orm.annotations.PrimaryKey; import com.perfectworldprogramming.mobile.orm.annotations.Transient; import com.perfectworldprogramming.mobile.orm.exception.DataAccessException; import com.perfectworldprogramming.mobile.orm.exception.ExtraResultsException; -import com.perfectworldprogramming.mobile.orm.exception.InvalidCursorExtractorException; +import com.perfectworldprogramming.mobile.orm.exception.InvalidCursorException; import com.perfectworldprogramming.mobile.orm.exception.InvalidCursorRowMapperException; +import com.perfectworldprogramming.mobile.orm.interfaces.ColumnTypeMapper; import com.perfectworldprogramming.mobile.orm.interfaces.CursorExtractor; import com.perfectworldprogramming.mobile.orm.interfaces.CursorRowMapper; - -import android.database.Cursor; +import com.perfectworldprogramming.mobile.orm.reflection.PrimaryKeyMapper; /** * User: Mark Spritzler @@ -34,10 +37,14 @@ public class CursorAdapter { * @return List returns a list of populated domain objects based on the cursor data. */ public List adaptListFromCursor(Cursor cursor, Class clazz) { - List results = new ArrayList(); - if (cursor.getCount() > 0) { - results = getValuesFromCursor(cursor, clazz); - cursor.close(); + List results = new ArrayList(); + if (cursor.getCount() > 0) { + try { + results = getValuesFromCursor(cursor, clazz); + } + finally { + cursor.close(); + } } return results; } @@ -56,63 +63,88 @@ public List adaptListFromCursor(Cursor cursor, CursorRowMapper cursorR List values = new ArrayList(); if (cursor != null) { if (cursor.moveToFirst()) { - do { + do { try { - T newInstance = cursorRowMapper.mapRow(cursor, cursor.getPosition()); + T newInstance = cursorRowMapper.mapRow(cursor, cursor.getPosition()); values.add(newInstance); - } catch (IllegalStateException ise) { - if (!cursor.isClosed()) { - cursor.close(); - } - throw new InvalidCursorRowMapperException(cursorRowMapper.getClass()); - } + } catch (IllegalStateException ise) { + if (!cursor.isClosed()) { + cursor.close(); + } + throw new InvalidCursorRowMapperException(cursorRowMapper.getClass()); + } } while (cursor.moveToNext()); } if (!cursor.isClosed()) { - cursor.close(); - } + cursor.close(); + } } return values; } /** * Returns a Single Domain Object from the Cursor's first row. All other rows will be ignored. + * Always moves to the first row and always closes the cursor. * @param cursor returned data from the database query. * @param clazz Domain object Class to put all the data from the cursor * @param Domain Object type of the Class * @return T domain object populated with data from the first row in the Cursor */ public T adaptFromCursor(Cursor cursor, Class clazz) { - T newInstance = null; + T newInstance = null; + if (cursor != null) { + try + { + if (cursor.moveToFirst()) { + newInstance = adaptCurrentFromCursor(cursor, clazz); + } + } + finally + { + cursor.close(); + } + } + return newInstance; + } + + /** + * Checks that the cursor is not {@code beforeFirst} or {@code afterLast} positions but does not move + * or close the cursor. + * @param cursor + * @param clazz + * @param willClose + * @return + */ + public T adaptCurrentFromCursor(Cursor cursor, Class clazz) { + T newInstance = null; if (cursor != null) { - if (cursor.moveToFirst()) { - newInstance = getSingleObjectValuesFromCursor(cursor, clazz); - cursor.close(); - } + if (!cursor.isBeforeFirst() && !cursor.isAfterLast()) { + newInstance = getSingleObjectValuesFromCursor(cursor, clazz); + } } return newInstance; } public T adaptFromCursor(Cursor cursor, CursorRowMapper cursorRowMapper) { - T newInstance = null; - if (cursor != null) { - if(cursor.getCount() != 1) { - throw new ExtraResultsException(cursor.getCount()); - } - - if (cursor.moveToFirst()) { - try { - newInstance = cursorRowMapper.mapRow(cursor, 1); - } catch (IllegalStateException ise) { - throw new InvalidCursorRowMapperException(cursorRowMapper.getClass()); - } finally { - if (!cursor.isClosed()) { - cursor.close(); - } - } - } - } - return newInstance; + T newInstance = null; + if (cursor != null) { + if(cursor.getCount() != 1) { + throw new ExtraResultsException(cursor.getCount()); + } + + if (cursor.moveToFirst()) { + try { + newInstance = cursorRowMapper.mapRow(cursor, 1); + } catch (IllegalStateException ise) { + throw new InvalidCursorRowMapperException(cursorRowMapper.getClass()); + } finally { + if (!cursor.isClosed()) { + cursor.close(); + } + } + } + } + return newInstance; } /** @@ -129,27 +161,31 @@ public T adaptFromCursor(Cursor cursor, CursorRowMapper cursorRowMapper) public T adaptFromCursor(Cursor cursor, CursorExtractor cursorExtractor) { T result = null; try { - result = cursorExtractor.extractData(cursor); + result = cursorExtractor.extractData(cursor); } catch (IllegalStateException ise) { - throw new InvalidCursorExtractorException(cursorExtractor.getClass()); + throw new InvalidCursorException(cursorExtractor.getClass()); } finally { - if (!cursor.isClosed()) { - cursor.close(); - } + if (!cursor.isClosed()) { + cursor.close(); + } } - return result; + return result; } private List getValuesFromCursor(Cursor cursor, Class clazz) { List values = new ArrayList(); if (cursor != null) { - if (cursor.moveToFirst()) { - do { - T newInstance = getSingleObjectValuesFromCursor(cursor, clazz); - values.add(newInstance); - } while (cursor.moveToNext()); + try { + if (cursor.moveToFirst()) { + do { + T newInstance = getSingleObjectValuesFromCursor(cursor, clazz); + values.add(newInstance); + } while (cursor.moveToNext()); + } + } + finally { + cursor.close(); } - cursor.close(); } return values; } @@ -160,11 +196,12 @@ private T getSingleObjectValuesFromCursor(Cursor cursor, Class clazz) { newInstance = clazz.newInstance(); setFieldValues(clazz, newInstance, cursor); } catch (InstantiationException e) { - e.printStackTrace(); + throw new RuntimeException("CursorAdapter", e); + //e.printStackTrace(); } catch (IllegalAccessException e) { - e.printStackTrace(); + throw new RuntimeException("CursorAdapter", e); + //e.printStackTrace(); } - return newInstance; } @@ -175,57 +212,32 @@ private void setFieldValues(Class clazz, T object, Cursor } } - private void setFieldValue(Field fieldToSet, T object, Cursor cursor) { - - //Skip over @Transient and @ForeignKey fields + private void setFieldValue(Field fieldToSet, T object, Cursor cursor) { + //Skip over @Transient, @ForeignKey and static fields if (fieldToSet.isAnnotationPresent(Transient.class) || - fieldToSet.isAnnotationPresent(ForeignKey.class)) { + fieldToSet.isAnnotationPresent(ForeignKey.class) + || ((fieldToSet.getModifiers()&Modifier.STATIC)!=0)) { return; - } - String columnName = getColumnName(fieldToSet); - - int columnIndex = cursor.getColumnIndex(columnName); - - fieldToSet.setAccessible(true); - Class clazz = fieldToSet.getType(); - Object value = getValue(clazz, cursor, columnIndex); - if (value != null) { - try { - fieldToSet.set(object, value); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } } - } - - private String getColumnName(Field field) { - String columnName; - Column column = field.getAnnotation(Column.class); - if (column == null) { - PrimaryKey key = field.getAnnotation(PrimaryKey.class); - columnName = key.value(); - } else { - columnName = column.value(); + final String value; + final ColumnTypeMapper mapper; + if (fieldToSet.isAnnotationPresent(PrimaryKey.class) ) { + mapper = PrimaryKeyMapper.INSTANCE; + value = fieldToSet.getAnnotation(PrimaryKey.class).value(); } - return columnName; - } - - private Object getValue(Class clazz, Cursor cursor, int columnIndex) { - if (cursor.getColumnCount() <= columnIndex || columnIndex < 0) { - throw new DataAccessException("Column index " + columnIndex + " does not exist"); - } - if (clazz.getName().endsWith("Integer") || clazz.getName().endsWith("int")) { - return cursor.getInt(columnIndex); - } else if (clazz.getName().endsWith("Long") || clazz.getName().endsWith("long")) { - return cursor.getLong(columnIndex); - } else if (clazz.getName().endsWith("Double") || clazz.getName().endsWith("double")) { - return cursor.getDouble(columnIndex); - } else if (clazz.getName().endsWith("Float") || clazz.getName().endsWith("float")) { - return cursor.getFloat(columnIndex); - } else if (clazz.getName().endsWith("String")) { - return cursor.getString(columnIndex); + else if (fieldToSet.isAnnotationPresent(Column.class)){ + mapper = fieldToSet.getAnnotation(Column.class).type().getMapper(); + value = fieldToSet.getAnnotation(Column.class).value(); + } + else + { + throw new DataAccessException("Invalid type, cannot find field "+fieldToSet.getName()+" on type "+object.getClass().getName()); + } + if(-1==cursor.getColumnIndex(value)) + { + throw new InvalidCursorException(object.getClass()); } - return null; + mapper.databaseToModel(cursor, fieldToSet, object); } } diff --git a/src/com/perfectworldprogramming/mobile/orm/Main.java b/src/com/perfectworldprogramming/mobile/orm/Main.java index 170d393..b16e30b 100644 --- a/src/com/perfectworldprogramming/mobile/orm/Main.java +++ b/src/com/perfectworldprogramming/mobile/orm/Main.java @@ -13,7 +13,8 @@ public class Main extends Activity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); - AndroidSQLLiteOpenHelper dbHelper = new AndroidSQLLiteOpenHelper(this.getApplicationContext(), null, "ormtest", 1); + @SuppressWarnings("unchecked") + AndroidSQLLiteOpenHelper dbHelper = new AndroidSQLLiteOpenHelper(this.getApplicationContext(), new Class[]{}, "ormtest", 1); dbHelper.getWritableDatabase(); } } \ No newline at end of file diff --git a/src/com/perfectworldprogramming/mobile/orm/annotations/Column.java b/src/com/perfectworldprogramming/mobile/orm/annotations/Column.java index a14c96d..1d59855 100644 --- a/src/com/perfectworldprogramming/mobile/orm/annotations/Column.java +++ b/src/com/perfectworldprogramming/mobile/orm/annotations/Column.java @@ -25,7 +25,7 @@ * * @return String field name in the database */ - public String value() default ""; + public String value(); /** * Should the column be unique and not allow duplicates. @@ -50,5 +50,5 @@ * * @return ColumnType */ - public ColumnType type() default ColumnType.TEXT; + public ColumnType type() default ColumnType.STRING; } diff --git a/src/com/perfectworldprogramming/mobile/orm/annotations/ColumnType.java b/src/com/perfectworldprogramming/mobile/orm/annotations/ColumnType.java index 9493397..c8d06f9 100644 --- a/src/com/perfectworldprogramming/mobile/orm/annotations/ColumnType.java +++ b/src/com/perfectworldprogramming/mobile/orm/annotations/ColumnType.java @@ -1,13 +1,41 @@ package com.perfectworldprogramming.mobile.orm.annotations; +import com.perfectworldprogramming.mobile.orm.interfaces.ColumnTypeMapper; +import com.perfectworldprogramming.mobile.orm.reflection.BlobMapper; +import com.perfectworldprogramming.mobile.orm.reflection.BooleanMapper; +import com.perfectworldprogramming.mobile.orm.reflection.DateMapper; +import com.perfectworldprogramming.mobile.orm.reflection.DoubleMapper; +import com.perfectworldprogramming.mobile.orm.reflection.FloatMapper; +import com.perfectworldprogramming.mobile.orm.reflection.IntegerMapper; +import com.perfectworldprogramming.mobile.orm.reflection.LongMapper; +import com.perfectworldprogramming.mobile.orm.reflection.StringMapper; + /** * User: Mark Spritzler * Date: 3/10/11 * Time: 8:42 PM */ public enum ColumnType { - TEXT, - INTEGER, - REAL, - BLOB + STRING(StringMapper.INSTANCE), + INTEGER(IntegerMapper.INSTANCE), + LONG(LongMapper.INSTANCE), + FLOAT(FloatMapper.INSTANCE), + DOUBLE(DoubleMapper.INSTANCE), + BLOB(BlobMapper.INSTANCE), + BOOLEAN(BooleanMapper.INSTANCE), + DATE(DateMapper.INSTANCE); + + private ColumnType(ColumnTypeMapper mapper) + { + this.mapper = mapper; + } + + @SuppressWarnings("unchecked") + public ColumnTypeMapper getMapper() + { + return this.mapper; + } + + @SuppressWarnings("rawtypes") + private final ColumnTypeMapper mapper; } diff --git a/src/com/perfectworldprogramming/mobile/orm/creator/SQLLiteCreateStatementGenerator.java b/src/com/perfectworldprogramming/mobile/orm/creator/SQLLiteCreateStatementGenerator.java index ba19f00..de0f842 100644 --- a/src/com/perfectworldprogramming/mobile/orm/creator/SQLLiteCreateStatementGenerator.java +++ b/src/com/perfectworldprogramming/mobile/orm/creator/SQLLiteCreateStatementGenerator.java @@ -1,15 +1,15 @@ package com.perfectworldprogramming.mobile.orm.creator; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + import com.perfectworldprogramming.mobile.orm.annotations.Column; import com.perfectworldprogramming.mobile.orm.annotations.ColumnType; import com.perfectworldprogramming.mobile.orm.annotations.ForeignKey; import com.perfectworldprogramming.mobile.orm.annotations.PrimaryKey; import com.perfectworldprogramming.mobile.orm.reflection.DomainClassAnalyzer; -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.List; - /** * This class generates CREATE SQL statements for mapped Domain objects. * @@ -163,7 +163,7 @@ private void addForeignKeyToStatement(StringBuilder createStatement, Field field private void addColumnsToStatement(StringBuilder createStatement, Column column) { createStatement.append(column.value()); createStatement.append(SPACE); - createStatement.append(column.type().name()); + createStatement.append(column.type().getMapper().getDatabaseColumnType()); createStatement.append(SPACE); if (column.unique()) { createStatement.append(UNIQUE); diff --git a/src/com/perfectworldprogramming/mobile/orm/exception/DataAccessException.java b/src/com/perfectworldprogramming/mobile/orm/exception/DataAccessException.java index dee4efd..5144556 100644 --- a/src/com/perfectworldprogramming/mobile/orm/exception/DataAccessException.java +++ b/src/com/perfectworldprogramming/mobile/orm/exception/DataAccessException.java @@ -1,18 +1,22 @@ package com.perfectworldprogramming.mobile.orm.exception; /** + * Base class for packaged exceptions. * User: Mark Spritzler * Date: 4/6/11 * Time: 11:26 AM */ public class DataAccessException extends RuntimeException { - /** - * - */ - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 4199875431810347897L; - public DataAccessException(String msg) { + public DataAccessException(String msg) { super(msg); } + + public DataAccessException(String detailMessage, Throwable throwable) + { + super(detailMessage, throwable); + } + } diff --git a/src/com/perfectworldprogramming/mobile/orm/exception/DatabaseSqlException.java b/src/com/perfectworldprogramming/mobile/orm/exception/DatabaseSqlException.java new file mode 100644 index 0000000..ae8a7bd --- /dev/null +++ b/src/com/perfectworldprogramming/mobile/orm/exception/DatabaseSqlException.java @@ -0,0 +1,15 @@ +package com.perfectworldprogramming.mobile.orm.exception; + +import android.database.SQLException; + +public class DatabaseSqlException extends DataAccessException +{ + + private static final long serialVersionUID = 6224888640967229700L; + + public DatabaseSqlException(String msg, SQLException sqle) + { + super(msg, sqle); + } + +} diff --git a/src/com/perfectworldprogramming/mobile/orm/exception/EmptySQLStatementException.java b/src/com/perfectworldprogramming/mobile/orm/exception/EmptySQLStatementException.java index e36321d..ed20037 100644 --- a/src/com/perfectworldprogramming/mobile/orm/exception/EmptySQLStatementException.java +++ b/src/com/perfectworldprogramming/mobile/orm/exception/EmptySQLStatementException.java @@ -2,9 +2,9 @@ public class EmptySQLStatementException extends DataAccessException { - private static final long serialVersionUID = 1L; - - public EmptySQLStatementException() { + private static final long serialVersionUID = 2355823153622503357L; + + public EmptySQLStatementException() { super("Invalid SQL. Please supply an SQL query string."); } } diff --git a/src/com/perfectworldprogramming/mobile/orm/exception/ExtraResultsException.java b/src/com/perfectworldprogramming/mobile/orm/exception/ExtraResultsException.java index c394570..b9dfb3c 100644 --- a/src/com/perfectworldprogramming/mobile/orm/exception/ExtraResultsException.java +++ b/src/com/perfectworldprogramming/mobile/orm/exception/ExtraResultsException.java @@ -7,9 +7,9 @@ */ public class ExtraResultsException extends DataAccessException { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 4713562069583405202L; - public ExtraResultsException(int numberOfResults) { + public ExtraResultsException(int numberOfResults) { super("Expected one row returned by query but received " + numberOfResults); } } diff --git a/src/com/perfectworldprogramming/mobile/orm/exception/FieldNotFoundException.java b/src/com/perfectworldprogramming/mobile/orm/exception/FieldNotFoundException.java new file mode 100644 index 0000000..824b628 --- /dev/null +++ b/src/com/perfectworldprogramming/mobile/orm/exception/FieldNotFoundException.java @@ -0,0 +1,13 @@ +package com.perfectworldprogramming.mobile.orm.exception; + +public class FieldNotFoundException extends DataAccessException +{ + + private static final long serialVersionUID = 4572576123276903345L; + + public FieldNotFoundException(String msg) + { + super(msg); + } + +} diff --git a/src/com/perfectworldprogramming/mobile/orm/exception/InvalidCursorException.java b/src/com/perfectworldprogramming/mobile/orm/exception/InvalidCursorException.java new file mode 100644 index 0000000..5bf9ae4 --- /dev/null +++ b/src/com/perfectworldprogramming/mobile/orm/exception/InvalidCursorException.java @@ -0,0 +1,11 @@ +package com.perfectworldprogramming.mobile.orm.exception; + +public class InvalidCursorException extends DataAccessException { + + private static final long serialVersionUID = 323422738996859681L; + + public InvalidCursorException(Class clazz) { + super("Invalid Cursor: "+ clazz.getName() + ". Possible reasons are 1) query does not contain all the fields, 2) query is using the wrong tables, 3) Not navigating through the Cursor correctly, either by the cursor is empty, or has past the end of the results."); + } + +} diff --git a/src/com/perfectworldprogramming/mobile/orm/exception/InvalidCursorExtractorException.java b/src/com/perfectworldprogramming/mobile/orm/exception/InvalidCursorExtractorException.java index 58f7967..5344295 100644 --- a/src/com/perfectworldprogramming/mobile/orm/exception/InvalidCursorExtractorException.java +++ b/src/com/perfectworldprogramming/mobile/orm/exception/InvalidCursorExtractorException.java @@ -1,11 +1,12 @@ package com.perfectworldprogramming.mobile.orm.exception; -public class InvalidCursorExtractorException extends DataAccessException { +public class InvalidCursorExtractorException extends InvalidCursorException { - private static final long serialVersionUID = 1L; - - public InvalidCursorExtractorException(Class class1) { - super("Invalid CursorExtractor: " + class1.getName() + ". Possible reasons are 1) query does not contain all the fields, 2) query is using the wrong tables, 3) Not navigating through the Cursor correctly, either by the cursor is empty, or has past the end of the results."); + private static final long serialVersionUID = 323422738996859681L; + + public InvalidCursorExtractorException(Class class1) { + super(class1); + //super("Invalid CursorExtractor: " + class1.getName() + ". Possible reasons are 1) query does not contain all the fields, 2) query is using the wrong tables, 3) Not navigating through the Cursor correctly, either by the cursor is empty, or has past the end of the results."); } } diff --git a/src/com/perfectworldprogramming/mobile/orm/exception/InvalidCursorRowMapperException.java b/src/com/perfectworldprogramming/mobile/orm/exception/InvalidCursorRowMapperException.java index 66188ef..47f3a45 100644 --- a/src/com/perfectworldprogramming/mobile/orm/exception/InvalidCursorRowMapperException.java +++ b/src/com/perfectworldprogramming/mobile/orm/exception/InvalidCursorRowMapperException.java @@ -1,10 +1,11 @@ package com.perfectworldprogramming.mobile.orm.exception; -public class InvalidCursorRowMapperException extends DataAccessException { +public class InvalidCursorRowMapperException extends InvalidCursorException { private static final long serialVersionUID = 1L; public InvalidCursorRowMapperException(Class class1) { - super("Invalid CursorRowMapper: " + class1.getName() + ". Possible reasons are 1) query does not contain all the fields, 2) query is using the wrong tables, 3) Not navigating through the Cursor correctly, either by the cursor is empty, or has past the end of the results."); + super(class1); + //super("Invalid CursorRowMapper: " + class1.getName() + ". Possible reasons are 1) query does not contain all the fields, 2) query is using the wrong tables, 3) Not navigating through the Cursor correctly, either by the cursor is empty, or has past the end of the results."); } } \ No newline at end of file diff --git a/src/com/perfectworldprogramming/mobile/orm/exception/InvalidQueryTypeException.java b/src/com/perfectworldprogramming/mobile/orm/exception/InvalidQueryTypeException.java index 86b01be..d296a96 100644 --- a/src/com/perfectworldprogramming/mobile/orm/exception/InvalidQueryTypeException.java +++ b/src/com/perfectworldprogramming/mobile/orm/exception/InvalidQueryTypeException.java @@ -1,12 +1,10 @@ package com.perfectworldprogramming.mobile.orm.exception; public class InvalidQueryTypeException extends DataAccessException { - /** - * - */ - private static final long serialVersionUID = 1L; - public InvalidQueryTypeException(String expectedType, String givenType) { + private static final long serialVersionUID = 5051391018559950899L; + + public InvalidQueryTypeException(String expectedType, String givenType) { super("Invalid Query type. Was expecting a " + expectedType + " but received a " + givenType); } diff --git a/src/com/perfectworldprogramming/mobile/orm/exception/NoPrimaryKeyFieldException.java b/src/com/perfectworldprogramming/mobile/orm/exception/NoPrimaryKeyFieldException.java index 8768bee..ef73391 100644 --- a/src/com/perfectworldprogramming/mobile/orm/exception/NoPrimaryKeyFieldException.java +++ b/src/com/perfectworldprogramming/mobile/orm/exception/NoPrimaryKeyFieldException.java @@ -2,12 +2,9 @@ public class NoPrimaryKeyFieldException extends DataAccessException { - /** - * - */ - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = -442347181326281845L; - public NoPrimaryKeyFieldException(Class clazz) { + public NoPrimaryKeyFieldException(Class clazz) { super("No primary key field found in class " + clazz.getSimpleName()); } } \ No newline at end of file diff --git a/src/com/perfectworldprogramming/mobile/orm/exception/NoRowsReturnedException.java b/src/com/perfectworldprogramming/mobile/orm/exception/NoRowsReturnedException.java index 8c39a99..d35a82a 100644 --- a/src/com/perfectworldprogramming/mobile/orm/exception/NoRowsReturnedException.java +++ b/src/com/perfectworldprogramming/mobile/orm/exception/NoRowsReturnedException.java @@ -2,12 +2,9 @@ public class NoRowsReturnedException extends DataAccessException { - /** - * - */ - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = -5416130249258941267L; - public NoRowsReturnedException(String msg) { + public NoRowsReturnedException(String msg) { super("No rows returned for query " + msg); } } diff --git a/src/com/perfectworldprogramming/mobile/orm/exception/TransientFieldException.java b/src/com/perfectworldprogramming/mobile/orm/exception/TransientFieldException.java new file mode 100644 index 0000000..8196cd6 --- /dev/null +++ b/src/com/perfectworldprogramming/mobile/orm/exception/TransientFieldException.java @@ -0,0 +1,12 @@ +package com.perfectworldprogramming.mobile.orm.exception; + +import java.lang.reflect.Field; + +public class TransientFieldException extends DataAccessException { + + private static final long serialVersionUID = -442347181326281845L; + + public TransientFieldException(Field field) { + super("Cannot persist transient field " + field.getName()); + } +} \ No newline at end of file diff --git a/src/com/perfectworldprogramming/mobile/orm/helper/AndroidSQLLiteOpenHelper.java b/src/com/perfectworldprogramming/mobile/orm/helper/AndroidSQLLiteOpenHelper.java index fcf7e6e..2dba7c4 100644 --- a/src/com/perfectworldprogramming/mobile/orm/helper/AndroidSQLLiteOpenHelper.java +++ b/src/com/perfectworldprogramming/mobile/orm/helper/AndroidSQLLiteOpenHelper.java @@ -4,9 +4,6 @@ import java.util.Arrays; import java.util.List; -import com.perfectworldprogramming.mobile.orm.creator.SQLLiteCreateStatementGenerator; -import com.perfectworldprogramming.mobile.orm.exception.DataAccessException; - import android.content.Context; import android.database.Cursor; import android.database.SQLException; @@ -14,6 +11,9 @@ import android.database.sqlite.SQLiteOpenHelper; import android.util.Log; +import com.perfectworldprogramming.mobile.orm.creator.SQLLiteCreateStatementGenerator; +import com.perfectworldprogramming.mobile.orm.exception.DatabaseSqlException; + /** * User: Mark Spritzler * Date: 3/10/11 @@ -32,7 +32,9 @@ public AndroidSQLLiteOpenHelper(Context context, Class[] class public void onCreate(SQLiteDatabase sqLiteDatabase) { SQLLiteCreateStatementGenerator sqlLiteCreateStatementGenerator = new SQLLiteCreateStatementGenerator(); for (Class clazz : classes) { - sqLiteDatabase.execSQL(sqlLiteCreateStatementGenerator.createCreateStatement(clazz)); + String createStatement = sqlLiteCreateStatementGenerator.createCreateStatement(clazz); + Log.d("ORM", createStatement); + sqLiteDatabase.execSQL(createStatement); } } @@ -49,14 +51,14 @@ remove backup table (DROP table 'temp_" + TableName) */ @Override public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) { - SQLLiteCreateStatementGenerator sqlLiteCreateStatementGenerator = new SQLLiteCreateStatementGenerator(); + SQLLiteCreateStatementGenerator sqlLiteCreateStatementGenerator = new SQLLiteCreateStatementGenerator(); sqLiteDatabase.beginTransaction(); for (Class clazz : classes) { // Thanks to Pentium10 at Stack Overflow for this solution. String createStatementIfNotExists = sqlLiteCreateStatementGenerator.createCreateIfNotExistsStatement(clazz); String tableName = clazz.getSimpleName(); try { - sqLiteDatabase.execSQL(createStatementIfNotExists); + sqLiteDatabase.execSQL(createStatementIfNotExists); List columns = getColumns(sqLiteDatabase, tableName); sqLiteDatabase.execSQL("ALTER table " + tableName + " RENAME TO 'temp_" + tableName + "'"); sqLiteDatabase.execSQL(sqlLiteCreateStatementGenerator.createCreateStatement(clazz)); @@ -65,8 +67,8 @@ public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) { sqLiteDatabase.execSQL(String.format( "INSERT INTO %s (%s) SELECT %s from temp_%s", tableName, cols, cols, tableName)); sqLiteDatabase.execSQL("DROP table 'temp_" + tableName +"'"); } catch (SQLException e) { - Log.v(tableName, e.getMessage(), e); - throw new DataAccessException(e.getMessage()); + Log.v("ORM", tableName+": "+e.getMessage(), e); + throw new DatabaseSqlException(e.getMessage(), e); } } sqLiteDatabase.setTransactionSuccessful(); @@ -83,8 +85,8 @@ private static List getColumns(SQLiteDatabase db, String tableName) { ar = new ArrayList(Arrays.asList(c.getColumnNames())); } } catch (Exception e) { - Log.v(tableName, e.getMessage(), e); - e.printStackTrace(); + Log.v("ORM", tableName+": "+e.getMessage(), e); + throw new RuntimeException("AndroidSQLLiteOpenHelper", e); } finally { if (c != null) c.close(); diff --git a/src/com/perfectworldprogramming/mobile/orm/helper/DBHelper.java b/src/com/perfectworldprogramming/mobile/orm/helper/DBHelper.java index 742bd81..3e2a4a4 100644 --- a/src/com/perfectworldprogramming/mobile/orm/helper/DBHelper.java +++ b/src/com/perfectworldprogramming/mobile/orm/helper/DBHelper.java @@ -2,6 +2,7 @@ import android.content.Context; import android.database.sqlite.SQLiteDatabase; +import android.util.Log; /** * This class might be deprecated if not needed @@ -15,7 +16,11 @@ public class DBHelper { private final AndroidSQLLiteOpenHelper openHelper; public DBHelper(Context context, Class[] classes, String dataBaseName, int dataBaseVersion) { - System.out.println(" " + context + " " + classes + " " + dataBaseName + " " + dataBaseVersion); + if(!dataBaseName.endsWith(".db")) + { + dataBaseName+=".db"; + } + Log.d("ORM", " " + context + " " + classes + " " + dataBaseName + " " + dataBaseVersion); openHelper = new AndroidSQLLiteOpenHelper(context, classes, dataBaseName, dataBaseVersion); establishDb(); } diff --git a/src/com/perfectworldprogramming/mobile/orm/interfaces/ColumnTypeMapper.java b/src/com/perfectworldprogramming/mobile/orm/interfaces/ColumnTypeMapper.java new file mode 100644 index 0000000..a546e60 --- /dev/null +++ b/src/com/perfectworldprogramming/mobile/orm/interfaces/ColumnTypeMapper.java @@ -0,0 +1,28 @@ +package com.perfectworldprogramming.mobile.orm.interfaces; + +import java.lang.reflect.Field; + +import com.perfectworldprogramming.mobile.orm.AndroidSQLiteTemplate; + +import android.content.ContentValues; +import android.database.Cursor; + +public interface ColumnTypeMapper +{ + void modelToDatabase(String columnName, Object object, ContentValues values); + void modelToDatabase(Field field, Object instance, ContentValues values); + void databaseToModel(Cursor cursor, String columnName, Object instance); + void databaseToModel(Cursor cursor, Field field, Object instance); + String getDatabaseColumnType(); + /** + * Returns the value on the {@code object} converted to a + * form suitable for use as an SQL query parameter. + * + * @see AndroidSQLiteTemplate + * @param object + * @param field + * + * @return + */ + Object asSqlQueryParameter(T input); +} diff --git a/src/com/perfectworldprogramming/mobile/orm/oql/AndroidOqlTemplate.java b/src/com/perfectworldprogramming/mobile/orm/oql/AndroidOqlTemplate.java new file mode 100644 index 0000000..295121f --- /dev/null +++ b/src/com/perfectworldprogramming/mobile/orm/oql/AndroidOqlTemplate.java @@ -0,0 +1,119 @@ +package com.perfectworldprogramming.mobile.orm.oql; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import android.database.sqlite.SQLiteDatabase; + +import com.perfectworldprogramming.mobile.orm.AndroidSQLiteTemplate; +import com.perfectworldprogramming.mobile.orm.interfaces.CursorExtractor; +import com.perfectworldprogramming.mobile.orm.interfaces.CursorRowMapper; + +/** + * Decorates the {@link AndroidSQLiteTemplate} with operations which refer to the domain + * object rather than database, allowing data access to be persistence agnostic. + * @author David O'Meara + * @since 14/06/2012 + * + */ +public class AndroidOqlTemplate extends AndroidSQLiteTemplate +{ + public AndroidOqlTemplate(SQLiteDatabase sqLiteDatabase) { + super(sqLiteDatabase); + } + + public long insert(String oql, OqlParameter... args) + { + return super.insert(QueryParser.parse(oql, getTypes(args)), convertParams(args)); + } + + public void update(String oql, OqlParameter... args) + { + //super.update(oql, args); + super.update(QueryParser.parse(oql, getTypes(args)), convertParams(args)); + } + + public void delete(String oql, OqlParameter... args) + { + //super.delete(oql, args); + super.delete(QueryParser.parse(oql, getTypes(args)), convertParams(args)); + } + + public int queryForInt(String oql, OqlParameter... args) + { + //return super.queryForInt(oql, args); + return super.queryForInt(QueryParser.parse(oql, getTypes(args)), convertParams(args)); + } + + public long queryForLong(String oql, OqlParameter... args) + { + //return super.queryForLong(oql, args); + return super.queryForLong(QueryParser.parse(oql, getTypes(args)), convertParams(args)); + } + + public String queryForString(String oql, OqlParameter... args) + { + //return super.queryForString(oql, args); + return super.queryForString(QueryParser.parse(oql, getTypes(args)), convertParams(args)); + } + + public T queryForObject(String oql, CursorRowMapper cursorRowMapper, OqlParameter... args) + { + //return super.queryForObject(oql, cursorRowMapper, args); + return super.queryForObject(QueryParser.parse(oql, getTypes(args)), cursorRowMapper, convertParams(args)); + } + + public T queryForObject(String oql, Class clazz, OqlParameter... args) + { + //return super.queryForObject(oql, clazz, args); + return super.queryForObject(QueryParser.parse(oql, getTypes(args)), clazz, convertParams(args)); + } + + public T queryForObject(String oql, CursorExtractor cursorExtractor, OqlParameter... args) + { + //return super.queryForObject(oql, cursorExtractor, args); + return super.queryForObject(QueryParser.parse(oql, getTypes(args)), cursorExtractor, convertParams(args)); + } + + public List query(String oql, Class clazz, OqlParameter... args) + { + //return super.query(oql, clazz, args); + return super.query(QueryParser.parse(oql, getTypes(args)), clazz, convertParams(args)); + } + + public List query(String oql, CursorRowMapper cursorRowMapper, OqlParameter... args) + { + //return super.query(oql, cursorRowMapper, args); + return super.query(QueryParser.parse(oql, getTypes(args)), cursorRowMapper, convertParams(args)); + } + + @Override + public Object mapQueryParameter(Object value, Class clazz, String fieldName) + { + return this.domainClassAnalyzer.mapQueryParameterByFieldName(value, clazz, fieldName); + } + + private List> getTypes(OqlParameter... args) + { + Set> types = new HashSet>(); + for(OqlParameter arg:args) + { + types.add(arg.getDomainClass()); + } + List> result = new ArrayList>(types.size()); + result.addAll(types); + return result; + } + private Object[] convertParams(OqlParameter... args) + { + Object[] result = new Object[args.length]; + for(int i=0;i + * @since 14/06/2012 + * + */ +public final class OqlParameter +{ + public OqlParameter(Class domainClass, String fieldName, Object domainValue) + { + this.domainClass = domainClass; + this.fieldName = fieldName; + this.domainValue = domainValue; + } + + public Class getDomainClass() + { + return domainClass; + } + public String getFieldName() + { + return fieldName; + } + public Object getDomainValue() + { + return domainValue; + } + + private final Class domainClass; + private final String fieldName; + private final Object domainValue; +} diff --git a/src/com/perfectworldprogramming/mobile/orm/oql/QueryParser.java b/src/com/perfectworldprogramming/mobile/orm/oql/QueryParser.java new file mode 100644 index 0000000..3bf42d6 --- /dev/null +++ b/src/com/perfectworldprogramming/mobile/orm/oql/QueryParser.java @@ -0,0 +1,134 @@ +package com.perfectworldprogramming.mobile.orm.oql; + +import java.lang.reflect.Field; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import com.perfectworldprogramming.mobile.orm.annotations.Column; +import com.perfectworldprogramming.mobile.orm.annotations.ForeignKey; +import com.perfectworldprogramming.mobile.orm.annotations.PrimaryKey; +import com.perfectworldprogramming.mobile.orm.exception.DataAccessException; +import com.perfectworldprogramming.mobile.orm.exception.FieldNotFoundException; +import com.perfectworldprogramming.mobile.orm.reflection.DomainClassAnalyzer; +/** + * Allows SQL to refer to domain classes and fields, which are then converted to SQL. + * Queries place matchable tokens in square brackets and refer to either a direct field or a qualified field + * using the (case insensitive) {@code class.getSimpleName()}. Duplicate fields will return the first match. + * In the special case where only the simple class name is entered, the primary key field will be substituted. + * This is to simplify queries using foreign keys. + * eg [fieldName] or [MyClass.myField]
+ * {@code "select * from Person where [age] = ?"}
+ * {@code "select * from Person where [dateOfBirth] < ?"}
+ * {@code "select * from Person where [Person.height] > ?"}
+ * {@code "select * from Person p, Address a where p.[Person.height] > ? and p.[Person] = a.[Address.person]"}
+ * @author David O'Meara + * @since 13/06/2012 + * + */ +public class QueryParser +{ + private QueryParser() + { + // prevent external instantiation + } + + public static String parse(String input, List> domainClasses) + { + StringBuilder result = new StringBuilder(); + int start = 0; + Matcher m = p.matcher(input); + while (m.find()) + { + result.append(input.substring(start, m.start())); + + if (m.groupCount() == 2 && m.group(2).length()>0) + { + for (Class clazz : domainClasses) + { + final String className = m.group(1); + final String fieldName = m.group(2); + if(clazz.getSimpleName().equalsIgnoreCase(className)) + { + String replace = getColumnName(clazz, fieldName); + if(replace==null || replace.length()==0) + { + throw new FieldNotFoundException("Could not find field '"+fieldName+"' in class "+clazz.getName()); + } + result.append(replace); + break; + } + } + } + else + { + final String matchName = m.group(1); + String replace = null; + for (Class clazz : domainClasses) + { + if(clazz.getSimpleName().equalsIgnoreCase(matchName)) + { + PrimaryKey pk = new DomainClassAnalyzer().getPrimaryKey(clazz); + replace = pk.value(); + break; + } + } + if(replace==null) + { + for (Class clazz : domainClasses) + { + replace = getColumnName(clazz, matchName); + if(replace.length()>0) + { + break; + } + } + } + if(replace==null || replace.length()==0) + { + throw new FieldNotFoundException("Could not find field '"+matchName+"' in domain class(es)"); + } + result.append(replace); + } + start = m.end(); + } + result.append(input.substring(start)); + return result.toString(); + } + + private static String getColumnName(Class clazz, String fieldName) + { + Field field; + try + { + field = clazz.getDeclaredField(fieldName); + } + catch (SecurityException e) + { + throw new DataAccessException("Failed to view field "+fieldName+" in class "+clazz.getName(), e); + } + catch (NoSuchFieldException e) + { + return ""; + } + if (field.isAnnotationPresent(Column.class)) + { + Column setter = field.getAnnotation(Column.class); + return setter.value(); + } + else if (field.isAnnotationPresent(PrimaryKey.class)) + { + PrimaryKey setter = field.getAnnotation(PrimaryKey.class); + return setter.value(); + } + else if (field.isAnnotationPresent(ForeignKey.class)) + { + ForeignKey setter = field.getAnnotation(ForeignKey.class); + return setter.value(); + } + return ""; + } + + private static final String REGEX_SUBST = "\\[([a-zA-Z0-9]+)\\.?([a-zA-Z0-9]*)\\]"; + private static final Pattern p = Pattern.compile(REGEX_SUBST); +} diff --git a/src/com/perfectworldprogramming/mobile/orm/reflection/AbstractMapper.java b/src/com/perfectworldprogramming/mobile/orm/reflection/AbstractMapper.java new file mode 100644 index 0000000..4785c43 --- /dev/null +++ b/src/com/perfectworldprogramming/mobile/orm/reflection/AbstractMapper.java @@ -0,0 +1,105 @@ +package com.perfectworldprogramming.mobile.orm.reflection; + +import java.lang.reflect.Field; + +import android.content.ContentValues; +import android.database.Cursor; + +import com.perfectworldprogramming.mobile.orm.annotations.Column; +import com.perfectworldprogramming.mobile.orm.exception.DataAccessException; +import com.perfectworldprogramming.mobile.orm.exception.FieldNotFoundException; +import com.perfectworldprogramming.mobile.orm.interfaces.ColumnTypeMapper; +import com.perfectworldprogramming.mobile.orm.interfaces.CursorExtractor; + +/** + * In many situations the default mapping to the database can use + * {@code object.toString()} and rely on SQLite to fill in the gaps. + * Note that this is for use with {@link Column} annotated fields only, + * for Primary Keys see {@link PrimaryKeyMapper} + * + * @author David O'Meara + * @since 06/06/2012 + * + */ +public abstract class AbstractMapper implements ColumnTypeMapper +{ + /** + * Convenience method for {@link CursorExtractor}s that know the column name + * but not the {@link Field} + * + * @param object + * @param columnName + * @param values + */ + @Override + public void modelToDatabase(String columnName, Object object, ContentValues values) + { + modelToDatabase(this.getFieldFromColumnName(columnName, object), object, values); + } + + @Override + public void modelToDatabase(Field field, Object instance, ContentValues values) + { + field.setAccessible(true); + try + { + Object value = field.get(instance); + if (value != null) + { + Column column = field.getAnnotation(Column.class); + values.put(column.value(), value.toString()); + } + } + catch (IllegalAccessException e) + { + throw new DataAccessException("Unable to get the Column value from the domain object: " + instance.getClass().getName() + " for Field: " + + field.getName()); + } + + } + + /** + * Convenience method for {@link CursorExtractor}s that know the column name + * but not the {@link Field} + * + * @param object + * @param columnName + * @param values + */ + @Override + public void databaseToModel(Cursor cursor, String columnName, Object object) + { + databaseToModel(cursor, this.getFieldFromColumnName(columnName, object), object); + } + + @Override + public abstract void databaseToModel(Cursor cursor, Field field, Object instance); + + @Override + public abstract String getDatabaseColumnType(); + + @Override + public Object asSqlQueryParameter(T value) + { + return value.toString(); + } + + private Field getFieldFromColumnName(String columnName, Object object) + { + Class clazz = object.getClass(); + Field[] fields = clazz.getDeclaredFields(); + for (Field field : fields) + { + if(field.isAnnotationPresent(Column.class)) + { + Column column = field.getAnnotation(Column.class); + if (columnName.equalsIgnoreCase(column.value())) + { + return field; + } + } + } + + throw new FieldNotFoundException("Could not find field for column name " + columnName + " in type " + clazz.getName()); + } +} diff --git a/src/com/perfectworldprogramming/mobile/orm/reflection/BlobMapper.java b/src/com/perfectworldprogramming/mobile/orm/reflection/BlobMapper.java new file mode 100644 index 0000000..db559e0 --- /dev/null +++ b/src/com/perfectworldprogramming/mobile/orm/reflection/BlobMapper.java @@ -0,0 +1,48 @@ +package com.perfectworldprogramming.mobile.orm.reflection; + +import java.lang.reflect.Field; +import java.sql.Blob; + +import android.database.Cursor; + +import com.perfectworldprogramming.mobile.orm.annotations.Column; +import com.perfectworldprogramming.mobile.orm.interfaces.ColumnTypeMapper; + +/** + * + * @author David O'Meara + * @since 06/06/2012 + * + */ +public class BlobMapper extends AbstractMapper +{ + public static final ColumnTypeMapper INSTANCE = new BlobMapper(); + + @Override + public void databaseToModel(Cursor cursor, Field field, Object instance) + { + field.setAccessible(true); + final Column column = field.getAnnotation(Column.class); + final int columnIndex = cursor.getColumnIndex(column.value()); + if(cursor.isNull(columnIndex)) + { + return; + } + + try + { + field.set(instance, cursor.getBlob(columnIndex)); + } + catch (IllegalAccessException e) + { + e.printStackTrace(); + } + } + + @Override + public String getDatabaseColumnType() + { + return "INTEGER"; + } + +} diff --git a/src/com/perfectworldprogramming/mobile/orm/reflection/BooleanMapper.java b/src/com/perfectworldprogramming/mobile/orm/reflection/BooleanMapper.java new file mode 100644 index 0000000..0965403 --- /dev/null +++ b/src/com/perfectworldprogramming/mobile/orm/reflection/BooleanMapper.java @@ -0,0 +1,88 @@ +package com.perfectworldprogramming.mobile.orm.reflection; + +import java.lang.reflect.Field; + +import android.content.ContentValues; +import android.database.Cursor; + +import com.perfectworldprogramming.mobile.orm.annotations.Column; +import com.perfectworldprogramming.mobile.orm.exception.DataAccessException; +import com.perfectworldprogramming.mobile.orm.interfaces.ColumnTypeMapper; +/** + * {@link ColumnTypeMapper} implementation for {@link Boolean} fields, which maps boolean values in the model + * layer to a 0|1 {@code INTEGER} in the database. + * + * @author David O'Meara + * @since 06/06/2012 + * + */ +public class BooleanMapper extends AbstractMapper +{ + public static final ColumnTypeMapper INSTANCE = new BooleanMapper(); + + @Override + public void modelToDatabase(Field field, Object instance, ContentValues values) + { + field.setAccessible(true); + try + { + final Column column = field.getAnnotation(Column.class); + if(Boolean.class.equals(field.getType())) + { + Boolean value = (Boolean)field.get(instance); + if(value!=null) + { + values.put(column.value(), value ? Integer.valueOf(1): Integer.valueOf(0)); + } + } + else + { + values.put(column.value(), field.getBoolean(instance) ? Integer.valueOf(1): Integer.valueOf(0)); + } + } + catch (IllegalAccessException e) + { + throw new DataAccessException("Unable to get the Column value from the domain object: " + instance.getClass().getName() + " for Field: " + + field.getName()); + } + catch(IllegalArgumentException e) + { + throw e; + } + } + + @Override + public void databaseToModel(Cursor cursor, Field field, Object instance) + { + field.setAccessible(true); + final Column column = field.getAnnotation(Column.class); + final int columnIndex = cursor.getColumnIndex(column.value()); + if(cursor.isNull(columnIndex)) + { + return; + } + + final Boolean value = Boolean.valueOf(cursor.getInt(columnIndex) == 1); + try + { + field.set(instance, value); + } + catch (IllegalAccessException e) + { + e.printStackTrace(); + } + } + + public String getDatabaseColumnType() + { + return "INTEGER"; + } + + @Override + public Object asSqlQueryParameter(Boolean value) + { + return (Boolean)value ? Integer.valueOf(1): Integer.valueOf(0); + } + + +} diff --git a/src/com/perfectworldprogramming/mobile/orm/reflection/DateMapper.java b/src/com/perfectworldprogramming/mobile/orm/reflection/DateMapper.java new file mode 100644 index 0000000..b3a5aae --- /dev/null +++ b/src/com/perfectworldprogramming/mobile/orm/reflection/DateMapper.java @@ -0,0 +1,80 @@ +package com.perfectworldprogramming.mobile.orm.reflection; + +import java.lang.reflect.Field; +import java.sql.Date; + +import android.content.ContentValues; +import android.database.Cursor; + +import com.perfectworldprogramming.mobile.orm.annotations.Column; +import com.perfectworldprogramming.mobile.orm.exception.DataAccessException; +import com.perfectworldprogramming.mobile.orm.interfaces.ColumnTypeMapper; +/** + * {@link ColumnTypeMapper} implementation for {@link Date} fields, which maps Date values in the model + * layer to {@code INTEGER} in the database. + * + * @author David O'Meara + * @since 06/06/2012 + * + */ +public class DateMapper extends AbstractMapper +{ + public static final ColumnTypeMapper INSTANCE = new DateMapper(); + + @Override + public void modelToDatabase(Field field, Object instance, ContentValues values) + { + field.setAccessible(true); + try + { + Date value = (Date)field.get(instance); + if(value!=null) + { + final Column column = field.getAnnotation(Column.class); + values.put(column.value(), value.getTime()); + } + } + catch (IllegalAccessException e) + { + throw new DataAccessException("Unable to get the Column value from the domain object: " + instance.getClass().getName() + " for Field: " + + field.getName()); + } + } + + @Override + public void databaseToModel(Cursor cursor, Field field, Object instance) + { + field.setAccessible(true); + final Column column = field.getAnnotation(Column.class); + final int columnIndex = cursor.getColumnIndex(column.value()); + if(cursor.isNull(columnIndex)) + { + return; + } + + final Date value = new Date(cursor.getLong(columnIndex)); + try + { + field.set(instance, value); + } + catch (IllegalAccessException e) + { + e.printStackTrace(); + } + } + + public String getDatabaseColumnType() + { + return "INTEGER"; + } + + @Override + public Object asSqlQueryParameter(Date value) + { + if(value!=null) + { + return Long.valueOf(((Date)value).getTime()); + } + return null; + } +} diff --git a/src/com/perfectworldprogramming/mobile/orm/reflection/DomainClassAnalyzer.java b/src/com/perfectworldprogramming/mobile/orm/reflection/DomainClassAnalyzer.java index aad7f89..dc56242 100644 --- a/src/com/perfectworldprogramming/mobile/orm/reflection/DomainClassAnalyzer.java +++ b/src/com/perfectworldprogramming/mobile/orm/reflection/DomainClassAnalyzer.java @@ -2,129 +2,284 @@ import java.lang.reflect.Field; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import android.content.ContentValues; + import com.perfectworldprogramming.mobile.orm.annotations.Column; import com.perfectworldprogramming.mobile.orm.annotations.ForeignKey; import com.perfectworldprogramming.mobile.orm.annotations.PrimaryKey; +import com.perfectworldprogramming.mobile.orm.annotations.Transient; import com.perfectworldprogramming.mobile.orm.exception.DataAccessException; +import com.perfectworldprogramming.mobile.orm.exception.FieldNotFoundException; import com.perfectworldprogramming.mobile.orm.exception.NoPrimaryKeyFieldException; - -import android.content.ContentValues; +import com.perfectworldprogramming.mobile.orm.exception.TransientFieldException; /** - * User: Mark Spritzler - * Date: 3/12/11 - * Time: 9:48 PM + * User: Mark Spritzler Date: 3/12/11 Time: 9:48 PM */ -public class DomainClassAnalyzer { +public class DomainClassAnalyzer +{ + + /** + * Given a {@code fieldName}, return the matching SQL {@code columnName} + * + * @param value + * @param clazz + * @param fieldName + * @return + */ + public String getSqlParameter(Object value, Class clazz, String fieldName) + { + Field field = null; + try + { + field = clazz.getDeclaredField(fieldName); + } + catch (SecurityException e) + { + throw new DataAccessException("Failed to access field " + fieldName + ": " + e.getMessage()); + } + catch (NoSuchFieldException e) + { + throw new FieldNotFoundException("Could not find field " + fieldName + " in " + clazz.getName()); + } + if (field.isAnnotationPresent(Transient.class)) + { + throw new TransientFieldException(field); + } + if (field.isAnnotationPresent(PrimaryKey.class)) + { + return field.getAnnotation(PrimaryKey.class).value(); + } + else if (field.isAnnotationPresent(ForeignKey.class)) + { + return field.getAnnotation(ForeignKey.class).value(); + } + else if (field.isAnnotationPresent(Column.class)) + { + return field.getAnnotation(Column.class).value(); + } + + throw new FieldNotFoundException("Could not find database column for field " + fieldName + " in " + clazz.getName()); + } + + public Object mapQueryParameterByFieldName(Object value, Class clazz, String fieldName) + { + Field field = null; + try + { + field = clazz.getDeclaredField(fieldName); + } + catch (SecurityException e) + { + throw new DataAccessException("Failed to access field " + fieldName + ": " + e.getMessage()); + } + catch (NoSuchFieldException e) + { + throw new FieldNotFoundException("Could not find domain field " + fieldName + " in " + clazz.getName()); + } + if (field.isAnnotationPresent(Transient.class)) + { + throw new TransientFieldException(field); + } + if (field.isAnnotationPresent(PrimaryKey.class)) + { + return PrimaryKeyMapper.INSTANCE.asSqlQueryParameter((Long) value); + } + else if (field.isAnnotationPresent(ForeignKey.class)) + { + return PrimaryKeyMapper.INSTANCE.asSqlQueryParameter((Long) value); + } + else if (field.isAnnotationPresent(Column.class)) + { + Column column = field.getAnnotation(Column.class); + return column.type().getMapper().asSqlQueryParameter(value); + } + + throw new FieldNotFoundException("Could not find database column for field " + fieldName + " in " + clazz.getName()); +} - public Field getPrimaryKeyField(Class clazz) { + /* * + * Given a {@code value} and the associated {@link Class} and + * {@link ColumnName}, find the correct {@link CursorTypeMapper} and return + * the value converted to the SQL domain. + * + * @param value + * the domain value + * @param clazz + * the destination class + * @param columnName + * @return + */ + public Object mapQueryParameterByColumnName(Object value, Class clazz, String columnName) + { + for (Field field : clazz.getDeclaredFields()) + { + // skip transient and static + if (field.isAnnotationPresent(Transient.class)) + { + continue; + } + field.setAccessible(true); + + if (field.isAnnotationPresent(PrimaryKey.class)) + { + PrimaryKey pk = field.getAnnotation(PrimaryKey.class); + if (pk.value().equalsIgnoreCase(columnName)) + { + return PrimaryKeyMapper.INSTANCE.asSqlQueryParameter((Long) value); + } + } + + if (field.isAnnotationPresent(ForeignKey.class)) + { + ForeignKey fk = field.getAnnotation(ForeignKey.class); + if (fk.value().equalsIgnoreCase(columnName)) + { + return PrimaryKeyMapper.INSTANCE.asSqlQueryParameter((Long) value); + } + } + + if (field.isAnnotationPresent(Column.class)) + { + Column column = field.getAnnotation(Column.class); + if (column.value().equalsIgnoreCase(columnName)) + { + return column.type().getMapper().asSqlQueryParameter(value); + } + } + } + throw new FieldNotFoundException("Could not find database column " + columnName + " in " + clazz.getName()); + } + + public Field getPrimaryKeyField(Class clazz) + { Field primaryKeyField = null; Field[] fields = clazz.getDeclaredFields(); - for (Field field : fields) { + for (Field field : fields) + { PrimaryKey primaryKey = field.getAnnotation(PrimaryKey.class); - if (primaryKey != null) { + if (primaryKey != null) + { primaryKeyField = field; break; } } - if (primaryKeyField == null) { + if (primaryKeyField == null) + { throw new NoPrimaryKeyFieldException(clazz); } return primaryKeyField; } - public String getPrimaryKeyFieldName(Class clazz) { + public String getPrimaryKeyFieldName(Class clazz) + { PrimaryKey primaryKey = getPrimaryKey(clazz); return primaryKey.value(); } - public PrimaryKey getPrimaryKey(Class clazz) { + public PrimaryKey getPrimaryKey(Class clazz) + { return getPrimaryKeyField(clazz).getAnnotation(PrimaryKey.class); } - public Field[] getForeignKeyFields(Class clazz) { + public List getForeignKeyFields(Class clazz) + { List foreignKeyFields = new ArrayList(); Field[] fields = clazz.getDeclaredFields(); - for (Field field : fields) { + for (Field field : fields) + { ForeignKey foreignKey = field.getAnnotation(ForeignKey.class); - if (foreignKey != null) { + if (foreignKey != null) + { foreignKeyFields.add(field); break; } } - return foreignKeyFields.toArray(new Field[foreignKeyFields.size()]); + return Collections.unmodifiableList(foreignKeyFields); } - public long getIdFromObject(Object o) { + public long getIdFromObject(Object o) + { Long id = -1l; Class clazz = o.getClass(); Field primaryKeyField = getPrimaryKeyField(clazz); primaryKeyField.setAccessible(true); - try { - id = (Long)primaryKeyField.get(o); - if (id == null) { - throw new DataAccessException("Domain classes must have an @PrimaryKey property in order to use the ORM functionality. Your " + o.getClass().getName() + " class is missing @PrimaryKey"); + try + { + id = (Long) primaryKeyField.get(o); + if (id == null) + { + throw new DataAccessException("Domain classes must have an @PrimaryKey property in order to use the ORM functionality. Your " + + o.getClass().getName() + " class is missing @PrimaryKey"); } - } catch (IllegalAccessException e) { + } + catch (IllegalAccessException e) + { throw new DataAccessException(e.getMessage()); } return id; } - - public void setIdToNewObject(Object object, long id) { - Field fieldToSet = this.getPrimaryKeyField(object.getClass()); + + public void setIdToNewObject(Object object, long id) + { + Field fieldToSet = this.getPrimaryKeyField(object.getClass()); fieldToSet.setAccessible(true); - try { + try + { fieldToSet.set(object, id); - } catch (IllegalAccessException e) { + } + catch (IllegalAccessException e) + { throw new DataAccessException(e.getMessage()); - } + } } - public ContentValues createContentValues(Object object) { + public ContentValues createContentValues(Object object) + { Class clazz = object.getClass(); - Field[] fields = clazz.getDeclaredFields(); + Field[] fields = clazz.getDeclaredFields(); ContentValues values = new ContentValues(fields.length); - for (Field field : fields) { - Column column = field.getAnnotation(Column.class); - if (column != null) { - addColumnValuesToContentValues(field, values, column.value(), object); - } else { - ForeignKey foreignKey = field.getAnnotation(ForeignKey.class); - if (foreignKey != null) { - addForeignKeyValuesToContentValues(field, foreignKey, values, object); + for (Field field : fields) + { + Column column = field.getAnnotation(Column.class); + if (column != null) + { + column.type().getMapper().modelToDatabase(field, object, values); + } + else + { + ForeignKey foreignKey = field.getAnnotation(ForeignKey.class); + if (foreignKey != null) + { + addForeignKeyValuesToContentValues(field, foreignKey, values, object); } } } return values; } - - private void addColumnValuesToContentValues(Field field, ContentValues values, String fieldName, Object object) { - field.setAccessible(true); - try { - Object value = field.get(object); - if (value != null) { - values.put(fieldName, value.toString()); + + private void addForeignKeyValuesToContentValues(Field field, ForeignKey foreignKey, ContentValues values, Object domainObject) + { + field.setAccessible(true); + try + { + Object foreignDomainObject = field.get(domainObject); + if (foreignDomainObject != null) + { + Object value = getIdFromObject(foreignDomainObject); + if (value != null) + { + values.put(foreignKey.value(), value.toString()); + } } - } catch (IllegalAccessException e) { - throw new DataAccessException("Unable to get the Column value from the domain object: " + object.getClass().getName() + " for Field: " + fieldName); } - } - - private void addForeignKeyValuesToContentValues(Field field, ForeignKey foreignKey, ContentValues values, Object domainObject) { - field.setAccessible(true); - try { - Object foreignDomainObject = field.get(domainObject); - if (foreignDomainObject != null) { - Object value = getIdFromObject(foreignDomainObject); - if (value != null) { - values.put(foreignKey.value(), value.toString()); - } - } - } catch (IllegalAccessException e) { + catch (IllegalAccessException e) + { throw new DataAccessException("Unable to get the Foreign Key value from the domain object: " + domainObject.getClass().getName()); } } + } diff --git a/src/com/perfectworldprogramming/mobile/orm/reflection/DoubleMapper.java b/src/com/perfectworldprogramming/mobile/orm/reflection/DoubleMapper.java new file mode 100644 index 0000000..e7bd4cc --- /dev/null +++ b/src/com/perfectworldprogramming/mobile/orm/reflection/DoubleMapper.java @@ -0,0 +1,47 @@ +package com.perfectworldprogramming.mobile.orm.reflection; + +import java.lang.reflect.Field; + +import android.database.Cursor; + +import com.perfectworldprogramming.mobile.orm.annotations.Column; +import com.perfectworldprogramming.mobile.orm.interfaces.ColumnTypeMapper; + +/** + * + * @author David O'Meara + * @since 06/06/2012 + * + */ +public class DoubleMapper extends AbstractMapper +{ + public static final ColumnTypeMapper INSTANCE = new DoubleMapper(); + + @Override + public void databaseToModel(Cursor cursor, Field field, Object instance) + { + field.setAccessible(true); + final Column column = field.getAnnotation(Column.class); + final int columnIndex = cursor.getColumnIndex(column.value()); + if(cursor.isNull(columnIndex)) + { + return; + } + + try + { + field.set(instance, Double.valueOf(cursor.getDouble(columnIndex))); + } + catch (IllegalAccessException e) + { + e.printStackTrace(); + } + } + + @Override + public String getDatabaseColumnType() + { + return "REAL"; + } + +} diff --git a/src/com/perfectworldprogramming/mobile/orm/reflection/FloatMapper.java b/src/com/perfectworldprogramming/mobile/orm/reflection/FloatMapper.java new file mode 100644 index 0000000..899f2ca --- /dev/null +++ b/src/com/perfectworldprogramming/mobile/orm/reflection/FloatMapper.java @@ -0,0 +1,47 @@ +package com.perfectworldprogramming.mobile.orm.reflection; + +import java.lang.reflect.Field; + +import android.database.Cursor; + +import com.perfectworldprogramming.mobile.orm.annotations.Column; +import com.perfectworldprogramming.mobile.orm.interfaces.ColumnTypeMapper; + +/** + * + * @author David O'Meara + * @since 06/06/2012 + * + */ +public class FloatMapper extends AbstractMapper +{ + public static final ColumnTypeMapper INSTANCE = new FloatMapper(); + + @Override + public void databaseToModel(Cursor cursor, Field field, Object instance) + { + field.setAccessible(true); + final Column column = field.getAnnotation(Column.class); + final int columnIndex = cursor.getColumnIndex(column.value()); + if(cursor.isNull(columnIndex)) + { + return; + } + + try + { + field.set(instance, cursor.getFloat(columnIndex)); + } + catch (IllegalAccessException e) + { + e.printStackTrace(); + } + } + + @Override + public String getDatabaseColumnType() + { + return "REAL"; + } + +} diff --git a/src/com/perfectworldprogramming/mobile/orm/reflection/IntegerMapper.java b/src/com/perfectworldprogramming/mobile/orm/reflection/IntegerMapper.java new file mode 100644 index 0000000..6f7ebbf --- /dev/null +++ b/src/com/perfectworldprogramming/mobile/orm/reflection/IntegerMapper.java @@ -0,0 +1,46 @@ +package com.perfectworldprogramming.mobile.orm.reflection; + +import java.lang.reflect.Field; + +import android.database.Cursor; + +import com.perfectworldprogramming.mobile.orm.annotations.Column; +import com.perfectworldprogramming.mobile.orm.interfaces.ColumnTypeMapper; + +/** + * + * @author David O'Meara + * @since 06/06/2012 + * + */ +public class IntegerMapper extends AbstractMapper +{ + public static final ColumnTypeMapper INSTANCE = new IntegerMapper(); + + @Override + public void databaseToModel(Cursor cursor, Field field, Object instance) + { + field.setAccessible(true); + final Column column = field.getAnnotation(Column.class); + final int columnIndex = cursor.getColumnIndex(column.value()); + if(cursor.isNull(columnIndex)) + { + return; + } + + try + { + field.set(instance, cursor.getInt(columnIndex)); + } + catch (IllegalAccessException e) + { + e.printStackTrace(); + } + } + + @Override + public String getDatabaseColumnType() + { + return "INTEGER"; + } +} diff --git a/src/com/perfectworldprogramming/mobile/orm/reflection/LongMapper.java b/src/com/perfectworldprogramming/mobile/orm/reflection/LongMapper.java new file mode 100644 index 0000000..d5cc73d --- /dev/null +++ b/src/com/perfectworldprogramming/mobile/orm/reflection/LongMapper.java @@ -0,0 +1,47 @@ +package com.perfectworldprogramming.mobile.orm.reflection; + +import java.lang.reflect.Field; + +import android.database.Cursor; + +import com.perfectworldprogramming.mobile.orm.annotations.Column; +import com.perfectworldprogramming.mobile.orm.interfaces.ColumnTypeMapper; + +/** + * + * @author David O'Meara + * @since 06/06/2012 + * + */ +public class LongMapper extends AbstractMapper +{ + public static final ColumnTypeMapper INSTANCE = new LongMapper(); + + @Override + public void databaseToModel(Cursor cursor, Field field, Object instance) + { + field.setAccessible(true); + final Column column = field.getAnnotation(Column.class); + final int columnIndex = cursor.getColumnIndex(column.value()); + if(cursor.isNull(columnIndex)) + { + return; + } + + try + { + field.set(instance, cursor.getLong(columnIndex)); + } + catch (IllegalAccessException e) + { + e.printStackTrace(); + } + } + + @Override + public String getDatabaseColumnType() + { + return "INTEGER"; + } + +} diff --git a/src/com/perfectworldprogramming/mobile/orm/reflection/PrimaryKeyMapper.java b/src/com/perfectworldprogramming/mobile/orm/reflection/PrimaryKeyMapper.java new file mode 100644 index 0000000..6381d88 --- /dev/null +++ b/src/com/perfectworldprogramming/mobile/orm/reflection/PrimaryKeyMapper.java @@ -0,0 +1,115 @@ +package com.perfectworldprogramming.mobile.orm.reflection; + +import java.lang.reflect.Field; + +import android.content.ContentValues; +import android.database.Cursor; + +import com.perfectworldprogramming.mobile.orm.annotations.PrimaryKey; +import com.perfectworldprogramming.mobile.orm.exception.DataAccessException; +import com.perfectworldprogramming.mobile.orm.exception.FieldNotFoundException; +import com.perfectworldprogramming.mobile.orm.interfaces.ColumnTypeMapper; +/** + * {@link ColumnTypeMapper} for the {@link PrimaryKey} annotated column + * + * @author David O'Meara + * @since 06/06/2012 + * + */ +public class PrimaryKeyMapper implements ColumnTypeMapper +{ + public static final ColumnTypeMapper INSTANCE = new PrimaryKeyMapper(); + + @Override + public void modelToDatabase(String columnName, Object object, ContentValues values) + { + Field field = this.getFieldFromColumnName(columnName, object); + if(field==null || !field.isAnnotationPresent(PrimaryKey.class) || !columnName.equalsIgnoreCase(field.getAnnotation(PrimaryKey.class).value())) + { + throw new FieldNotFoundException("Could not find primary key field for column name " + columnName + " in type " + object.getClass().getName()); + } + modelToDatabase(field, object, values); + } + + @Override + public void modelToDatabase(Field field, Object instance, ContentValues values) + { + field.setAccessible(true); + try + { + final PrimaryKey pk = field.getAnnotation(PrimaryKey.class); + if(Long.class.equals(field.getType())) + { + Long value = (Long)field.get(instance); + if(value!=null) + { + values.put(pk.value(), value); + } + } + else + { + values.put(pk.value(), field.getLong(instance) ); + } + } + catch (IllegalAccessException e) + { + throw new DataAccessException("Unable to get the Column value from the domain object: " + instance.getClass().getName() + " for Field: " + + field.getName()); + } + } + + @Override + public void databaseToModel(Cursor cursor, String columnName, Object object) + { + Field field = this.getFieldFromColumnName(columnName, object); + if(!field.getName().equals(columnName)) + { + throw new FieldNotFoundException("Could not find primary key field for column name " + columnName + " in type " + object.getClass().getName()); + } + databaseToModel(cursor, field, object); + } + + @Override + public void databaseToModel(Cursor cursor, Field field, Object instance) + { + field.setAccessible(true); + final PrimaryKey pk = field.getAnnotation(PrimaryKey.class); + final int columnIndex = cursor.getColumnIndex(pk.value()); + + try + { + field.set(instance, cursor.getLong(columnIndex)); + } + catch (IllegalAccessException e) + { + e.printStackTrace(); + } + } + + public String getDatabaseColumnType() + { + return "INTEGER"; + } + + @Override + public Object asSqlQueryParameter(Long value) + { + return value.toString(); + } + + private Field getFieldFromColumnName(String columnName, Object object) + { + Class clazz = object.getClass(); + Field[] fields = clazz.getDeclaredFields(); + for (Field field : fields) + { + PrimaryKey pk = field.getAnnotation(PrimaryKey.class); + if(pk!=null) + { + return field; + } + } + + throw new FieldNotFoundException("Could not find primary key field for column name " + columnName + " in type " + clazz.getName()); + } +} diff --git a/src/com/perfectworldprogramming/mobile/orm/reflection/StringMapper.java b/src/com/perfectworldprogramming/mobile/orm/reflection/StringMapper.java new file mode 100644 index 0000000..883eafd --- /dev/null +++ b/src/com/perfectworldprogramming/mobile/orm/reflection/StringMapper.java @@ -0,0 +1,47 @@ +package com.perfectworldprogramming.mobile.orm.reflection; + +import java.lang.reflect.Field; + +import android.database.Cursor; + +import com.perfectworldprogramming.mobile.orm.annotations.Column; +import com.perfectworldprogramming.mobile.orm.interfaces.ColumnTypeMapper; + +/** + * + * @author David O'Meara + * @since 06/06/2012 + * + */ +public class StringMapper extends AbstractMapper +{ + public static final ColumnTypeMapper INSTANCE = new StringMapper(); + + @Override + public void databaseToModel(Cursor cursor, Field field, Object instance) + { + field.setAccessible(true); + final Column column = field.getAnnotation(Column.class); + final int columnIndex = cursor.getColumnIndex(column.value()); + if(cursor.isNull(columnIndex)) + { + return; + } + + try + { + field.set(instance, cursor.getString(columnIndex)); + } + catch (IllegalAccessException e) + { + e.printStackTrace(); + } + } + + @Override + public String getDatabaseColumnType() + { + return "TEXT"; + } + +} diff --git a/test/com/perfectworldprogramming/mobile/orm/test/AndroidSQLiteTemplateTests.java b/test/com/perfectworldprogramming/mobile/orm/test/AndroidSQLiteTemplateTests.java index a8f99ee..56f1796 100644 --- a/test/com/perfectworldprogramming/mobile/orm/test/AndroidSQLiteTemplateTests.java +++ b/test/com/perfectworldprogramming/mobile/orm/test/AndroidSQLiteTemplateTests.java @@ -2,12 +2,15 @@ import java.util.List; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.test.ActivityInstrumentationTestCase2; +import android.util.Log; import com.perfectworldprogramming.mobile.orm.AndroidSQLiteTemplate; import com.perfectworldprogramming.mobile.orm.exception.DataAccessException; import com.perfectworldprogramming.mobile.orm.exception.EmptySQLStatementException; -import com.perfectworldprogramming.mobile.orm.exception.InvalidCursorExtractorException; -import com.perfectworldprogramming.mobile.orm.exception.InvalidCursorRowMapperException; +import com.perfectworldprogramming.mobile.orm.exception.InvalidCursorException; import com.perfectworldprogramming.mobile.orm.helper.DBHelper; import com.perfectworldprogramming.mobile.orm.interfaces.CursorExtractor; import com.perfectworldprogramming.mobile.orm.interfaces.CursorRowMapper; @@ -22,865 +25,914 @@ import com.perfectworldprogramming.mobile.orm.test.interfaces.PersonCursorRowMapper; import com.perfectworldprogramming.mobile.orm.test.interfaces.SampleDataHelper; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.test.ActivityInstrumentationTestCase2; -import android.util.Log; - /** - * User: Mark Spritzler - * Date: 4/7/11 - * Time: 12:06 PM + * User: Mark Spritzler Date: 4/7/11 Time: 12:06 PM */ -public class AndroidSQLiteTemplateTests extends ActivityInstrumentationTestCase2
{ - - public AndroidSQLiteTemplateTests() { - super("org.springframework.mobile.orm.test", Main.class); - } - - AndroidSQLiteTemplate template; - List sampleAccounts; - - SQLiteDatabase dataBase; - - @SuppressWarnings("unchecked") +public class AndroidSQLiteTemplateTests extends + ActivityInstrumentationTestCase2
{ + + public AndroidSQLiteTemplateTests() { + super("org.springframework.mobile.orm.test", Main.class); + } + + AndroidSQLiteTemplate template; + DBHelper helper; + List sampleAccounts; + + SQLiteDatabase dataBase; + + @SuppressWarnings("unchecked") public void setUp() { - try { - super.setUp(); - } catch (Exception e) { - e.printStackTrace(); - } - DBHelper helper = new DBHelper(this.getInstrumentation().getContext(), new Class[]{Person.class, Address.class, Account.class}, "ormtest", 3); - template = new AndroidSQLiteTemplate(helper.getSqlLiteDatabase()); - SampleDataHelper.addDataToDatabase(template); - sampleAccounts = SampleDataHelper.getAccounts(); - dataBase =helper.getSqlLiteDatabase(); - } - - @Override + helper = new DBHelper(this.getInstrumentation().getContext(), + new Class[] { Person.class, Address.class, Account.class }, + "ormtest.db", 3); + template = new AndroidSQLiteTemplate(helper.getSqlLiteDatabase()); + SampleDataHelper.addDataToDatabase(template); + sampleAccounts = SampleDataHelper.getAccounts(); + dataBase = helper.getSqlLiteDatabase(); + } + + @Override protected void tearDown() throws Exception { - super.tearDown(); + if (helper != null) { + helper.cleanup(); + } + } + + // Currently passes + public void testSuccessInsertWithDomainObject() { + Person person = new Person(); + person.setAge(5); + person.setFirstName("Ryan"); + person.setLastName("Simpson"); + person.setHeight(2.6); + long id = template.insert(person); + + // Check out by using the api directly. + Cursor cursor = dataBase.rawQuery( + "Select * from Person where PERSON_ID = " + id, null); + assertNotNull(cursor); + assertEquals(1, cursor.getCount()); + cursor.moveToFirst(); + assertEquals("Ryan", + cursor.getString(cursor.getColumnIndex(Person.COL_FIRST_NAME))); + assertEquals("Simpson", + cursor.getString(cursor.getColumnIndex("LAST_NAME"))); + } + + // Currently passes but needs more code + /* + * Missing Mandatory/not Nullable Field Field that is set to unique is not + * unique (next version, but the code still should work in this scenario and + * would throw a DataAccessException) + */ + public void testFailureInsertWithDomainObject() { + // missing mandatory field + Person person = new Person(); + person.setAge(5); + person.setLastName("Simpson"); + person.setHeight(2.6); + try { + template.insert(person); + fail("Exception should be thrown because a mandatory field is not set"); + } catch (DataAccessException dae) { + } catch (Exception e) { + fail("This should throw a Spring DataAccessException not one from SQLite: " + + e.getClass() + ": " + e.getMessage()); + } + } + + // Currently Passes + public void testSuccessInsertSQL() { + String sql = "INSERT INTO Person (FIRST_NAME, LAST_NAME, AGE) VALUES ('?', '?', ?)"; + Object[] args = { "Bugs", "Bunny", 10 }; + long id = template.insert(sql, args); + + Cursor cursor = dataBase.rawQuery( + "Select * from Person where PERSON_ID = " + id, null); + assertNotNull(cursor); + assertEquals(1, cursor.getCount()); + cursor.moveToFirst(); + assertEquals("Bugs", + cursor.getString(cursor.getColumnIndex(Person.COL_FIRST_NAME))); + assertEquals("Bunny", + cursor.getString(cursor.getColumnIndex("LAST_NAME"))); + assertEquals(10, cursor.getInt(cursor.getColumnIndex("AGE"))); + } + + // Currently Passes + /* + * Invalid Insert statement string using FIELDS Using an update statement + * instead of an insert + */ + public void testFailureInsertSQL() { + String sql = "INSERT INTO Person FIELDS (FIRST_NAME, LAST_NAME, AGE) VALUES ('?', '?', ?)"; + Object[] args = { "Bugs", "Bunny", 10 }; + try { + template.insert(sql, args); + fail("Exception should be thrown because a insert statement is invalid"); + } catch (DataAccessException de) { + } catch (Exception e) { + fail("This should throw a Spring DataAccessException not one from SQLite"); + } + + sql = "UPDATE Person (FIRST_NAME, LAST_NAME, AGE) VALUES ('?', '?', ?)"; + try { + template.insert(sql, args); + fail("Exception should be thrown because it is an update statement"); + } catch (DataAccessException de) { + } catch (Exception e) { + fail("This should throw a Spring DataAccessException not one from SQLite"); + } + } + + // Currently passes + public void testSuccessUpdateWithDomainObject() { + // Take the last account in collection and make changes + // First just a Real field + Account account = sampleAccounts.get(sampleAccounts.size() - 1); + account.setAmount(42.00); + long numberOfAccountsUpdated = template.update(account); + + assertEquals(1, numberOfAccountsUpdated); + assertAccount(account, "Select * from Account where YEAR_STARTED=2011"); + + // Now an Integer field + account.setYearAccountOpened(1900); + numberOfAccountsUpdated = template.update(account); + assertEquals(1, numberOfAccountsUpdated); + assertAccount(account, "Select * from Account where YEAR_STARTED=1900"); + + // Now a Text field + account.setAccountType("Invalid"); + numberOfAccountsUpdated = template.update(account); + assertEquals(1, numberOfAccountsUpdated); + assertAccount(account, "Select * from Account where YEAR_STARTED=1900"); + + // Now all three field types in one update + account.setAccountType("Business Pro"); + account.setYearAccountOpened(2011); + account.setAmount(4200.00); + numberOfAccountsUpdated = template.update(account); + assertEquals(1, numberOfAccountsUpdated); + assertAccount(account, "Select * from Account where YEAR_STARTED=2011"); + } + // Currently Passes + public void testFailureUpdateWithDomainObject() { + // No Id set in domain object + Account account = new Account(); + try { + template.update(account); + fail("Exception should be thrown because id is not set"); + } catch (DataAccessException de) { + } catch (Exception e) { + Log.i("ORM", Log.getStackTraceString(e)); + fail("This should throw a Spring DataAccessException not one from SQLite " + + e.getMessage()); + } + // Use an object that is not mapped + Long longObject = Long.valueOf(5); + try { + template.update(longObject); + fail("Exception should be thrown because Long is not a domain object"); + } catch (DataAccessException de) { + } catch (Exception e) { + fail("This should throw a Spring DataAccessException not one from SQLite " + + e.getMessage()); + } + + // Object is mapped, but not a table in the database + NoPKDomain noPK = new NoPKDomain(); + try { + template.update(noPK); + fail("Exception should be thrown because there isn't a table for this domain mapped object"); + } catch (DataAccessException de) { + } catch (Exception e) { + fail("This should throw a Spring DataAccessException not one from SQLite " + + e.getMessage()); + } + + } // Currently passes - public void testSuccessInsertWithDomainObject() { - Person person = new Person(); - person.setAge(5); - person.setFirstName("Ryan"); - person.setLastName("Simpson"); - person.setHeight(2.6); - long id = template.insert(person); - - // Check out by using the api directly. - Cursor cursor = dataBase.rawQuery("Select * from Person where PERSON_ID = " + id, null); - assertNotNull(cursor); - assertEquals(1, cursor.getCount()); - cursor.moveToFirst(); - assertEquals("Ryan", cursor.getString(cursor.getColumnIndex("FIRST_NAME"))); - assertEquals("Simpson", cursor.getString(cursor.getColumnIndex("LAST_NAME"))); - } - - //Currently passes but needs more code - /* - * Missing Mandatory/not Nullable Field - * Field that is set to unique is not unique (next version, but the code still should work in this scenario and would throw a DataAccessException) - */ - public void testFailureInsertWithDomainObject() { - //missing mandatory field - Person person = new Person(); - person.setAge(5); - person.setLastName("Simpson"); - person.setHeight(2.6); - try { - template.insert(person); - fail("Exception should be thrown because a mandatory field is not set"); - } catch (DataAccessException dae) { - } catch (Exception e) { - fail("This should throw a Spring DataAccessException not one from SQLite"); - } - } - - // Currently Passes - public void testSuccessInsertSQL() { - String sql = "INSERT INTO Person (FIRST_NAME, LAST_NAME, AGE) VALUES ('?', '?', ?)"; - Object[] args = {"Bugs", "Bunny", 10}; - long id = template.insert(sql, args); - - Cursor cursor = dataBase.rawQuery("Select * from Person where PERSON_ID = " + id, null); - assertNotNull(cursor); - assertEquals(1, cursor.getCount()); - cursor.moveToFirst(); - assertEquals("Bugs", cursor.getString(cursor.getColumnIndex("FIRST_NAME"))); - assertEquals("Bunny", cursor.getString(cursor.getColumnIndex("LAST_NAME"))); - assertEquals(10, cursor.getInt(cursor.getColumnIndex("AGE"))); - } - - // Currently Passes - /* - * Invalid Insert statement string using FIELDS - * Using an update statement instead of an insert - */ - public void testFailureInsertSQL() { - String sql = "INSERT INTO Person FIELDS (FIRST_NAME, LAST_NAME, AGE) VALUES ('?', '?', ?)"; - Object[] args = {"Bugs", "Bunny", 10}; - try { - template.insert(sql, args); - fail("Exception should be thrown because a insert statement is invalid"); - } catch (DataAccessException de) { - } catch (Exception e) { - fail("This should throw a Spring DataAccessException not one from SQLite"); - } - - sql = "UPDATE Person (FIRST_NAME, LAST_NAME, AGE) VALUES ('?', '?', ?)"; - try { - template.insert(sql, args); - fail("Exception should be thrown because it is an update statement"); - } catch (DataAccessException de) { - } catch (Exception e) { - fail("This should throw a Spring DataAccessException not one from SQLite"); - } - } - - // Currently passes - public void testSuccessUpdateWithDomainObject() { - // Take the last account in collection and make changes - // First just a Real field - Account account = sampleAccounts.get(sampleAccounts.size()-1); - account.setAmount(42.00); - long numberOfAccountsUpdated = template.update(account); - - assertEquals(1, numberOfAccountsUpdated); - assertAccount(account, "Select * from Account where YEAR_STARTED=2011"); - - // Now an Integer field - account.setYearAccountOpened(1900); - numberOfAccountsUpdated = template.update(account); - assertEquals(1, numberOfAccountsUpdated); - assertAccount(account, "Select * from Account where YEAR_STARTED=1900"); - - // Now a Text field - account.setAccountType("Invalid"); - numberOfAccountsUpdated = template.update(account); - assertEquals(1, numberOfAccountsUpdated); - assertAccount(account, "Select * from Account where YEAR_STARTED=1900"); - - // Now all three field types in one update - account.setAccountType("Business Pro"); - account.setYearAccountOpened(2011); - account.setAmount(4200.00); - numberOfAccountsUpdated = template.update(account); - assertEquals(1, numberOfAccountsUpdated); - assertAccount(account, "Select * from Account where YEAR_STARTED=2011"); - - } - - - // Currently Passes - public void testFailureUpdateWithDomainObject() { - //No Id set in domain object - Account account = new Account(); - try { - template.update(account); - fail("Exception should be thrown because id is not set"); - } catch (DataAccessException de) { - } catch (Exception e) { - Log.i("Template Test", Log.getStackTraceString(e)); - fail("This should throw a Spring DataAccessException not one from SQLite " + e.getMessage()); - } - - // Use an object that is not mapped - Long longObject = new Long(5); - try { - template.update(longObject); - fail("Exception should be thrown because Long is not a domain object"); - } catch (DataAccessException de) { - } catch (Exception e) { - fail("This should throw a Spring DataAccessException not one from SQLite " + e.getMessage()); - } - - // Object is mapped, but not a table in the database - NoPKDomain noPK = new NoPKDomain(); - try { - template.update(noPK); - fail("Exception should be thrown because there isn't a table for this domain mapped object"); - } catch (DataAccessException de) { - } catch (Exception e) { - fail("This should throw a Spring DataAccessException not one from SQLite " + e.getMessage()); - } - - } - - // Currently passes - public void testSuccessUpdateSQL() { - // Update statement to one row - String sql = "UPDATE Account set AMOUNT = ? where ACCOUNT_TYPE = '?'"; - Object[] args = {new Double(68.00), "Personal"}; - template.update(sql, args); - - Cursor cursor = dataBase.rawQuery("Select Amount from Account where ACCOUNT_TYPE='Personal'", null); - assertNotNull(cursor); - assertEquals(1, cursor.getCount()); - cursor.moveToFirst(); - Double results = cursor.getDouble(0); - assertEquals(new Double(68.00), results); - - // Update statement to more than one row - sql = "UPDATE Account set AMOUNT = ? where ACCOUNT_TYPE = '?'"; - args[0] = new Double(67.00); - args[1] = "Business"; - template.update(sql, args); - cursor = dataBase.rawQuery("Select Amount from Account where ACCOUNT_TYPE='Business'", null); - assertNotNull(cursor); - assertEquals(3, cursor.getCount()); - cursor.moveToFirst(); - results = cursor.getDouble(0); - assertEquals(new Double(67.00), results); - - // a delete statement - // This will work, because you can pass a delete statement to update - template.update("Delete from Account where ACCOUNT_ID = ?", new Object[]{1}); - - } - - - /* Currently passes - * Null sql, - * invalid update - * table doesn't exist - * set field doesn't exist - * set field to wrong type - * an insert statement, - * a select statement - * - */ - public void testFailureUpdateSQL() { - String sql = ""; - Object[] nullArgs = {}; - - // empty sql - try { - template.update(sql, nullArgs); - fail("Exception should be thrown because there isn't an update statement"); - } catch (DataAccessException de) { - } catch (Exception e) { - fail("This should throw a Spring DataAccessException not one from SQLite " + e.getMessage()); - } - - // table doesn't exist - sql = "UPDATE NonExistingTable set AMOUNT = ? where ACCOUNT_TYPE = '?'"; - Object[] validArgs = {new Double(67.00), "Business"}; - try { - template.update(sql, validArgs); - fail("Exception should be thrown because the table doesn't exist in update statement"); - } catch (DataAccessException de) { - } catch (Exception e) { - fail("This should throw a Spring DataAccessException not one from SQLite " + e.getMessage()); - } - - // set field doesn't exist - sql = "UPDATE Account set Non_Field = ? where ACCOUNT_TYPE = '?'"; - try { - template.update(sql, validArgs); - fail("Exception should be thrown because the field doesn't exist update statement"); - } catch (DataAccessException de) { - } catch (Exception e) { - fail("This should throw a Spring DataAccessException not one from SQLite " + e.getMessage()); - } - - // set field to wrong type - sql = "UPDATE Account set AMOUNT = ? where ACCOUNT_TYPE = '?'"; - Object[] invalidArgs = {"Hello", "Business"}; - try { - template.update(sql, invalidArgs); - fail("Exception should be thrown because the field value is the wrong type update statement"); - } catch (DataAccessException de) { - } catch (Exception e) { - fail("This should throw a Spring DataAccessException not one from SQLite " + e.getMessage()); - } - - // an insert statement, - sql = "INSERT INTO Person FIELDS (FIRST_NAME, LAST_NAME, AGE) VALUES ('?', '?', ?)"; - Object[] insertArgs = {"Bugs", "Bunny", 10}; - try { - template.update(sql, insertArgs); - fail("Exception should be thrown because this is an insert statement not an update statement"); - } catch (DataAccessException de) { - } catch (Exception e) { - fail("This should throw a Spring DataAccessException not one from SQLite " + e.getMessage()); - } - - // a select statement - sql = "Select * from Person"; - try { - template.update(sql, new Object[]{}); - fail("Exception should be thrown because this is a select statement not an update statement"); - } catch (DataAccessException de) { - } catch (Exception e) { - fail("This should throw a Spring DataAccessException not one from SQLite " + e.getMessage()); - } - - } - - // Currently passes - public void testSuccessDeleteWithDomainObject() { - Account account = sampleAccounts.get(5); - long results = template.delete(account); - assertEquals(1, results); - } - - /* - * Currently passes - * null domain object - * domain object with invalid id - * domain object without an id - */ - public void testFailureDeleteWithDomainObject() { - Account account = null; - try { - template.delete(account); - fail("Exception should be thrown because the domain object is null"); - } catch (DataAccessException de) { - } catch (Exception e) { - fail("This should throw a Spring DataAccessException not one from SQLite " + e.getMessage()); - } - - account = new Account(); - DomainClassAnalyzer analyzer = new DomainClassAnalyzer(); - analyzer.setIdToNewObject(account, 42); - try { - template.delete(account); - fail("Exception should be thrown because this is an invalid id"); - } catch (DataAccessException de) { - } catch (Exception e) { - fail("This should throw a Spring DataAccessException not one from SQLite " + e.getMessage()); - } - - account = new Account(); - try { - template.delete(account); - fail("Exception should be thrown because the id in the domain object is null"); - } catch (DataAccessException de) { - } catch (Exception e) { - fail("This should throw a Spring DataAccessException not one from SQLite " + e.getMessage()); - } - } - - // Currently Passes - public void testSuccessDeleteWithTableNameAndIdsToDelete() { - String table = "Account"; - String keyColumnName = "ACCOUNT_ID"; - String keyValue = "1"; - long results = template.delete(table, keyColumnName, keyValue); - assertEquals(1, results); - - keyValue = "2"; - results = template.delete(table, keyColumnName, keyValue); - assertEquals(1, results); - } - - /* - * Currently Passes - * empty strings and string array - * null table - * null keyColumnName - * null keyValues - * Wrong table name - * Wrong key column name - * Wrong id doesn't exist - */ - public void testFailureDeleteWithTableNameAndIdsToDelete() { - // empty strings and string array - String table = ""; - String keyColumnName = ""; - String keyValue = ""; - try { - template.delete(table, keyColumnName, keyValue); - fail("Exception should be thrown because there isn't a delete statement"); - } catch (DataAccessException de) { - } catch (Exception e) { - fail("This should throw a Spring DataAccessException not one from SQLite " + e.getMessage()); - } - - //null table - table = null; - keyColumnName = "ACCOUNT_ID"; - keyValue = "1"; - try { - template.delete(table, keyColumnName, keyValue); - fail("Exception should be thrown because there isn't a delete statement"); - } catch (DataAccessException de) { - } catch (Exception e) { - fail("This should throw a Spring DataAccessException not one from SQLite " + e.getMessage()); - } - - // null keyColumnName - table = "Account"; - keyColumnName = null; - try { - template.delete(table, keyColumnName, keyValue); - fail("Exception should be thrown because there isn't a delete statement"); - } catch (DataAccessException de) { - } catch (Exception e) { - fail("This should throw a Spring DataAccessException not one from SQLite " + e.getMessage()); - } - - // null keyValues - keyColumnName = "ACCOUNT_ID"; - keyValue = null; - try { - template.delete(table, keyColumnName, keyValue); - fail("Exception should be thrown because there isn't a delete statement"); - } catch (DataAccessException de) { - } catch (Exception e) { - fail("This should throw a Spring DataAccessException not one from SQLite " + e.getMessage()); - } - - // Fix the null from previous test - keyValue = "1"; - - // Wrong table name - table = "NonExistingTable"; - try { - template.delete(table, keyColumnName, keyValue); - fail("Exception should be thrown because there isn't a delete statement"); - } catch (DataAccessException de) { - } catch (Exception e) { - fail("This should throw a Spring DataAccessException not one from SQLite " + e.getMessage()); - } - - // Wrong key column name - table = "Account"; - keyColumnName = "BAD_ID"; - try { - template.delete(table, keyColumnName, keyValue); - fail("Exception should be thrown because there isn't a delete statement"); - } catch (DataAccessException de) { - } catch (Exception e) { - fail("This should throw a Spring DataAccessException not one from SQLite " + e.getMessage()); - } - - // Wrong id doesn't exist - keyColumnName = "ACCOUNT_ID"; - keyValue = "42"; - long results = template.delete(table, keyColumnName, keyValue); - assertEquals(0, results); - } - - // Currently Passes - public void testSuccessDeleteWithSQL() { - String sql = "Delete from Account where ACCOUNT_TYPE = '?'"; - Object[] args = {"Personal"}; - template.delete(sql, args); - - sql = "Delete from Account where ACCOUNT_TYPE = '?'"; - args[0] = "Business"; - template.delete(sql, args); - } - - // Currently Passes - public void testFailureDeleteWithSQL() { - String sql = ""; - Object[] nullArgs = {}; - - // empty sql - try { - template.delete(sql, nullArgs); - fail("Exception should be thrown because there isn't a delete statement"); - } catch (DataAccessException de) { - } catch (Exception e) { - fail("This should throw a Spring DataAccessException not one from SQLite " + e.getMessage()); - } - - // table doesn't exist - sql = "Delete from NonExistingTable where ACCOUNT_TYPE = '?'"; - Object[] validArgs = {"Business"}; - try { - template.delete(sql, validArgs); - fail("Exception should be thrown because the table doesn't exist in delete statement"); - } catch (DataAccessException de) { - } catch (Exception e) { - fail("This should throw a Spring DataAccessException not one from SQLite " + e.getMessage()); - } - - // an insert statement, - sql = "INSERT INTO Person FIELDS (FIRST_NAME, LAST_NAME, AGE) VALUES ('?', '?', ?)"; - Object[] insertArgs = {"Bugs", "Bunny", 10}; - try { - template.delete(sql, insertArgs); - fail("Exception should be thrown because this is an insert statement not a delete statement"); - } catch (DataAccessException de) { - } catch (Exception e) { - fail("This should throw a Spring DataAccessException not one from SQLite " + e.getMessage()); - } - - // a select statement - sql = "Select * from Person"; - try { - template.delete(sql, new Object[]{}); - fail("Exception should be thrown because this is a select statement not a delete statement"); - } catch (DataAccessException de) { - } catch (Exception e) { - fail("This should throw a Spring DataAccessException not one from SQLite " + e.getMessage()); - } - } - - - // Currently passes - public void testSuccessQueryForInt() { - String sql = "Select count(*) from Person"; - String args = ""; - Integer results = template.queryForInt(sql, args); - - assertNotNull(results); - Cursor cursor = dataBase.rawQuery("Select count(*) from Person", null); - assertNotNull(cursor); - assertEquals(1, cursor.getCount()); - cursor.moveToFirst(); - Integer realCount = cursor.getInt(0); - assertEquals(realCount, results); - } - - // Currently Passes - public void testFailureQueryForInt() { - String sql = "Select FIRST_NAME from Person Where FIRST_NAME='?'"; - String args = "George"; - try { - template.queryForInt(sql, args); - fail("DataAccessException should be thrown because query returns zero rows"); - } catch (DataAccessException de) { - } catch (Exception e) { - fail("This should throw a Spring DataAccessException not one from SQLite " + e.getMessage()); - } - - // returns one row, but since it is a String the int is set to 0; - args = "John"; - int zero = template.queryForInt(sql, args); - assertEquals(0, zero); - - } - - // Currently passes - public void testSuccessQueryForLong() { - String sql = "Select count(*) from Person"; - String args = ""; - Long results = template.queryForLong(sql, args); - - assertNotNull(results); - Cursor cursor = dataBase.rawQuery("Select count(*) from Person", null); - assertNotNull(cursor); - assertEquals(1, cursor.getCount()); - cursor.moveToFirst(); - Long realCount = cursor.getLong(0); - assertEquals(realCount, results); - } - - // Currently Passes - public void testFailureQueryForLong() { - String sql = "Select FIRST_NAME from Person Where FIRST_NAME='?'"; - String args = "George"; - try { - template.queryForLong(sql, args); - fail("DataAccessException should be thrown because query returns zero rows"); - } catch (DataAccessException de) { - } catch (Exception e) { - fail("This should throw a Spring DataAccessException not one from SQLite " + e.getMessage()); - } - args = "John"; - long zero = template.queryForLong(sql, args); - assertEquals(0, zero); - - } - - // Currently passes - public void testSuccessQueryForString() { - String sql = "Select FIRST_NAME from Person Where FIRST_NAME='?'"; - String args = "John"; - String results = template.queryForString(sql, args); - - assertNotNull(results); - assertEquals("John", results); - - // Still will be successful and convert the int field to a String - sql = "Select AGE from Person Where FIRST_NAME='?'"; - results = template.queryForString(sql, args); - assertNotNull(results); - assertEquals("42", results); - - // Still will be successful with more than one row but return just the first row's value - sql = "Select age from Person"; - args = null; - results = template.queryForString(sql, args); - assertNotNull(results); - assertEquals("42", results); - - } - - // Currently passes - public void testFailureQueryForString() { - String sql = "Select AGE from Person Where FIRST_NAME='?'"; - String args = "George"; - try { - template.queryForString(sql, args); - fail("DataAccessException should be thrown because it is returning 0 rows"); - } catch (DataAccessException de) { - } catch (Exception e) { - fail("This should throw a Spring DataAccessException not one from SQLite " + e.getMessage()); - } - } - - // Currently passes - public void testSuccessQueryForObjectWithDomainClass() { - String sql="Select * from Person where FIRST_NAME='?'"; - Class clazz = Person.class; - String args="John"; - Person person = template.queryForObject(sql, clazz, args); - assertJohnThePerson(person); - - // Still successful because SQLite returns the first row even if the query returns more than one row - sql="Select * from Person"; - args = null; - person = template.queryForObject(sql, clazz, args); - assertJohnThePerson(person); - } - - // Currently passes. - public void testFailureQueryForObjectWithDomainClass() { - String sql="Select * from Person where FIRST_NAME='?'"; - Class clazz = Person.class; - String args="George"; - try { - Object results = template.queryForObject(sql, clazz, args); - fail("DataAccessException should be thrown because results returned 0 rows but returns: " + results); - } catch (DataAccessException de) { - } catch (Exception e) { - fail("This should throw a Spring DataAccessException not one from SQLite " + e.getMessage()); - } - } - - // Currently passes - public void testSuccessQueryForObjectWithCursorRowMapper() { - String sql = "Select * from ADDRESS where ZIP_CODE='?'"; - AddressCursorRowMapper addressCursorRowMapper = new AddressCursorRowMapper(); - Address address = template.queryForObject(sql, addressCursorRowMapper, "12345"); - - assertNotNull(address); - assertEquals("Philadelphia", address.getCity()); - assertEquals("PA", address.getState()); - assertEquals("123 Broad Street", address.getStreet()); - assertEquals("12345", address.getZipCode()); - } - - // Currently passes - public void testFailuresQueryForObjectWithCursorRowMapper() { - String sql=""; - CursorRowMapper
cursorRowMapper = null; - String args=""; - try { - template.queryForObject(sql, cursorRowMapper, args); - fail("Should throw an IllegalArgumentException because the cursorRowMapper is null"); - } catch (IllegalArgumentException ie) { - } catch (Exception e) { - fail("Only an IllegalArgumentException should be thrown"); - } - - cursorRowMapper = new AddressCursorRowMapper(); - try { - template.queryForObject(sql, cursorRowMapper, args); - fail("Should throw an EmptySQLStatementException because the cursorRowMapper is null"); - } catch (EmptySQLStatementException ie) { - } catch (Exception e) { - fail("Only an EmptySQLStatementException should be thrown " + e.getMessage()); - } - - sql = "Select * from Person Where FIRST_NAME = '?'"; - args = "John"; - try { - template.queryForObject(sql, cursorRowMapper, args); - fail("Should throw an InvalidCursorRowMapperException because the cursorRowMapper is of the wrong type. it is an Address Row mapper but the query is from Person"); - } catch (InvalidCursorRowMapperException ie) { - } catch (Exception e) { - fail("Only an EmptySQLStatementException should be thrown: " + e.getMessage()); - } - } - - // Currently passes - public void testSuccessQueryForObjectWithCursorExtractor() { - String sql = "SELECT * from PERSON p, ADDRESS a where a.PERSON_ID = p.PERSON_ID and p.FIRST_NAME = '?'"; - Person person = template.queryForObject(sql, new PersonCursorExtractor(), "John"); - - assertNotNull(person); - assertEquals(new Integer(42), person.getAge()); - assertEquals("John", person.getFirstName()); - assertEquals("Doe", person.getLastName()); - assertEquals(new Double("5.1d"), person.getHeight()); - List
addresses = person.getAddresses(); - assertNotNull(addresses); - assertEquals(2, addresses.size()); - } - - // Currently passes - public void testFailureQueryForObjectWithCursorExtractor() { - String sql=""; - CursorExtractor
cursorExtractor = null; - String args=""; - try { - template.queryForObject(sql, cursorExtractor, args); - fail("Should throw an IllegalArgumentException because the cursorExtractor is null"); - } catch (IllegalArgumentException ie) { - } catch (Exception e) { - fail("Only an IllegalArgumentException should be thrown"); - } - - cursorExtractor = new AddressCursorExtractor(); - try { - template.queryForObject(sql, cursorExtractor, args); - fail("Should throw an EmptySQLStatementException because the cursorExtractor is null"); - } catch (EmptySQLStatementException ie) { - } catch (Exception e) { - fail("Only an EmptySQLStatementException should be thrown " + e.getMessage()); - } - - sql = "Select * from Person"; - try { - template.queryForObject(sql, cursorExtractor, args); - fail("Should throw an InvalidCursorExtractorException because the query does not return enough fields or from the Address table in a join clause"); - } catch (InvalidCursorExtractorException ie) { - } catch (Exception e) { - fail(e.getMessage()); - } - - } - - // Currently passes - public void testSuccessFindById() { - Person person = template.findById(Person.class, 1l); - assertJohnThePerson(person); - } - - // Currently passes - public void testFailedFindById() { - Person person = template.findById(Person.class, 50l); - assertNull(person); - } - - // Currently passes - public void testSuccessQueryWithDomainClass() { - List people = template.query("Select * from Person Where FIRST_NAME='?'", Person.class, "John"); - - assertNotNull(people); - assertEquals(1, people.size()); - Person person = people.get(0); - assertJohnThePerson(person); - } - - // Currently passes but needs more code, null sql, empty sql - public void testFailureQueryWithDomainClass() { - List people = template.query("Select * from Person Where FIRST_NAME='?'", Person.class, "Jonathon"); - assertNotNull(people); - assertEquals(0, people.size()); - - // Null sql - String sql = null; - try { - template.query(sql, Person.class, "Jonathon"); - fail("Should throw an EmptySQLStatementException because the query does not return enough fields or from the Address table in a join clause"); - } catch (EmptySQLStatementException esse) { - } catch (Exception e) { - fail(e.getMessage()); - } - - // Empty String sql - sql = ""; - try { - template.query(sql, Person.class, "Jonathon"); - fail("Should throw an EmptySQLStatementException because the query does not return enough fields or from the Address table in a join clause"); - } catch (EmptySQLStatementException esse) { - } catch (Exception e) { - fail(e.getMessage()); - } - - // Empty args - sql = "Select * from Person Where FIRST_NAME='?'"; - try { - template.query(sql, Person.class, (Object[])null); - fail("A DataAccessException should be thrown because the arguments for the where clause is null"); - } catch (DataAccessException dae) { - } catch (Exception e) { - fail(e.getMessage()); - } - } - - // Currently passes - public void testSuccessQueryWithCursorRowMapper() { - String sqlStart = "Select * from Person Where"; - CursorRowMapper personCursorRowMapper = new PersonCursorRowMapper(); - String sql = sqlStart + " FIRST_NAME='?'"; - List people = template.query(sql, personCursorRowMapper, "John"); - - assertNotNull(people); - assertEquals(1, people.size()); - Person person = people.get(0); - assertJohnThePerson(person); - - List everyone = template.query("Select * from Person", personCursorRowMapper); - assertNotNull(everyone); - Cursor cursor = dataBase.rawQuery("Select count(*) from Person", null); - assertNotNull(cursor); - assertEquals(1, cursor.getCount()); - cursor.moveToFirst(); - int realCount = cursor.getInt(0); - assertEquals(realCount, everyone.size()); - } - - // Currently passes add null sql or null rowmapper tests - public void testFailuresQueryWithCursorRowMapper() { - String sqlStart = "Select * from Person Where"; - CursorRowMapper personCursorRowMapper = new PersonCursorRowMapper(); - String sql = sqlStart + " FIRST_NAME=?"; - try { - template.query(sql, personCursorRowMapper, "John"); - fail("A DataAccessException should be thrown because the String parameter is not quoted"); - } catch (DataAccessException dae) { - } catch (Exception e) { - fail("This should throw a Spring DataAccessException not one from SQLite " + e.getMessage()); - } - - - try { - template.query("", personCursorRowMapper); - fail("A DataAccessException should be thrown because there is no query string"); - } catch (DataAccessException dae) { - } catch (Exception e) { - fail("This should throw a Spring DataAccessException not one from SQLite " + e.getMessage()); - } - - try { - template.query("Select * from Address", personCursorRowMapper); - fail("A DataAccessException should be thrown because they are Addresses not People"); - } catch (DataAccessException dae) { - } catch (Exception e) { - fail("This should throw a Spring DataAccessException not one from SQLite " + e.getMessage()); - } - } - - private void assertJohnThePerson(Person john) { - assertEquals(new Integer(42), john.getAge()); - assertEquals("John", john.getFirstName()); - assertEquals("Doe", john.getLastName()); - assertEquals(new Double("5.1d"), john.getHeight()); - } - - private void assertAccount(Account account, String sql) { - Cursor cursor = dataBase.rawQuery(sql, null); - assertNotNull(cursor); - assertEquals(1, cursor.getCount()); - cursor.moveToFirst(); - Account checkAccount = new Account(); - checkAccount.setAccountType(cursor.getString(cursor.getColumnIndex("ACCOUNT_TYPE"))); - checkAccount.setAmount(cursor.getDouble(cursor.getColumnIndex("AMOUNT"))); - checkAccount.setYearAccountOpened(cursor.getInt(cursor.getColumnIndex("YEAR_STARTED"))); - assertEquals(account, checkAccount); - } + public void testSuccessUpdateSQL() { + // Update statement to one row + String sql = "UPDATE Account set AMOUNT = ? where ACCOUNT_TYPE = '?'"; + Object[] args = { Double.valueOf(68.00), "Personal" }; + template.update(sql, args); + + Cursor cursor = dataBase.rawQuery( + "Select Amount from Account where ACCOUNT_TYPE='Personal'", + null); + assertNotNull(cursor); + assertEquals(1, cursor.getCount()); + cursor.moveToFirst(); + Double results = cursor.getDouble(0); + assertEquals(Double.valueOf(68.00), results); + + // Update statement to more than one row + sql = "UPDATE Account set AMOUNT = ? where ACCOUNT_TYPE = '?'"; + args[0] = Double.valueOf(67.00); + args[1] = "Business"; + template.update(sql, args); + sql = "Select " + Account.COL_AMOUNT + " from Account where " + + Account.COL_ACCOUNT_TYPE + "='Business'"; + cursor = dataBase.rawQuery(sql, null); + assertNotNull(cursor); + assertEquals(3, cursor.getCount()); + cursor.moveToFirst(); + results = cursor.getDouble(0); + assertEquals(Double.valueOf(67.00), results); + + // a delete statement + // This will work, because you can pass a delete statement to update + sql = "Delete from Account where " + Account.PK_ACCOUNT_ID + " = ?"; + template.update(sql, new Object[] { 1 }); + + } + + /* + * Currently passes Null sql, invalid update table doesn't exist set field + * doesn't exist set field to wrong type an insert statement, a select + * statement + */ + public void testFailureUpdateSQL() { + String sql = ""; + Object[] nullArgs = {}; + + // empty sql + try { + template.update(sql, nullArgs); + fail("Exception should be thrown because there isn't an update statement"); + } catch (DataAccessException de) { + } catch (Exception e) { + fail("This should throw a Spring DataAccessException not one from SQLite " + + e.getMessage()); + } + + // table doesn't exist + sql = "UPDATE NonExistingTable set AMOUNT = ? where ACCOUNT_TYPE = '?'"; + Object[] validArgs = { Double.valueOf(67.00), "Business" }; + try { + template.update(sql, validArgs); + fail("Exception should be thrown because the table doesn't exist in update statement"); + } catch (DataAccessException de) { + } catch (Exception e) { + fail("This should throw a Spring DataAccessException not one from SQLite " + + e.getMessage()); + } + + // set field doesn't exist + sql = "UPDATE Account set Non_Field = ? where ACCOUNT_TYPE = '?'"; + try { + template.update(sql, validArgs); + fail("Exception should be thrown because the field doesn't exist update statement"); + } catch (DataAccessException de) { + } catch (Exception e) { + fail("This should throw a Spring DataAccessException not one from SQLite " + + e.getMessage()); + } + + // set field to wrong type + sql = "UPDATE Account set AMOUNT = ? where ACCOUNT_TYPE = '?'"; + Object[] invalidArgs = { "Hello", "Business" }; + try { + template.update(sql, invalidArgs); + fail("Exception should be thrown because the field value is the wrong type update statement"); + } catch (DataAccessException de) { + } catch (Exception e) { + fail("This should throw a Spring DataAccessException not one from SQLite " + + e.getMessage()); + } + + // an insert statement, + sql = "INSERT INTO Person FIELDS (FIRST_NAME, LAST_NAME, AGE) VALUES ('?', '?', ?)"; + Object[] insertArgs = { "Bugs", "Bunny", 10 }; + try { + template.update(sql, insertArgs); + fail("Exception should be thrown because this is an insert statement not an update statement"); + } catch (DataAccessException de) { + } catch (Exception e) { + fail("This should throw a Spring DataAccessException not one from SQLite " + + e.getMessage()); + } + + // a select statement + sql = "Select * from Person"; + try { + template.update(sql, new Object[] {}); + fail("Exception should be thrown because this is a select statement not an update statement"); + } catch (DataAccessException de) { + } catch (Exception e) { + fail("This should throw a Spring DataAccessException not one from SQLite " + + e.getMessage()); + } + + } + + // Currently passes + public void testSuccessDeleteWithDomainObject() { + Account account = sampleAccounts.get(5); + long results = template.delete(account); + assertEquals(1, results); + } + + /* + * Currently passes null domain object domain object with invalid id domain + * object without an id + */ + public void testFailureDeleteWithDomainObject() { + Account account = null; + try { + template.delete(account); + fail("Exception should be thrown because the domain object is null"); + } catch (DataAccessException de) { + } catch (Exception e) { + fail("This should throw a Spring DataAccessException not one from SQLite " + + e.getMessage()); + } + + account = new Account(); + DomainClassAnalyzer analyzer = new DomainClassAnalyzer(); + analyzer.setIdToNewObject(account, 42); + try { + template.delete(account); + fail("Exception should be thrown because this is an invalid id"); + } catch (DataAccessException de) { + } catch (Exception e) { + fail("This should throw a Spring DataAccessException not one from SQLite " + + e.getMessage()); + } + + account = new Account(); + try { + template.delete(account); + fail("Exception should be thrown because the id in the domain object is null"); + } catch (DataAccessException de) { + } catch (Exception e) { + fail("This should throw a Spring DataAccessException not one from SQLite " + + e.getMessage()); + } + } + + // Currently Passes + public void testSuccessDeleteWithTableNameAndIdsToDelete() { + String table = "Account"; + String keyColumnName = "ACCOUNT_ID"; + String keyValue = "1"; + long results = template.delete(table, keyColumnName, keyValue); + assertEquals(1, results); + + keyValue = "2"; + results = template.delete(table, keyColumnName, keyValue); + assertEquals(1, results); + } + + /* + * Currently Passes empty strings and string array null table null + * keyColumnName null keyValues Wrong table name Wrong key column name Wrong + * id doesn't exist + */ + public void testFailureDeleteWithTableNameAndIdsToDelete() { + // empty strings and string array + String table = ""; + String keyColumnName = ""; + String keyValue = ""; + try { + template.delete(table, keyColumnName, keyValue); + fail("Exception should be thrown because there isn't a delete statement"); + } catch (DataAccessException de) { + } catch (Exception e) { + fail("This should throw a Spring DataAccessException not one from SQLite " + + e.getMessage()); + } + + // null table + table = null; + keyColumnName = "ACCOUNT_ID"; + keyValue = "1"; + try { + template.delete(table, keyColumnName, keyValue); + fail("Exception should be thrown because there isn't a delete statement"); + } catch (DataAccessException de) { + } catch (Exception e) { + fail("This should throw a Spring DataAccessException not one from SQLite " + + e.getMessage()); + } + + // null keyColumnName + table = "Account"; + keyColumnName = null; + try { + template.delete(table, keyColumnName, keyValue); + fail("Exception should be thrown because there isn't a delete statement"); + } catch (DataAccessException de) { + } catch (Exception e) { + fail("This should throw a Spring DataAccessException not one from SQLite " + + e.getMessage()); + } + + // null keyValues + keyColumnName = "ACCOUNT_ID"; + keyValue = null; + try { + template.delete(table, keyColumnName, keyValue); + fail("Exception should be thrown because there isn't a delete statement"); + } catch (DataAccessException de) { + } catch (Exception e) { + fail("This should throw a Spring DataAccessException not one from SQLite " + + e.getMessage()); + } + + // Fix the null from previous test + keyValue = "1"; + + // Wrong table name + table = "NonExistingTable"; + try { + template.delete(table, keyColumnName, keyValue); + fail("Exception should be thrown because there isn't a delete statement"); + } catch (DataAccessException de) { + } catch (Exception e) { + fail("This should throw a Spring DataAccessException not one from SQLite " + + e.getMessage()); + } + + // Wrong key column name + table = "Account"; + keyColumnName = "BAD_ID"; + try { + template.delete(table, keyColumnName, keyValue); + fail("Exception should be thrown because there isn't a delete statement"); + } catch (DataAccessException de) { + } catch (Exception e) { + fail("This should throw a Spring DataAccessException not one from SQLite " + + e.getMessage()); + } + + // Wrong id doesn't exist + keyColumnName = "ACCOUNT_ID"; + keyValue = "42"; + long results = template.delete(table, keyColumnName, keyValue); + assertEquals(0, results); + } + + // Currently Passes + public void testSuccessDeleteWithSQL() { + String sql = "Delete from Account where ACCOUNT_TYPE = '?'"; + Object[] args = { "Personal" }; + template.delete(sql, args); + + sql = "Delete from Account where ACCOUNT_TYPE = '?'"; + args[0] = "Business"; + template.delete(sql, args); + } + + // Currently Passes + public void testFailureDeleteWithSQL() { + String sql = ""; + Object[] nullArgs = {}; + + // empty sql + try { + template.delete(sql, nullArgs); + fail("Exception should be thrown because there isn't a delete statement"); + } catch (DataAccessException de) { + } catch (Exception e) { + fail("This should throw a Spring DataAccessException not one from SQLite " + + e.getMessage()); + } + + // table doesn't exist + sql = "Delete from NonExistingTable where ACCOUNT_TYPE = '?'"; + Object[] validArgs = { "Business" }; + try { + template.delete(sql, validArgs); + fail("Exception should be thrown because the table doesn't exist in delete statement"); + } catch (DataAccessException de) { + } catch (Exception e) { + fail("This should throw a Spring DataAccessException not one from SQLite " + + e.getMessage()); + } + + // an insert statement, + sql = "INSERT INTO Person FIELDS (FIRST_NAME, LAST_NAME, AGE) VALUES ('?', '?', ?)"; + Object[] insertArgs = { "Bugs", "Bunny", 10 }; + try { + template.delete(sql, insertArgs); + fail("Exception should be thrown because this is an insert statement not a delete statement"); + } catch (DataAccessException de) { + } catch (Exception e) { + fail("This should throw a Spring DataAccessException not one from SQLite " + + e.getMessage()); + } + + // a select statement + sql = "Select * from Person"; + try { + template.delete(sql, new Object[] {}); + fail("Exception should be thrown because this is a select statement not a delete statement"); + } catch (DataAccessException de) { + } catch (Exception e) { + fail("This should throw a Spring DataAccessException not one from SQLite " + + e.getMessage()); + } + } + + // Currently passes + public void testSuccessQueryForInt() { + String sql = "Select count(*) from Person"; + String args = ""; + Integer results = template.queryForInt(sql, args); + + assertNotNull(results); + Cursor cursor = dataBase.rawQuery("Select count(*) from Person", null); + assertNotNull(cursor); + assertEquals(1, cursor.getCount()); + cursor.moveToFirst(); + Integer realCount = cursor.getInt(0); + assertEquals(realCount, results); + } + + // Currently Passes + public void testFailureQueryForInt() { + String sql = "Select FIRST_NAME from Person Where FIRST_NAME='?'"; + String args = "George"; + try { + template.queryForInt(sql, args); + fail("DataAccessException should be thrown because query returns zero rows"); + } catch (DataAccessException de) { + } catch (Exception e) { + fail("This should throw a Spring DataAccessException not one from SQLite " + + e.getMessage()); + } + + // returns one row, but since it is a String the int is set to 0; + args = "John"; + int zero = template.queryForInt(sql, args); + assertEquals(0, zero); + + } + + // Currently passes + public void testSuccessQueryForLong() { + String sql = "Select count(*) from Person"; + String args = ""; + Long results = template.queryForLong(sql, args); + + assertNotNull(results); + Cursor cursor = dataBase.rawQuery("Select count(*) from Person", null); + assertNotNull(cursor); + assertEquals(1, cursor.getCount()); + cursor.moveToFirst(); + Long realCount = cursor.getLong(0); + assertEquals(realCount, results); + } + + // Currently Passes + public void testFailureQueryForLong() { + String sql = "Select FIRST_NAME from Person Where FIRST_NAME='?'"; + String args = "George"; + try { + template.queryForLong(sql, args); + fail("DataAccessException should be thrown because query returns zero rows"); + } catch (DataAccessException de) { + } catch (Exception e) { + fail("This should throw a Spring DataAccessException not one from SQLite " + + e.getMessage()); + } + args = "John"; + long zero = template.queryForLong(sql, args); + assertEquals(0, zero); + + } + + // Currently passes + public void testSuccessQueryForString() { + String sql = "Select FIRST_NAME from Person Where FIRST_NAME='?'"; + String args = "John"; + String results = template.queryForString(sql, args); + + assertNotNull(results); + assertEquals("John", results); + + // Still will be successful and convert the int field to a String + sql = "Select AGE from Person Where FIRST_NAME='?'"; + results = template.queryForString(sql, args); + assertNotNull(results); + assertEquals("42", results); + + // Still will be successful with more than one row but return just the + // first row's value + sql = "Select age from Person"; + args = null; + results = template.queryForString(sql, args); + assertNotNull(results); + assertEquals("42", results); + + } + + // Currently passes + public void testFailureQueryForString() { + String sql = "Select AGE from Person Where FIRST_NAME='?'"; + String args = "George"; + try { + template.queryForString(sql, args); + fail("DataAccessException should be thrown because it is returning 0 rows"); + } catch (DataAccessException de) { + } catch (Exception e) { + fail("This should throw a Spring DataAccessException not one from SQLite " + + e.getMessage()); + } + } + + // Currently passes + public void testSuccessQueryForObjectWithDomainClass() { + final String sql = "Select * from Person where FIRST_NAME='?'"; + Class clazz = Person.class; + String args = "John"; + final Person john = template.queryForObject(sql, clazz, args); + assertJohnThePerson(john); + + // Still successful because SQLite returns the first row even if the + // query returns more than one row + final String sql_star = "Select * from Person"; + args = null; + Person person = template.queryForObject(sql_star, clazz, args); + assertJohnThePerson(person); + + args = "Jane"; + final Person jane = template.queryForObject(sql, clazz, args); + assertJaneThePerson(jane); + } + + // Currently passes. + public void testFailureQueryForObjectWithDomainClass() { + String sql = "Select * from Person where FIRST_NAME='?'"; + Class clazz = Person.class; + String args = "George"; + try { + Object results = template.queryForObject(sql, clazz, args); + fail("DataAccessException should be thrown because results returned 0 rows but returns: " + + results); + } catch (DataAccessException de) { + } catch (Exception e) { + fail("This should throw a Spring DataAccessException not one from SQLite " + + e.getMessage()); + } + } + + // Currently passes + public void testSuccessQueryForObjectWithCursorRowMapper() { + String sql = "Select * from ADDRESS where ZIP_CODE='?'"; + AddressCursorRowMapper addressCursorRowMapper = new AddressCursorRowMapper(); + Address address = template.queryForObject(sql, addressCursorRowMapper, + "12345"); + + assertNotNull(address); + assertEquals("Philadelphia", address.getCity()); + assertEquals("PA", address.getState()); + assertEquals("123 Broad Street", address.getStreet()); + assertEquals("12345", address.getZipCode()); + } + + // Currently passes + public void testFailuresQueryForObjectWithCursorRowMapper() { + String sql = ""; + CursorRowMapper
cursorRowMapper = null; + String args = ""; + try { + template.queryForObject(sql, cursorRowMapper, args); + fail("Should throw an IllegalArgumentException because the cursorRowMapper is null"); + } catch (IllegalArgumentException ie) { + } catch (Exception e) { + fail("Only an IllegalArgumentException should be thrown"); + } + + cursorRowMapper = new AddressCursorRowMapper(); + try { + template.queryForObject(sql, cursorRowMapper, args); + fail("Should throw an EmptySQLStatementException because the cursorRowMapper is null"); + } catch (EmptySQLStatementException ie) { + } catch (Exception e) { + fail("Only an EmptySQLStatementException should be thrown " + + e.getMessage()); + } + + sql = "Select * from Person Where FIRST_NAME = '?'"; + args = "John"; + try { + template.queryForObject(sql, cursorRowMapper, args); + fail("Should throw an InvalidCursorRowMapperException because the cursorRowMapper is of the wrong type. it is an Address Row mapper but the query is from Person"); + } catch (InvalidCursorException ie) { + } catch (Exception e) { + fail("Only an InvalidCursorException should be thrown: " + + e.getMessage()); + } + } + + // Currently passes + public void testSuccessQueryForObjectWithCursorExtractor() { + String sql = "SELECT * from PERSON p, ADDRESS a where a.PERSON_ID = p.PERSON_ID and p.FIRST_NAME = '?'"; + Person person = template.queryForObject(sql, + new PersonCursorExtractor(), "John"); + + assertNotNull(person); + assertEquals(Integer.valueOf(42), person.getAge()); + assertEquals("John", person.getFirstName()); + assertEquals("Doe", person.getLastName()); + assertEquals("Height", Double.valueOf("5.1d"), person.getHeight()); + List
addresses = person.getAddresses(); + assertNotNull(addresses); + assertEquals("Address size", 2, addresses.size()); + } + + // Currently passes + public void testFailureQueryForObjectWithCursorExtractor() { + String sql = ""; + CursorExtractor
cursorExtractor = null; + String args = ""; + try { + template.queryForObject(sql, cursorExtractor, args); + fail("Should throw an IllegalArgumentException because the cursorExtractor is null"); + } catch (IllegalArgumentException ie) { + } catch (Exception e) { + fail("Only an IllegalArgumentException should be thrown, not " + + e.getClass().getName()); + } + + cursorExtractor = new AddressCursorExtractor(); + try { + template.queryForObject(sql, cursorExtractor, args); + fail("Should throw an EmptySQLStatementException because the cursorExtractor is null"); + } catch (EmptySQLStatementException ie) { + } catch (Exception e) { + fail("Only an EmptySQLStatementException should be thrown " + + e.getMessage()); + } + + sql = "Select * from Person"; + try { + template.queryForObject(sql, cursorExtractor, args); + fail("Should throw an InvalidCursorExtractorException because the query does not return enough fields or from the Address table in a join clause"); + } catch (InvalidCursorException ie) { + } catch (Exception e) { + fail(e.getMessage()); + } + + } + + // Currently passes + public void testSuccessFindById() { + Person person = template.findById(Person.class, 1L); + assertJohnThePerson(person); + } + + // Currently passes + public void testFailedFindById() { + Person person = template.findById(Person.class, 50L); + assertNull(person); + } + + // Currently passes + public void testSuccessQueryWithDomainClass() { + List people = template.query( + "Select * from Person Where FIRST_NAME='?'", Person.class, + "John"); + + assertNotNull(people); + assertEquals(1, people.size()); + Person person = people.get(0); + assertJohnThePerson(person); + } + + // Currently passes but needs more code, null sql, empty sql + public void testFailureQueryWithDomainClass() { + List people = template.query( + "Select * from Person Where FIRST_NAME='?'", Person.class, + "Jonathon"); + assertNotNull(people); + assertEquals(0, people.size()); + + // Null sql + String sql = null; + try { + template.query(sql, Person.class, "Jonathon"); + fail("Should throw an EmptySQLStatementException because the query does not return enough fields or from the Address table in a join clause"); + } catch (EmptySQLStatementException esse) { + } catch (Exception e) { + fail(e.getMessage()); + } + + // Empty String sql + sql = ""; + try { + template.query(sql, Person.class, "Jonathon"); + fail("Should throw an EmptySQLStatementException because the query does not return enough fields or from the Address table in a join clause"); + } catch (EmptySQLStatementException esse) { + } catch (Exception e) { + fail(e.getMessage()); + } + + // Empty args + sql = "Select * from Person Where FIRST_NAME='?'"; + try { + template.query(sql, Person.class, (Object[]) null); + fail("A DataAccessException should be thrown because the arguments for the where clause is null"); + } catch (DataAccessException dae) { + } catch (Exception e) { + fail(e.getMessage()); + } + } + + // Currently passes + public void testSuccessQueryWithCursorRowMapper() { + String sqlStart = "Select * from Person Where"; + CursorRowMapper personCursorRowMapper = new PersonCursorRowMapper(); + String sql = sqlStart + " FIRST_NAME='?'"; + List people = template + .query(sql, personCursorRowMapper, "John"); + + assertNotNull(people); + assertEquals(1, people.size()); + Person person = people.get(0); + assertJohnThePerson(person); + + List everyone = template.query("Select * from Person", + Person.class); + assertNotNull(everyone); + Cursor cursor = dataBase.rawQuery("Select count(*) from Person", null); + assertNotNull(cursor); + assertEquals(1, cursor.getCount()); + cursor.moveToFirst(); + int realCount = cursor.getInt(0); + assertEquals(realCount, everyone.size()); + } + + // Currently passes add null sql or null rowmapper tests + public void testFailuresQueryWithCursorRowMapper() { + String sqlStart = "Select * from Person Where"; + CursorRowMapper personCursorRowMapper = new PersonCursorRowMapper(); + String sql = sqlStart + " FIRST_NAME=?"; + try { + template.query(sql, personCursorRowMapper, "John"); + fail("A DataAccessException should be thrown because the String parameter is not quoted"); + } catch (DataAccessException dae) { + } catch (Exception e) { + fail("This should throw a Spring DataAccessException not one from SQLite " + + e.getMessage()); + } + + try { + template.query("", Person.class); + fail("A DataAccessException should be thrown because there is no query string"); + } catch (DataAccessException dae) { + } catch (Exception e) { + fail("This should throw a Spring DataAccessException not one from SQLite " + + e.getMessage()); + } + + try { + template.query("Select * from Address", personCursorRowMapper); + fail("A DataAccessException should be thrown because they are Addresses not People"); + } catch (DataAccessException dae) { + } catch (Exception e) { + fail("This should throw a Spring DataAccessException not one from SQLite " + + e.getMessage()); + } + } + + private void assertJohnThePerson(Person john) { + assertEquals("Age", Integer.valueOf(42), john.getAge()); + assertEquals("First name", "John", john.getFirstName()); + assertEquals("Last name", "Doe", john.getLastName()); + assertEquals("Height", Double.valueOf("5.1d"), john.getHeight()); + assertTrue("Staff", john.isStaff()); + } + + private void assertJaneThePerson(Person jane) { + assertEquals("Age", Integer.valueOf(21), jane.getAge()); + assertEquals("First name", "Jane", jane.getFirstName()); + assertEquals("Last name", "Smith", jane.getLastName()); + assertEquals("Height", Double.valueOf("6.2D"), jane.getHeight()); + assertFalse("Staff", jane.isStaff()); + } + + private void assertAccount(Account account, String sql) { + Cursor cursor = dataBase.rawQuery(sql, null); + assertNotNull(cursor); + assertEquals(1, cursor.getCount()); + cursor.moveToFirst(); + Account checkAccount = new Account(); + checkAccount.setAccountType(cursor.getString(cursor + .getColumnIndex("ACCOUNT_TYPE"))); + checkAccount + .setAmount(cursor.getDouble(cursor.getColumnIndex("AMOUNT"))); + checkAccount.setYearAccountOpened(cursor.getInt(cursor + .getColumnIndex("YEAR_STARTED"))); + assertEquals(account, checkAccount); + } } diff --git a/test/com/perfectworldprogramming/mobile/orm/test/creator/TableCreatorTest.java b/test/com/perfectworldprogramming/mobile/orm/test/creator/TableCreatorTest.java index 2921cd5..8495e6b 100644 --- a/test/com/perfectworldprogramming/mobile/orm/test/creator/TableCreatorTest.java +++ b/test/com/perfectworldprogramming/mobile/orm/test/creator/TableCreatorTest.java @@ -2,21 +2,21 @@ import java.util.List; +import android.test.ActivityInstrumentationTestCase2; +import android.util.Log; import com.perfectworldprogramming.mobile.orm.creator.SQLLiteCreateStatementGenerator; import com.perfectworldprogramming.mobile.orm.test.Main; import com.perfectworldprogramming.mobile.orm.test.domain.Address; import com.perfectworldprogramming.mobile.orm.test.domain.Person; -import android.test.ActivityInstrumentationTestCase2; - /** * User: Mark Spritzler * Date: 3/14/11 * Time: 2:40 PM */ public class TableCreatorTest extends ActivityInstrumentationTestCase2
{ - + // TODO need more tests private SQLLiteCreateStatementGenerator SQLLiteCreateStatementGenerator = new SQLLiteCreateStatementGenerator(); @@ -24,26 +24,18 @@ public TableCreatorTest() { super("org.springframework.mobile.orm.test", Main.class); } - public void setUp() { - try { - super.setUp(); - } catch (Exception e) { - e.printStackTrace(); - } - } - @SuppressWarnings("unchecked") - public void testGenerateTables() { + public void testGenerateTables() { String statement = SQLLiteCreateStatementGenerator.createCreateStatement(Person.class); assertNotNull("Statement should not be null", statement); - System.out.println(statement); + Log.d("ORM", statement); SQLLiteCreateStatementGenerator.setClasses(new Class[]{Person.class, Address.class}); List createStatements = SQLLiteCreateStatementGenerator.getCreateStatements(); assertNotNull("Should return a list", createStatements); assertEquals("Should return two statements", 2, createStatements.size()); for (String aStatement : createStatements) { - System.out.println(aStatement); + Log.d("ORM", aStatement); } } diff --git a/test/com/perfectworldprogramming/mobile/orm/test/domain/Account.java b/test/com/perfectworldprogramming/mobile/orm/test/domain/Account.java index 1e03d79..b2945b2 100644 --- a/test/com/perfectworldprogramming/mobile/orm/test/domain/Account.java +++ b/test/com/perfectworldprogramming/mobile/orm/test/domain/Account.java @@ -5,17 +5,21 @@ import com.perfectworldprogramming.mobile.orm.annotations.PrimaryKey; public class Account { - - @PrimaryKey("ACCOUNT_ID") + public static final String PK_ACCOUNT_ID = "ACCOUNT_ID"; + public static final String COL_ACCOUNT_TYPE = "ACCOUNT_TYPE"; + public static final String COL_AMOUNT = "AMOUNT"; + public static final String COL_YEAR_STARTED = "YEAR_STARTED"; + + @PrimaryKey(PK_ACCOUNT_ID) private Long id; - @Column(value="ACCOUNT_TYPE", nullable=false, type=ColumnType.TEXT) + @Column(value=COL_ACCOUNT_TYPE, nullable=false, type=ColumnType.STRING) private String accountType; - @Column(value="AMOUNT", type=ColumnType.REAL) + @Column(value=COL_AMOUNT, type=ColumnType.STRING) private double amount; - @Column(value="YEAR_STARTED", type=ColumnType.INTEGER) + @Column(value=COL_YEAR_STARTED, type=ColumnType.INTEGER) private int yearAccountOpened; public Long getId() { diff --git a/test/com/perfectworldprogramming/mobile/orm/test/domain/Address.java b/test/com/perfectworldprogramming/mobile/orm/test/domain/Address.java index 2de31f1..ee4cee1 100644 --- a/test/com/perfectworldprogramming/mobile/orm/test/domain/Address.java +++ b/test/com/perfectworldprogramming/mobile/orm/test/domain/Address.java @@ -15,16 +15,16 @@ public class Address { @PrimaryKey(value = "ADDRESS_ID") private Long id; - @Column(value = "STREET", type = ColumnType.TEXT, nullable = false) + @Column(value = "STREET", type = ColumnType.STRING, nullable = false) private String street; - @Column(value = "CITY", type = ColumnType.TEXT, nullable = false) + @Column(value = "CITY", type = ColumnType.STRING, nullable = false) private String city; - @Column(value = "STATE", type = ColumnType.TEXT, nullable = false) + @Column(value = "STATE", type = ColumnType.STRING, nullable = false) private String state; - @Column(value = "ZIP_CODE", type = ColumnType.TEXT, nullable = false) + @Column(value = "ZIP_CODE", type = ColumnType.STRING, nullable = false) private String zipCode; @ForeignKey(value = "PERSON_ID") @@ -90,12 +90,12 @@ public void setPerson(Person person) { @Override public String toString() { - String address = "Id: " + id + - " Street: " + street + - " City: " + city + - " State: " + state + - " Zip Code: " + zipCode; - return address; + String address = "Id: " + id + + " Street: " + street + + " City: " + city + + " State: " + state + + " Zip Code: " + zipCode; + return address; } @Override diff --git a/test/com/perfectworldprogramming/mobile/orm/test/domain/NoPKDomain.java b/test/com/perfectworldprogramming/mobile/orm/test/domain/NoPKDomain.java index c49a8f3..8fc509a 100644 --- a/test/com/perfectworldprogramming/mobile/orm/test/domain/NoPKDomain.java +++ b/test/com/perfectworldprogramming/mobile/orm/test/domain/NoPKDomain.java @@ -6,10 +6,10 @@ public class NoPKDomain { - @Column(value="value1", type=ColumnType.TEXT) + @Column(value="value1", type=ColumnType.STRING) private String value1; - @Column(value="value1", type=ColumnType.TEXT) + @Column(value="value1", type=ColumnType.STRING) private String value2; @ForeignKey(value="PERSON_ID") diff --git a/test/com/perfectworldprogramming/mobile/orm/test/domain/Person.java b/test/com/perfectworldprogramming/mobile/orm/test/domain/Person.java index b3e21fd..5653b7a 100644 --- a/test/com/perfectworldprogramming/mobile/orm/test/domain/Person.java +++ b/test/com/perfectworldprogramming/mobile/orm/test/domain/Person.java @@ -1,13 +1,13 @@ package com.perfectworldprogramming.mobile.orm.test.domain; +import java.util.ArrayList; +import java.util.List; + import com.perfectworldprogramming.mobile.orm.annotations.Column; import com.perfectworldprogramming.mobile.orm.annotations.ColumnType; import com.perfectworldprogramming.mobile.orm.annotations.PrimaryKey; import com.perfectworldprogramming.mobile.orm.annotations.Transient; -import java.util.ArrayList; -import java.util.List; - /** * User: Mark Spritzler * Date: 3/14/11 @@ -15,34 +15,62 @@ */ public class Person { - @PrimaryKey(value = "PERSON_ID") + public static final String PK_PERSON ="PERSON_ID"; + public static final String COL_FIRST_NAME ="FIRST_NAME"; + public static final String COL_LAST_NAME ="LAST_NAME"; + public static final String COL_AGE ="AGE"; + public static final String COL_JACKET_SIZE ="JACKET_SIZE"; + public static final String COL_DEPENDANTS ="DEPENDANTS"; + public static final String COL_PETS ="PETS"; + public static final String COL_STAFF ="STAFF"; + public static final String COL_FEMALE ="FEMALE"; + public static final String COL_WEIGHT ="WEIGHT"; + public static final String COL_SHOE_SIZE ="SHOE_SIZE"; + public static final String COL_HEIGHT ="HEIGHT"; + public static final String COL_WEALTH ="WEALTH"; + public static final String COL_START_DATE ="START_DATE"; + + @PrimaryKey(value = PK_PERSON) private Long id; - @Column(value = "FIRST_NAME", type = ColumnType.TEXT, nullable = false) + @Column(value = COL_FIRST_NAME, type = ColumnType.STRING, nullable = false) private String firstName; - @Column(value = "LAST_NAME", type = ColumnType.TEXT, nullable = false) + @Column(value = COL_LAST_NAME, type = ColumnType.STRING, nullable = false) private String lastName; - @Column(value = "AGE", type = ColumnType.INTEGER, nullable = false) + @Column(value = COL_AGE, type = ColumnType.INTEGER, nullable = false) private Integer age; - @Column(value = "HEIGHT", type = ColumnType.REAL) + @Column(value = COL_JACKET_SIZE, type = ColumnType.INTEGER) + private int jacketSize; + + @Column(value = COL_HEIGHT, type = ColumnType.DOUBLE) private Double height; - @Column(value = "WEIGHT", type = ColumnType.REAL) + @Column(value = COL_WEIGHT, type = ColumnType.FLOAT) private Float weight; - @Column(value = "JACKET_SIZE", type = ColumnType.INTEGER) - private int jacketSize; + @Column(value = COL_DEPENDANTS, type = ColumnType.LONG) + private Long dependants; + + @Column(value = COL_PETS, type = ColumnType.LONG) + private long pets; - @Column(value = "SHOE_SIZE", type = ColumnType.REAL) + @Column(value = COL_SHOE_SIZE, type = ColumnType.FLOAT) private float shoeSize; - @Column(value = "WEALTH", type = ColumnType.REAL) + @Column(value = COL_WEALTH, type = ColumnType.DOUBLE) private double wealth; - + @Column(value = COL_STAFF, type = ColumnType.BOOLEAN) + private boolean staff; + + @Column(value = COL_FEMALE, type = ColumnType.BOOLEAN) + private Boolean female; + + @Column(value = COL_START_DATE, type = ColumnType.DATE) + private java.sql.Date startDate; @Transient private List
addresses; @@ -97,38 +125,88 @@ public void setHeight(Double height) { } public Float getWeight() { - return weight; - } + return weight; + } - public void setWeight(Float weight) { - this.weight = weight; - } + public void setWeight(Float weight) { + this.weight = weight; + } - public int getJacketSize() { - return jacketSize; - } + public int getJacketSize() { + return jacketSize; + } - public void setJacketSize(int jacketSize) { - this.jacketSize = jacketSize; - } + public void setJacketSize(int jacketSize) { + this.jacketSize = jacketSize; + } - public float getShoeSize() { - return shoeSize; - } + public float getShoeSize() { + return shoeSize; + } - public void setShoeSize(float shoeSize) { - this.shoeSize = shoeSize; - } + public void setShoeSize(float shoeSize) { + this.shoeSize = shoeSize; + } - public double getWealth() { - return wealth; - } + public double getWealth() { + return wealth; + } - public void setWealth(double wealth) { - this.wealth = wealth; - } + public void setWealth(double wealth) { + this.wealth = wealth; + } - public List
getAddresses() { + public boolean isStaff() + { + return staff; + } + + public void setStaff(boolean staff) + { + this.staff = staff; + } + + public Long getDependants() + { + return dependants; + } + + public void setDependants(Long dependants) + { + this.dependants = dependants; + } + + public long getPets() + { + return pets; + } + + public void setPets(long pets) + { + this.pets = pets; + } + + public Boolean getFemale() + { + return female; + } + + public void setFemale(Boolean female) + { + this.female = female; + } + + public java.sql.Date getStartDate() + { + return startDate; + } + + public void setStartDate(java.sql.Date startDate) + { + this.startDate = startDate; + } + + public List
getAddresses() { return addresses; } @@ -146,23 +224,26 @@ public void addAddress(Address address) { @Override public String toString() { - String person = "Id: " + id + - " First Name: " + firstName + - " Last Name: " + lastName + - " Age: " + age + - " Height: " + height + - " Weight: " + weight + - " Jacket Size: " + jacketSize + - " Shoe Size: " + shoeSize + - " Wealth: " + wealth; - if (addresses != null && addresses.size() > 0) { - String stringOfAddresses = "\t Addresses: \n"; - for (Address address : addresses) { - stringOfAddresses += "\tt " + address; - } - person += stringOfAddresses; - } - return person; + String person = "Id: " + id + + " First Name: " + firstName + + " Last Name: " + lastName + + " Age: " + age + + " Height: " + height + + " Weight: " + weight + + " Jacket Size: " + jacketSize + + " Shoe Size: " + shoeSize + + " Wealth: " + wealth + + " Staff: " + staff + + " Female: " + female+ + " Start Date: "+startDate; + if (addresses != null && addresses.size() > 0) { + String stringOfAddresses = "\t Addresses: \n"; + for (Address address : addresses) { + stringOfAddresses += "\tt " + address; + } + person += stringOfAddresses; + } + return person; } @Override diff --git a/test/com/perfectworldprogramming/mobile/orm/test/helper/AndroidSQLLiteOpenHelperTest.java b/test/com/perfectworldprogramming/mobile/orm/test/helper/AndroidSQLLiteOpenHelperTest.java index 3ebd499..0fa5496 100644 --- a/test/com/perfectworldprogramming/mobile/orm/test/helper/AndroidSQLLiteOpenHelperTest.java +++ b/test/com/perfectworldprogramming/mobile/orm/test/helper/AndroidSQLLiteOpenHelperTest.java @@ -25,11 +25,6 @@ public AndroidSQLLiteOpenHelperTest() { @SuppressWarnings("unchecked") public void setUp() { - try { - super.setUp(); - } catch (Exception e) { - e.printStackTrace(); - } Context ctx = this.getInstrumentation().getContext(); helper = new AndroidSQLLiteOpenHelper(ctx, new Class[]{Person.class, Address.class, Account.class}, "ormtest", 3); } diff --git a/test/com/perfectworldprogramming/mobile/orm/test/helper/DBHelperTests.java b/test/com/perfectworldprogramming/mobile/orm/test/helper/DBHelperTests.java index 89f433d..98c3586 100644 --- a/test/com/perfectworldprogramming/mobile/orm/test/helper/DBHelperTests.java +++ b/test/com/perfectworldprogramming/mobile/orm/test/helper/DBHelperTests.java @@ -1,5 +1,7 @@ package com.perfectworldprogramming.mobile.orm.test.helper; +import android.database.sqlite.SQLiteDatabase; +import android.test.ActivityInstrumentationTestCase2; import com.perfectworldprogramming.mobile.orm.helper.DBHelper; import com.perfectworldprogramming.mobile.orm.test.Main; @@ -7,36 +9,36 @@ import com.perfectworldprogramming.mobile.orm.test.domain.Address; import com.perfectworldprogramming.mobile.orm.test.domain.Person; -import android.database.sqlite.SQLiteDatabase; -import android.test.ActivityInstrumentationTestCase2; - /** - * User: Mark Spritzler - * Date: 3/14/11 - * Time: 9:24 PM + * User: Mark Spritzler Date: 3/14/11 Time: 9:24 PM */ -public class DBHelperTests extends ActivityInstrumentationTestCase2
{ +public class DBHelperTests extends ActivityInstrumentationTestCase2
+{ DBHelper helper; - public DBHelperTests() { - super("org.springframework.mobile.orm.test", Main.class); + public DBHelperTests() + { + super("org.springframework.mobile.orm.test", Main.class); } - + @SuppressWarnings("unchecked") - public void setUp() { - try { - super.setUp(); - } catch (Exception e) { - e.printStackTrace(); - } - helper = new DBHelper(this.getInstrumentation().getContext(), new Class[]{Person.class, Address.class, Account.class}, "ormtest", 3); + public void setUp() + { + helper = new DBHelper(this.getInstrumentation().getContext(), new Class[] { Person.class, Address.class, Account.class }, "ormtest", 3); + } + + @Override + public void tearDown() + { + helper.cleanup(); } - - //@Test - public void testStartDBTests() { + + // @Test + public void testStartDBTests() + { SQLiteDatabase db = helper.getSqlLiteDatabase(); assertNotNull(db); int version = db.getVersion(); - assertEquals("", 3, version); + assertEquals("Database version", 3, version); } } diff --git a/test/com/perfectworldprogramming/mobile/orm/test/interfaces/AddressCursorExtractor.java b/test/com/perfectworldprogramming/mobile/orm/test/interfaces/AddressCursorExtractor.java index 61c5ae1..8d56f9a 100644 --- a/test/com/perfectworldprogramming/mobile/orm/test/interfaces/AddressCursorExtractor.java +++ b/test/com/perfectworldprogramming/mobile/orm/test/interfaces/AddressCursorExtractor.java @@ -1,6 +1,7 @@ package com.perfectworldprogramming.mobile.orm.test.interfaces; +import com.perfectworldprogramming.mobile.orm.CursorAdapter; import com.perfectworldprogramming.mobile.orm.interfaces.CursorExtractor; import com.perfectworldprogramming.mobile.orm.test.domain.Address; import com.perfectworldprogramming.mobile.orm.test.domain.Person; @@ -14,32 +15,14 @@ */ public class AddressCursorExtractor implements CursorExtractor
{ - private static final String PERSON_ID = "PERSON_ID"; - private static final String FIRST_NAME = "FIRST_NAME"; - private static final String LAST_NAME = "LAST_NAME"; - private static final String HEIGHT = "HEIGHT"; - private static final String AGE = "AGE"; - private static final String ADDRESS_ID = "ADDRESS_ID"; - private static final String STREET = "STREET"; - private static final String CITY = "CITY"; - private static final String STATE = "STATE"; - private static final String ZIP_CODE = "ZIP_CODE"; - @Override public Address extractData(Cursor cursor) { Address address = null; if (cursor != null) { if (cursor.moveToFirst()) { - address = new Address(cursor.getLong(cursor.getColumnIndex(ADDRESS_ID))); - address.setCity(cursor.getString(cursor.getColumnIndex(CITY))); - address.setState(cursor.getString(cursor.getColumnIndex(STATE))); - address.setStreet(cursor.getString(cursor.getColumnIndex(STREET))); - address.setZipCode(cursor.getString(cursor.getColumnIndex(ZIP_CODE))); - Person person = new Person(cursor.getLong(cursor.getColumnIndex(PERSON_ID))); - person.setFirstName(cursor.getString(cursor.getColumnIndex(FIRST_NAME))); - person.setLastName(cursor.getString(cursor.getColumnIndex(LAST_NAME))); - person.setHeight(cursor.getDouble(cursor.getColumnIndex(HEIGHT))); - person.setAge(cursor.getInt(cursor.getColumnIndex(AGE))); + CursorAdapter adapter = new CursorAdapter(); + address = adapter.adaptCurrentFromCursor(cursor, Address.class); + Person person = adapter.adaptFromCursor(cursor, Person.class); address.setPerson(person); } } diff --git a/test/com/perfectworldprogramming/mobile/orm/test/interfaces/AddressCursorRowMapper.java b/test/com/perfectworldprogramming/mobile/orm/test/interfaces/AddressCursorRowMapper.java index eb64184..5252f09 100644 --- a/test/com/perfectworldprogramming/mobile/orm/test/interfaces/AddressCursorRowMapper.java +++ b/test/com/perfectworldprogramming/mobile/orm/test/interfaces/AddressCursorRowMapper.java @@ -1,31 +1,21 @@ package com.perfectworldprogramming.mobile.orm.test.interfaces; +import android.database.Cursor; + +import com.perfectworldprogramming.mobile.orm.CursorAdapter; import com.perfectworldprogramming.mobile.orm.interfaces.CursorRowMapper; import com.perfectworldprogramming.mobile.orm.test.domain.Address; -import android.database.Cursor; - /** * User: Mark Spritzler * Date: 4/6/11 * Time: 10:55 AM */ public class AddressCursorRowMapper implements CursorRowMapper
{ - - private static final String ID = "ADDRESS_ID"; - private static final String STREET = "STREET"; - private static final String CITY = "CITY"; - private static final String STATE = "STATE"; - private static final String ZIP_CODE = "ZIP_CODE"; @Override public Address mapRow(Cursor cursor, int rowNum) { - Address address = new Address(cursor.getLong(cursor.getColumnIndex(ID))); - address.setCity(cursor.getString(cursor.getColumnIndex(CITY))); - address.setState(cursor.getString(cursor.getColumnIndex(STATE))); - address.setStreet(cursor.getString(cursor.getColumnIndex(STREET))); - address.setZipCode(cursor.getString(cursor.getColumnIndex(ZIP_CODE))); - return address; + return new CursorAdapter().adaptCurrentFromCursor(cursor, Address.class); } } diff --git a/test/com/perfectworldprogramming/mobile/orm/test/interfaces/CursorExtractorTests.java b/test/com/perfectworldprogramming/mobile/orm/test/interfaces/CursorExtractorTests.java index 33aeb43..3fc8436 100644 --- a/test/com/perfectworldprogramming/mobile/orm/test/interfaces/CursorExtractorTests.java +++ b/test/com/perfectworldprogramming/mobile/orm/test/interfaces/CursorExtractorTests.java @@ -3,6 +3,7 @@ import java.util.ArrayList; import java.util.List; +import android.test.ActivityInstrumentationTestCase2; import com.perfectworldprogramming.mobile.orm.AndroidSQLiteTemplate; import com.perfectworldprogramming.mobile.orm.helper.DBHelper; @@ -11,8 +12,6 @@ import com.perfectworldprogramming.mobile.orm.test.domain.Address; import com.perfectworldprogramming.mobile.orm.test.domain.Person; -import android.test.ActivityInstrumentationTestCase2; - /** * User: Mark Spritzler * Date: 4/6/11 @@ -32,24 +31,25 @@ public CursorExtractorTests() { @SuppressWarnings("unchecked") public void setUp() { - try { - super.setUp(); - } catch (Exception e) { - e.printStackTrace(); - } helper = new DBHelper(this.getInstrumentation().getContext(), new Class[]{Person.class, Address.class, Account.class}, "ormtest", 3); template = new AndroidSQLiteTemplate(helper.getSqlLiteDatabase()); SampleDataHelper.addDataToDatabase(template); } + + @Override + protected void tearDown() throws Exception + { + helper.cleanup(); + } public void testSuccessfulPersonExtractor() { String sql = "SELECT * from PERSON p, ADDRESS a where a.PERSON_ID = p.PERSON_ID and p.FIRST_NAME = '?'"; Person person = template.queryForObject(sql, new PersonCursorExtractor(), "John"); assertNotNull(person); - assertEquals(new Integer(42), person.getAge()); + assertEquals(Integer.valueOf(42), person.getAge()); assertEquals("John", person.getFirstName()); assertEquals("Doe", person.getLastName()); - assertEquals(new Double("5.1d"), person.getHeight()); + assertEquals(Double.valueOf("5.1d"), person.getHeight()); List
addresses = person.getAddresses(); assertNotNull(addresses); assertEquals(2, addresses.size()); @@ -65,9 +65,9 @@ public void testSuccessfulAddressExtractor() { assertEquals("12345", address.getZipCode()); Person person = address.getPerson(); assertNotNull(person); - assertEquals(new Integer(42), person.getAge()); + assertEquals(Integer.valueOf(42), person.getAge()); assertEquals("John", person.getFirstName()); assertEquals("Doe", person.getLastName()); - assertEquals(new Double("5.1d"), person.getHeight()); + assertEquals(Double.valueOf("5.1d"), person.getHeight()); } } diff --git a/test/com/perfectworldprogramming/mobile/orm/test/interfaces/CursorRowMapperTests.java b/test/com/perfectworldprogramming/mobile/orm/test/interfaces/CursorRowMapperTests.java index 564e3a8..f456e7a 100644 --- a/test/com/perfectworldprogramming/mobile/orm/test/interfaces/CursorRowMapperTests.java +++ b/test/com/perfectworldprogramming/mobile/orm/test/interfaces/CursorRowMapperTests.java @@ -3,6 +3,7 @@ import java.util.ArrayList; import java.util.List; +import android.test.ActivityInstrumentationTestCase2; import com.perfectworldprogramming.mobile.orm.AndroidSQLiteTemplate; import com.perfectworldprogramming.mobile.orm.exception.ExtraResultsException; @@ -12,53 +13,58 @@ import com.perfectworldprogramming.mobile.orm.test.domain.Address; import com.perfectworldprogramming.mobile.orm.test.domain.Person; -import android.test.ActivityInstrumentationTestCase2; - /** - * User: Mark Spritzler - * Date: 4/6/11 - * Time: 10:50 AM + * User: Mark Spritzler Date: 4/6/11 Time: 10:50 AM */ -public class CursorRowMapperTests extends ActivityInstrumentationTestCase2
{ +public class CursorRowMapperTests extends ActivityInstrumentationTestCase2
+{ - - public CursorRowMapperTests() { - super("org.springframework.mobile.orm.test", Main.class); + public CursorRowMapperTests() + { + super("org.springframework.mobile.orm.test", Main.class); } AndroidSQLiteTemplate template; + DBHelper helper; List samplePeople = new ArrayList(); List
sampleAddress = new ArrayList
(); @SuppressWarnings("unchecked") - public void setUp() { - try { - super.setUp(); - } catch (Exception e) { - e.printStackTrace(); - } - DBHelper helper = new DBHelper(this.getInstrumentation().getContext(), new Class[]{Person.class, Address.class, Account.class}, "ormtest", 3); + public void setUp() + { + helper = new DBHelper(this.getInstrumentation().getContext(), new Class[] { Person.class, Address.class, Account.class }, "ormtest", 3); + template = new AndroidSQLiteTemplate(helper.getSqlLiteDatabase()); SampleDataHelper.addDataToDatabase(template); } + @Override + protected void tearDown() throws Exception + { + helper.cleanup(); + } + // Address tests first - public void testSuccessfulAddressMapperTest() { + public void testSuccessfulAddressMapperTest() + { List
addresses = template.query("Select * from ADDRESS", new AddressCursorRowMapper()); assertNotNull(addresses); assertEquals(2, addresses.size()); - for (Address address : sampleAddress) { + for (Address address : sampleAddress) + { assertTrue(addresses.contains(address)); } } - public void testAddressQueryWithNoResultsTest () { + public void testAddressQueryWithNoResultsTest() + { List
addresses = template.query("Select * from ADDRESS where 1=2", new AddressCursorRowMapper()); assertNotNull(addresses); assertEquals(0, addresses.size()); } - public void testAddressSingleObjectSuccessTest () { + public void testAddressSingleObjectSuccessTest() + { Address address = template.queryForObject("Select * from ADDRESS where ZIP_CODE='?'", new AddressCursorRowMapper(), "12345"); assertNotNull(address); assertEquals("Philadelphia", address.getCity()); @@ -67,69 +73,103 @@ public void testAddressSingleObjectSuccessTest () { assertEquals("12345", address.getZipCode()); } - //@Test(expected = ExtraResultsException.class) - public void testAddressSingleObjectReturnsNoResultsTest() { - try { - template.queryForObject("Select * from ADDRESS where ZIP_CODE='?'", new AddressCursorRowMapper(), "98765"); - fail("This test should have thrown an ExtraResultsException stating that 0 rows were returned when expecting 1"); - } catch (ExtraResultsException ere) { - String numberOfResults = "0"; - assertEquals("Expected one row returned by query but received " + numberOfResults, ere.getMessage()); - } + // @Test(expected = ExtraResultsException.class) + public void testAddressSingleObjectReturnsNoResultsTest() + { + try + { + template.queryForObject("Select * from ADDRESS where ZIP_CODE='?'", new AddressCursorRowMapper(), "98765"); + fail("This test should have thrown an ExtraResultsException stating that 0 rows were returned when expecting 1"); + } + catch (ExtraResultsException ere) + { + String numberOfResults = "0"; + assertEquals("Expected one row returned by query but received " + numberOfResults, ere.getMessage()); + } + catch (Exception e) + { + e.printStackTrace(); + fail("Should have thrown ExtraResultsException, not " + e.getClass().getName()); + } } - //@Test(expected = ExtraResultsException.class) - public void testAddressSingleObjectReturnsTwoRowsTest() { - try { - template.queryForObject("Select * from ADDRESS", new AddressCursorRowMapper()); - } catch (ExtraResultsException ere) { - String numberOfResults = "2"; - assertEquals("Expected one row returned by query but received " + numberOfResults, ere.getMessage()); - } + // @Test(expected = ExtraResultsException.class) + public void testAddressSingleObjectReturnsTwoRowsTest() + { + try + { + template.queryForObject("Select * from ADDRESS", new AddressCursorRowMapper()); + } + catch (ExtraResultsException ere) + { + String numberOfResults = "2"; + assertEquals("Expected one row returned by query but received " + numberOfResults, ere.getMessage()); + } } // Person tests second - public void testSuccessfulPersonMapperTest() { + public void testSuccessfulPersonMapperTest() + { List persons = template.query("Select * from PERSON", new PersonCursorRowMapper()); assertNotNull(persons); assertEquals(2, persons.size()); - for (Person person : samplePeople) { + for (Person person : samplePeople) + { assertTrue(persons.contains(person)); } } - public void testPersonQueryWithNoResultsTest () { + public void testPersonQueryWithNoResultsTest() + { List persons = template.query("Select * from ADDRESS where 1=2", new PersonCursorRowMapper()); assertNotNull(persons); assertEquals(0, persons.size()); } - public void testPersonSingleObjectSuccessTest () { + public void testPersonSingleObjectSuccessTest() + { Person person = template.queryForObject("Select * from PERSON where FIRST_NAME='?'", new PersonCursorRowMapper(), "John"); assertNotNull(person); - assertEquals(new Integer(42), person.getAge()); + assertEquals(Integer.valueOf(42), person.getAge()); assertEquals("John", person.getFirstName()); assertEquals("Doe", person.getLastName()); - assertEquals(new Double("5.1d"), person.getHeight()); + assertEquals(Double.valueOf("5.1d"), person.getHeight()); } - //@Test(expected = ExtraResultsException.class) - public void testPersonSingleObjectReturnsNoResultsTest() { - try { - template.queryForObject("Select * from PERSON where FIRST_NAME='?' and AGE=?", new PersonCursorRowMapper(), "George", 37); - } catch (ExtraResultsException ere) { - String numberOfResults = "0"; - assertEquals("Expected one row returned by query but received " + numberOfResults, ere.getMessage()); - } + // @Test(expected = ExtraResultsException.class) + public void testPersonSingleObjectReturnsNoResultsTest() + { + try + { + template.queryForObject("Select * from PERSON where FIRST_NAME='?' and AGE=?", new PersonCursorRowMapper(), "George", 37); + } + catch (ExtraResultsException ere) + { + String numberOfResults = "0"; + assertEquals("Expected one row returned by query but received " + numberOfResults, ere.getMessage()); + } + catch (Exception e) + { + e.printStackTrace(); + fail("Should have thrown ExtraResultsException, not " + e.getClass().getName()); + } } - //@Test(expected = ExtraResultsException.class) - public void testPersonSingleObjectReturnsTwoRowsTest() { - try { - template.queryForObject("Select * from PERSON", new PersonCursorRowMapper()); - } catch (ExtraResultsException ere) { - String numberOfResults = "2"; - assertEquals("Expected one row returned by query but received " + numberOfResults, ere.getMessage()); - } + // @Test(expected = ExtraResultsException.class) + public void testPersonSingleObjectReturnsTwoRowsTest() + { + try + { + template.queryForObject("Select * from PERSON", new PersonCursorRowMapper()); + } + catch (ExtraResultsException ere) + { + String numberOfResults = "2"; + assertEquals("Expected one row returned by query but received " + numberOfResults, ere.getMessage()); + } + catch (Exception e) + { + fail("Should have thrown ExtraResultsException, not " + e.getClass().getName()); + } } } diff --git a/test/com/perfectworldprogramming/mobile/orm/test/interfaces/PersonCursorExtractor.java b/test/com/perfectworldprogramming/mobile/orm/test/interfaces/PersonCursorExtractor.java index fa75cea..32243ab 100644 --- a/test/com/perfectworldprogramming/mobile/orm/test/interfaces/PersonCursorExtractor.java +++ b/test/com/perfectworldprogramming/mobile/orm/test/interfaces/PersonCursorExtractor.java @@ -1,8 +1,10 @@ package com.perfectworldprogramming.mobile.orm.test.interfaces; -import android.database.Cursor; +import java.util.List; +import android.database.Cursor; +import com.perfectworldprogramming.mobile.orm.CursorAdapter; import com.perfectworldprogramming.mobile.orm.interfaces.CursorExtractor; import com.perfectworldprogramming.mobile.orm.test.domain.Address; import com.perfectworldprogramming.mobile.orm.test.domain.Person; @@ -14,35 +16,28 @@ */ public class PersonCursorExtractor implements CursorExtractor { - private static final String PERSON_ID = "PERSON_ID"; - private static final String FIRST_NAME = "FIRST_NAME"; - private static final String LAST_NAME = "LAST_NAME"; - private static final String HEIGHT = "HEIGHT"; - private static final String AGE = "AGE"; - private static final String ADDRESS_ID = "ADDRESS_ID"; - private static final String STREET = "STREET"; - private static final String CITY = "CITY"; - private static final String STATE = "STATE"; - private static final String ZIP_CODE = "ZIP_CODE"; - @Override public Person extractData(Cursor cursor) { Person person = null; if (cursor != null) { if (cursor.moveToFirst()) { - person = new Person(cursor.getLong(cursor.getColumnIndex(PERSON_ID))); - person.setFirstName(cursor.getString(cursor.getColumnIndex(FIRST_NAME))); - person.setLastName(cursor.getString(cursor.getColumnIndex(LAST_NAME))); - person.setHeight(cursor.getDouble(cursor.getColumnIndex(HEIGHT))); - person.setAge(cursor.getInt(cursor.getColumnIndex(AGE))); - do { - Address address = new Address(cursor.getLong(cursor.getColumnIndex(ADDRESS_ID))); - address.setCity(cursor.getString(cursor.getColumnIndex(CITY))); - address.setState(cursor.getString(cursor.getColumnIndex(STATE))); - address.setStreet(cursor.getString(cursor.getColumnIndex(STREET))); - address.setZipCode(cursor.getString(cursor.getColumnIndex(ZIP_CODE))); - person.addAddress(address); - } while (cursor.moveToNext()); + CursorAdapter adapter = new CursorAdapter(); + person = adapter.adaptCurrentFromCursor(cursor, Person.class); + //cursor.getLong(cursor.getColumnIndex(PERSON_ID))); +// person.setFirstName(cursor.getString(cursor.getColumnIndex(FIRST_NAME))); +// person.setLastName(cursor.getString(cursor.getColumnIndex(LAST_NAME))); +// person.setHeight(cursor.getDouble(cursor.getColumnIndex(HEIGHT))); +// person.setAge(cursor.getInt(cursor.getColumnIndex(AGE))); + List
addresses = adapter.adaptListFromCursor(cursor, Address.class); +// do { +// Address address = new Address(cursor.getLong(cursor.getColumnIndex(ADDRESS_ID))); +// address.setCity(cursor.getString(cursor.getColumnIndex(CITY))); +// address.setState(cursor.getString(cursor.getColumnIndex(STATE))); +// address.setStreet(cursor.getString(cursor.getColumnIndex(STREET))); +// address.setZipCode(cursor.getString(cursor.getColumnIndex(ZIP_CODE))); +// person.addAddress(address); +// } while (cursor.moveToNext()); + person.setAddresses(addresses); } } return person; diff --git a/test/com/perfectworldprogramming/mobile/orm/test/interfaces/PersonCursorRowMapper.java b/test/com/perfectworldprogramming/mobile/orm/test/interfaces/PersonCursorRowMapper.java index be9e21a..0da9fc4 100644 --- a/test/com/perfectworldprogramming/mobile/orm/test/interfaces/PersonCursorRowMapper.java +++ b/test/com/perfectworldprogramming/mobile/orm/test/interfaces/PersonCursorRowMapper.java @@ -1,31 +1,20 @@ package com.perfectworldprogramming.mobile.orm.test.interfaces; +import android.database.Cursor; + +import com.perfectworldprogramming.mobile.orm.CursorAdapter; import com.perfectworldprogramming.mobile.orm.interfaces.CursorRowMapper; import com.perfectworldprogramming.mobile.orm.test.domain.Person; -import android.database.Cursor; - /** * User: Mark Spritzler * Date: 4/6/11 * Time: 10:50 AM */ public class PersonCursorRowMapper implements CursorRowMapper { - - private static final String ID = "PERSON_ID"; - private static final String FIRST_NAME = "FIRST_NAME"; - private static final String LAST_NAME = "LAST_NAME"; - private static final String HEIGHT = "HEIGHT"; - private static final String AGE = "AGE"; - @Override public Person mapRow(Cursor cursor, int rowNum) { - Person person = new Person(cursor.getLong(cursor.getColumnIndex(ID))); - person.setFirstName(cursor.getString(cursor.getColumnIndex(FIRST_NAME))); - person.setLastName(cursor.getString(cursor.getColumnIndex(LAST_NAME))); - person.setHeight(cursor.getDouble(cursor.getColumnIndex(HEIGHT))); - person.setAge(cursor.getInt(cursor.getColumnIndex(AGE))); - return person; + return new CursorAdapter().adaptCurrentFromCursor(cursor, Person.class); } } diff --git a/test/com/perfectworldprogramming/mobile/orm/test/interfaces/SampleDataHelper.java b/test/com/perfectworldprogramming/mobile/orm/test/interfaces/SampleDataHelper.java index 08c1f24..0731d5f 100644 --- a/test/com/perfectworldprogramming/mobile/orm/test/interfaces/SampleDataHelper.java +++ b/test/com/perfectworldprogramming/mobile/orm/test/interfaces/SampleDataHelper.java @@ -82,22 +82,32 @@ private static List createPeople() { person1.setAge(42); person1.setFirstName("John"); person1.setLastName("Doe"); - person1.setHeight(5.1d); - person1.setWeight(175.6f); + person1.setHeight(5.1D); + person1.setWeight(175.6F); person1.setJacketSize(43); - person1.setShoeSize(9.5f); - person1.setWealth(210000.00d); + person1.setShoeSize(9.5F); + person1.setWealth(210000.00D); + person1.setStaff(true); + person1.setFemale(false); + person1.setDependants(2L); + person1.setPets(1L); + person1.setStartDate(new java.sql.Date(2012-1900, 1, 1)); samplePeople.add(person1); Person person2 = new Person(); person2.setAge(21); - person2.setHeight(6.2d); - person2.setFirstName("Bill"); - person2.setLastName("Smith"); - person1.setWeight(225.6f); - person1.setJacketSize(48); - person1.setShoeSize(12.5f); - person1.setWealth(10000.00d); + person2.setHeight(6.2D); + person2.setFirstName("Jane"); + person2.setLastName("Smith"); + person2.setWeight(225.6F); + person2.setJacketSize(48); + person2.setShoeSize(12.5F); + person2.setWealth(10000.00D); + person2.setStaff(false); + person2.setFemale(true); + person2.setDependants(0L); + person2.setPets(3L); // cat lady + person2.setStartDate(new java.sql.Date(2012-1900, 2, 3)); samplePeople.add(person2); return samplePeople; @@ -108,56 +118,56 @@ private static List createAccounts() { //For successful delete Account account1 = new Account(); account1.setAccountType("Personal"); - account1.setAmount(23.00d); + account1.setAmount(23.00D); account1.setYearAccountOpened(1989); accounts.add(account1); //For successful delete Account account2 = new Account(); account2.setAccountType("Business"); - account2.setAmount(560000.00d); + account2.setAmount(560000.00D); account2.setYearAccountOpened(1990); accounts.add(account2); // for successful delete Account account3 = new Account(); account3.setAccountType("Business"); - account3.setAmount(475.60d); + account3.setAmount(475.60D); account3.setYearAccountOpened(1991); accounts.add(account3); // for successful delete Account account4 = new Account(); account4.setAccountType("Business"); - account4.setAmount(4200.00d); + account4.setAmount(4200.00D); account4.setYearAccountOpened(2000); accounts.add(account4); // for successful update Account account5 = new Account(); account5.setAccountType("Business Plus"); - account5.setAmount(4200.00d); + account5.setAmount(4200.00D); account5.setYearAccountOpened(2001); accounts.add(account5); // for successful update Account account6 = new Account(); account6.setAccountType("Personal Plus"); - account6.setAmount(4200.00d); + account6.setAmount(4200.00D); account6.setYearAccountOpened(2002); accounts.add(account6); // for successful update Account account7 = new Account(); account7.setAccountType("Personal Plus"); - account7.setAmount(4200.00d); + account7.setAmount(4200.00D); account7.setYearAccountOpened(2003); - accounts.add(account7); + accounts.add(account7); // for successful update Account account8 = new Account(); account8.setAccountType("Business Plus"); - account8.setAmount(4200.00d); + account8.setAmount(4200.00D); account8.setYearAccountOpened(2011); accounts.add(account8); diff --git a/test/com/perfectworldprogramming/mobile/orm/test/oql/OqlQueryParameterTest.java b/test/com/perfectworldprogramming/mobile/orm/test/oql/OqlQueryParameterTest.java new file mode 100644 index 0000000..f972719 --- /dev/null +++ b/test/com/perfectworldprogramming/mobile/orm/test/oql/OqlQueryParameterTest.java @@ -0,0 +1,172 @@ +package com.perfectworldprogramming.mobile.orm.test.oql; + +import java.util.List; + +import android.database.sqlite.SQLiteDatabase; +import android.test.ActivityInstrumentationTestCase2; + +import com.perfectworldprogramming.mobile.orm.helper.DBHelper; +import com.perfectworldprogramming.mobile.orm.oql.AndroidOqlTemplate; +import com.perfectworldprogramming.mobile.orm.oql.OqlParameter; +import com.perfectworldprogramming.mobile.orm.test.AndroidSQLiteTemplateTests; +import com.perfectworldprogramming.mobile.orm.test.Main; +import com.perfectworldprogramming.mobile.orm.test.domain.Account; +import com.perfectworldprogramming.mobile.orm.test.domain.Address; +import com.perfectworldprogramming.mobile.orm.test.domain.Person; +import com.perfectworldprogramming.mobile.orm.test.interfaces.SampleDataHelper; + +/** + * Essentially a copy of {@link AndroidSQLiteTemplateTests} using {@link QueryParameter}s + * so that database conversion of values is hidden from domain layer, excluding the column name, + * which is already declared in the domain classes. + * @author David O'Meara + * @since 10/06/2012 + * + */ +public class OqlQueryParameterTest extends ActivityInstrumentationTestCase2
{ + + AndroidOqlTemplate template; + DBHelper helper; + List sampleAccounts; + + SQLiteDatabase dataBase; + + public OqlQueryParameterTest() { + super("org.springframework.mobile.orm.test", Main.class); + } + + @SuppressWarnings("unchecked") + public void setUp() { + helper = new DBHelper(this.getInstrumentation().getContext(), new Class[]{Person.class, Address.class, Account.class}, "ormtest", 3); + dataBase = helper.getSqlLiteDatabase(); + template = new AndroidOqlTemplate(helper.getSqlLiteDatabase()); + SampleDataHelper.addDataToDatabase(template); + } + + @Override + protected void tearDown() throws Exception + { + helper.cleanup(); + } + + // Currently passes + public void testSqlQueryByPrimaryKey() + { + OqlParameter param = new OqlParameter(Person.class, "id", 1L); + String oql = "SELECT * from Person where [id] = ?"; + Person person = template.queryForObject(oql, Person.class, param); + assertNotNull(person); + assertEquals("ID", Long.valueOf(1), person.getId()); + assertEquals("First name", "John", person.getFirstName()); + } + + public void testSqlQueryByString() + { + OqlParameter param = new OqlParameter(Person.class, "firstName", "John"); + String oql = "SELECT * from Person where [firstName] = '?'"; + + Person person = template.queryForObject(oql, Person.class, param); + assertNotNull(person); + assertEquals("ID", Long.valueOf(1), person.getId()); + assertEquals("First name", "John", person.getFirstName()); + } + + public void testSqlQueryByInteger() + { + OqlParameter param = new OqlParameter(Person.class, "age", 42); + + String oql = "SELECT * from Person where [age] = ?"; + Person person = template.queryForObject(oql, Person.class, param); + assertNotNull(person); + assertEquals("ID", Long.valueOf(1), person.getId()); + assertEquals("First name", "John", person.getFirstName()); + assertEquals("Age", Integer.valueOf(42), person.getAge()); + + param = new OqlParameter(Person.class, "jacketSize", 43); + oql = "SELECT * from Person where [jacketSize] = ?"; + person = template.queryForObject(oql, Person.class, param); + assertNotNull(person); + assertEquals("ID", Long.valueOf(1), person.getId()); + assertEquals("First name", "John", person.getFirstName()); + assertEquals("Age", Integer.valueOf(42), person.getAge()); + assertEquals("Jacket size", 43, person.getJacketSize()); + } + + public void testSqlQueryByLong() + { + OqlParameter param = new OqlParameter(Person.class, "dependants", 2D); + String oql = "SELECT * from Person where [dependants] = ?"; + Person person = template.queryForObject(oql, Person.class, param); + assertNotNull(person); + assertEquals("ID", Long.valueOf(1), person.getId()); + assertEquals("First name", "John", person.getFirstName()); + + param = new OqlParameter(Person.class, "pets", 1D); + oql = "SELECT * from Person where [pets] = ?"; + person = template.queryForObject(oql, Person.class, param); + assertNotNull(person); + assertEquals("ID", Long.valueOf(1), person.getId()); + assertEquals("First name", "John", person.getFirstName()); + } + + public void testSqlQueryByBoolean() + { + OqlParameter param = new OqlParameter(Person.class, "staff", true); + String oql = "SELECT * from Person where "+Person.COL_STAFF+" = ?"; + Person person = template.queryForObject(oql, Person.class, param); + assertNotNull(person); + assertEquals("ID", Long.valueOf(1), person.getId()); + assertEquals("First name", "John", person.getFirstName()); + + param = new OqlParameter(Person.class, "female", false); + oql = "SELECT * from Person where [female] = ?"; + person = template.queryForObject(oql, Person.class, param); + assertNotNull(person); + assertEquals("ID", Long.valueOf(1), person.getId()); + assertEquals("First name", "John", person.getFirstName()); + } + + public void testSqlQueryByFloat() + { + OqlParameter param = new OqlParameter(Person.class, "weight", 200F); + String oql = "SELECT * from Person where [weight] < ?"; + Person person = template.queryForObject(oql, Person.class, param); + assertNotNull(person); + assertEquals("ID", Long.valueOf(1), person.getId()); + assertEquals("First name", "John", person.getFirstName()); + + param = new OqlParameter(Person.class, "shoeSize", 10F); + oql = "SELECT * from Person where [shoeSize] < ?"; + person = template.queryForObject(oql, Person.class, param); + assertNotNull(person); + assertEquals("ID", Long.valueOf(1), person.getId()); + assertEquals("First name", "John", person.getFirstName()); + } + + public void testSqlQueryByDouble() + { + OqlParameter param = new OqlParameter(Person.class, "height", 6D); + String oql = "SELECT * from Person where [height] < ?"; + Person person = template.queryForObject(oql, Person.class, param); + assertNotNull(person); + assertEquals("ID", Long.valueOf(1), person.getId()); + assertEquals("First name", "John", person.getFirstName()); + + param = new OqlParameter(Person.class, "wealth", 200000D); + oql = "SELECT * from Person where [wealth] > ?"; + person = template.queryForObject(oql, Person.class, param); + assertNotNull(person); + assertEquals("ID", Long.valueOf(1), person.getId()); + assertEquals("First name", "John", person.getFirstName()); + } + + public void testSqlQueryByDate() + { + OqlParameter param = new OqlParameter(Person.class, "startDate", new java.sql.Date(2012-1900, 2, 1)); + String oql = "SELECT * from Person where [startDate] < ?"; + Person person = template.queryForObject(oql, Person.class, param); + assertNotNull(person); + assertEquals("ID", Long.valueOf(1), person.getId()); + assertEquals("First name", "John", person.getFirstName()); + } +} diff --git a/test/com/perfectworldprogramming/mobile/orm/test/oql/QueryParserTest.java b/test/com/perfectworldprogramming/mobile/orm/test/oql/QueryParserTest.java new file mode 100644 index 0000000..bf2d81a --- /dev/null +++ b/test/com/perfectworldprogramming/mobile/orm/test/oql/QueryParserTest.java @@ -0,0 +1,146 @@ +package com.perfectworldprogramming.mobile.orm.test.oql; + +import java.util.ArrayList; +import java.util.List; + +import android.test.ActivityInstrumentationTestCase2; + +import com.perfectworldprogramming.mobile.orm.exception.FieldNotFoundException; +import com.perfectworldprogramming.mobile.orm.oql.QueryParser; +import com.perfectworldprogramming.mobile.orm.test.Main; +import com.perfectworldprogramming.mobile.orm.test.domain.Address; +import com.perfectworldprogramming.mobile.orm.test.domain.Person; + +public class QueryParserTest extends ActivityInstrumentationTestCase2
+{ + public QueryParserTest() + { + super("org.springframework.mobile.orm.test", Main.class); + } + + public void testUnchanged() + { + final String input = "select * from Person"; + List> singleton = new ArrayList>(); + singleton.add(Person.class); + assertEquals(input, QueryParser.parse(input, singleton)); + } + + public void testUnchanged2() + { + final String input = "select * from Person where FIRST_NAME = ?"; + List> singleton = new ArrayList>(); + singleton.add(Person.class); + assertEquals(input, QueryParser.parse(input, singleton)); + } + + public void testSingleSimple() + { + final String input = "select * from Person where [firstName] = ?"; + final String expected = "select * from Person where FIRST_NAME = ?"; + List> singleton = new ArrayList>(); + singleton.add(Person.class); + final String actual = QueryParser.parse(input, singleton); + assertEquals(expected, actual); + } + + public void testMultipleSimple() + { + final String input = "select * from Person where [firstName] = ? and [age] < ?"; + final String expected = "select * from Person where FIRST_NAME = ? and AGE < ?"; + List> singleton = new ArrayList>(); + singleton.add(Person.class); + final String actual = QueryParser.parse(input, singleton); + assertEquals(expected, actual); + } + + public void testSingleComplex() + { + final String input = "select * from Person where [Person.firstName] = ?"; + final String expected = "select * from Person where FIRST_NAME = ?"; + List> singleton = new ArrayList>(); + singleton.add(Person.class); + final String actual = QueryParser.parse(input, singleton); + assertEquals(expected, actual); + } + + public void testMultipleComplex() + { + final String input = "select * from Person where [Person.firstName] = ? and [Person.age] < ?"; + final String expected = "select * from Person where FIRST_NAME = ? and AGE < ?"; + List> singleton = new ArrayList>(); + singleton.add(Person.class); + final String actual = QueryParser.parse(input, singleton); + assertEquals(expected, actual); + } + + public void testFailSimpleMatchInvalidField() + { + final String input = "select * from Person where [abcdef] = ?"; + List> singleton = new ArrayList>(); + singleton.add(Person.class); + try + { + QueryParser.parse(input, singleton); + fail("FieldNotFound expected"); + } + catch(FieldNotFoundException e) + { + // expected + } + } + public void testFailSimpleMatchInvalidClass() + { + final String input = "select * from Person where [age] = ?"; + List> singleton = new ArrayList>(); + singleton.add(Address.class); + try + { + QueryParser.parse(input, singleton); + fail("FieldNotFound expected"); + } + catch(FieldNotFoundException e) + { + // expected + } + } + + public void testFailSimpleMatchInvalidClassAndField() + { + final String input = "select * from Person where [Address.age] = ?"; + List> singleton = new ArrayList>(); + singleton.add(Person.class); + singleton.add(Address.class); + try + { + QueryParser.parse(input, singleton); + fail("FieldNotFound expected"); + } + catch(FieldNotFoundException e) + { + // expected + } + } + + public void testMultipleClasses() + { + final String input = "select * from Person p, Address a where p.[lastName] = ? and p.[Person.id]=a.[Address.person]"; + final String expected = "select * from Person p, Address a where p.LAST_NAME = ? and p.PERSON_ID=a.PERSON_ID"; + List> list = new ArrayList>(); + list.add(Person.class); + list.add(Address.class); + final String actual = QueryParser.parse(input, list); + assertEquals(expected, actual); + } + + public void testForeignKeyJoin() + { + final String input = "select * from Person p, Address a where p.[Person]=a.[Address.person]"; + final String expected = "select * from Person p, Address a where p.PERSON_ID=a.PERSON_ID"; + List> list = new ArrayList>(); + list.add(Person.class); + list.add(Address.class); + final String actual = QueryParser.parse(input, list); + assertEquals(expected, actual); + } +} diff --git a/test/com/perfectworldprogramming/mobile/orm/test/reflection/ColumnTypeMapperTest.java b/test/com/perfectworldprogramming/mobile/orm/test/reflection/ColumnTypeMapperTest.java new file mode 100644 index 0000000..84f2f1e --- /dev/null +++ b/test/com/perfectworldprogramming/mobile/orm/test/reflection/ColumnTypeMapperTest.java @@ -0,0 +1,708 @@ +package com.perfectworldprogramming.mobile.orm.test.reflection; + +import java.lang.reflect.Field; + +import android.content.ContentValues; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.test.ActivityInstrumentationTestCase2; + +import com.perfectworldprogramming.mobile.orm.AndroidSQLiteTemplate; +import com.perfectworldprogramming.mobile.orm.annotations.Column; +import com.perfectworldprogramming.mobile.orm.exception.DataAccessException; +import com.perfectworldprogramming.mobile.orm.helper.DBHelper; +import com.perfectworldprogramming.mobile.orm.interfaces.ColumnTypeMapper; +import com.perfectworldprogramming.mobile.orm.reflection.AbstractMapper; +import com.perfectworldprogramming.mobile.orm.reflection.BooleanMapper; +import com.perfectworldprogramming.mobile.orm.reflection.DateMapper; +import com.perfectworldprogramming.mobile.orm.reflection.DoubleMapper; +import com.perfectworldprogramming.mobile.orm.reflection.FloatMapper; +import com.perfectworldprogramming.mobile.orm.reflection.IntegerMapper; +import com.perfectworldprogramming.mobile.orm.reflection.LongMapper; +import com.perfectworldprogramming.mobile.orm.reflection.PrimaryKeyMapper; +import com.perfectworldprogramming.mobile.orm.reflection.StringMapper; +import com.perfectworldprogramming.mobile.orm.test.Main; +import com.perfectworldprogramming.mobile.orm.test.domain.Account; +import com.perfectworldprogramming.mobile.orm.test.domain.Address; +import com.perfectworldprogramming.mobile.orm.test.domain.Person; +import com.perfectworldprogramming.mobile.orm.test.interfaces.SampleDataHelper; + +public class ColumnTypeMapperTest extends ActivityInstrumentationTestCase2
+{ + + AndroidSQLiteTemplate template; + DBHelper helper; + SQLiteDatabase dataBase; + + public ColumnTypeMapperTest() + { + super("org.springframework.mobile.orm.test", Main.class); + } + + @Override + @SuppressWarnings("unchecked") + public void setUp() + { + helper = new DBHelper(this.getInstrumentation().getContext(), new Class[] { Person.class, Address.class, Account.class }, "ormtest", 3); + template = new AndroidSQLiteTemplate(helper.getSqlLiteDatabase()); + dataBase = helper.getSqlLiteDatabase(); + SampleDataHelper.addDataToDatabase(template); + } + + @Override + protected void tearDown() throws Exception + { + helper.cleanup(); + } + + public void testAbstractMapperFieldModelToDatabase() + { + AbstractMapper mapper = (AbstractMapper) StringMapper.INSTANCE; + + Address address = new Address(); + address.setZipCode("2468"); + Field field = getField(Address.class, "zipCode"); + ContentValues cv = new ContentValues(); + mapper.modelToDatabase(field, address, cv); + doTestContentValues(cv, "ZIP_CODE", "2468"); + } + + public void testAbstractMapperColumnModelToDatabase() + { + AbstractMapper mapper = (AbstractMapper) StringMapper.INSTANCE; + + Address address = new Address(); + address.setZipCode("13579"); + ContentValues cv = new ContentValues(); + mapper.modelToDatabase("ZIP_CODE", address, cv); + doTestContentValues(cv, "ZIP_CODE", "13579"); + } + + public void testAbstractMapperFieldDatabaseToModel() + { + AbstractMapper mapper = (AbstractMapper) StringMapper.INSTANCE; + + Address address = new Address(); + Field field = getField(Address.class, "zipCode"); + Cursor cursor = dataBase.rawQuery("Select * from Address where ADDRESS_ID = 1", null); + cursor.moveToFirst(); + mapper.databaseToModel(cursor, field, address); + assertEquals("Zip Code", "90808", address.getZipCode()); + } + + public void testAbstractMapperColumnDatabaseToModel() + { + AbstractMapper mapper = (AbstractMapper) StringMapper.INSTANCE; + Address address = new Address(); + Cursor cursor = dataBase.rawQuery("Select * from Address where ADDRESS_ID = 2", null); + cursor.moveToFirst(); + mapper.databaseToModel(cursor, "ZIP_CODE", address); + assertEquals("Zip Code", "12345", address.getZipCode()); + } + + public void testMapNullPrimaryKeyModelToDatabase() + { + ColumnTypeMapper mapper = PrimaryKeyMapper.INSTANCE; + + Person person = new Person(); + // Primary key + ContentValues cv = new ContentValues(); + mapper.modelToDatabase("PERSON_ID", person, cv); + doTestNullContentValues(cv, "PERSON_ID", true); + } + + public void testMapNullStringModelToDatabase() + { + Person person = new Person(); + ColumnTypeMapper mapper = StringMapper.INSTANCE; + // String + ContentValues cv = new ContentValues(); + mapper.modelToDatabase(Person.COL_FIRST_NAME, person, cv); + doTestNullContentValues(cv, Person.COL_FIRST_NAME, true); + Field field = getField(Person.class, "firstName"); + doTestNullContentValues(cv, field, true); + } + + public void testMapNullIntegerModelToDatabase() + { + Person person = new Person(); + // Integer and int primitive + ColumnTypeMapper mapper = IntegerMapper.INSTANCE; + ContentValues cv = new ContentValues(); + mapper.modelToDatabase("AGE", person, cv); + doTestNullContentValues(cv, "AGE", true); + Field field = getField(Person.class, "age"); + doTestNullContentValues(cv, field, true); + + cv = new ContentValues(); + mapper.modelToDatabase("JACKET_SIZE", person, cv); + doTestNullContentValues(cv, "JACKET_SIZE", false); + assertEquals("Jacket size", "0", cv.get("JACKET_SIZE")); + field = getField(Person.class, "jacketSize"); + doTestNullContentValues(cv, field, false); + } + + public void testMapNullLongModelToDatabase() + { + Person person = new Person(); + ColumnTypeMapper mapper = LongMapper.INSTANCE; + ContentValues cv = new ContentValues(); + mapper.modelToDatabase("DEPENDANTS", person, cv); + doTestNullContentValues(cv, "DEPENDANTS", true); + Field field = getField(Person.class, "dependants"); + doTestNullContentValues(cv, field, true); + + cv = new ContentValues(); + mapper.modelToDatabase("PETS", person, cv); + doTestNullContentValues(cv, "PETS", false); + assertEquals("Pets", "0", cv.get("PETS")); + field = getField(Person.class, "pets"); + doTestNullContentValues(cv, field, false); + } + public void testMapNullFloatModelToDatabase() + { + Person person = new Person(); + ColumnTypeMapper mapper = FloatMapper.INSTANCE; + ContentValues cv = new ContentValues(); + mapper.modelToDatabase("WEIGHT", person, cv); + doTestNullContentValues(cv, "WEIGHT", true); + Field field = getField(Person.class, "weight"); + doTestNullContentValues(cv, field, true); + + cv = new ContentValues(); + mapper.modelToDatabase("SHOE_SIZE", person, cv); + doTestNullContentValues(cv, "SHOE_SIZE", false); + assertEquals("Shoe size", "0.0", cv.get("SHOE_SIZE")); + field = getField(Person.class, "shoeSize"); + doTestNullContentValues(cv, field, false); + } + public void testMapNullDoubleModelToDatabase() + { + Person person = new Person(); + ColumnTypeMapper mapper = DoubleMapper.INSTANCE; + ContentValues cv = new ContentValues(); + mapper.modelToDatabase("HEIGHT", person, cv); + doTestNullContentValues(cv, "HEIGHT", true); + Field field = getField(Person.class, "height"); + doTestNullContentValues(cv, field, true); + + cv = new ContentValues(); + mapper.modelToDatabase("WEALTH", person, cv); + doTestNullContentValues(cv, "WEALTH", false); + assertEquals("Wealth", "0.0", cv.get("WEALTH")); + field = getField(Person.class, "wealth"); + doTestNullContentValues(cv, field, false); + } + public void testMapNullBooleanModelToDatabase() + { + Person person = new Person(); + ColumnTypeMapper mapper = BooleanMapper.INSTANCE; + ContentValues cv = new ContentValues(); + mapper.modelToDatabase("FEMALE", person, cv); + doTestNullContentValues(cv, "FEMALE", true); + Field field = getField(Person.class, "female"); + doTestNullContentValues(cv, field, true); + + cv = new ContentValues(); + mapper.modelToDatabase("STAFF", person, cv); + doTestNullContentValues(cv, "STAFF", false); + assertEquals("Staff", Integer.valueOf(0), cv.get("STAFF")); + field = getField(Person.class, "staff"); + doTestNullContentValues(cv, field, false); + } + + //TODO null String mapping, requires a nullable String field in the DB + public void t_estMapNullStringDatabaseToModel() + { + long nullId = template.insert(new Person()); + Cursor cursor = dataBase.rawQuery("Select * from Person where PERSON_ID = "+nullId, null); + cursor.moveToFirst(); + Person person = new Person(); + ColumnTypeMapper mapper = StringMapper.INSTANCE; + + mapper.databaseToModel(cursor, Person.COL_FIRST_NAME, person); + assertNull("First name", person.getFirstName()); + Field field = getField(Person.class, "firstName"); + mapper.databaseToModel(cursor, field, person); + assertNull("First name", person.getFirstName()); + + //template.delete("Select * from Person where PERSON_ID = "+nullId, (Object[])null); + } + + public void testMapNullIntegerDatabaseToModel() + { + final Person nullPerson = new Person("first", "last", 10, 5.5D); + long nullId = template.insert(nullPerson); + Cursor cursor = dataBase.rawQuery("Select * from Person where PERSON_ID = "+nullId, null); + cursor.moveToFirst(); + Person person = new Person(); + ColumnTypeMapper mapper = IntegerMapper.INSTANCE; + + // TODO requires a nullable Integer wrapper field + //mapper.databaseToModel(cursor, "AGE", person); + //assertNull("Age", person.getAge()); + //Field field = getField(Person.class, "age"); + //mapper.databaseToModel(cursor, field, person); + //assertNull("Age", person.getAge()); + + mapper.databaseToModel(cursor, "JACKET_SIZE", person); + assertEquals("Jacket size", 0, person.getJacketSize()); + Field field = getField(Person.class, "jacketSize"); + mapper.databaseToModel(cursor, field, person); + assertEquals("Jacket size", 0, person.getJacketSize()); + + template.delete(nullPerson); + } + + public void testMapNullLongDatabaseToModel() + { + final Person nullPerson = new Person("first", "last", 10, 5.5D); + long nullId = template.insert(nullPerson); + Cursor cursor = dataBase.rawQuery("Select * from Person where PERSON_ID = "+nullId, null); + cursor.moveToFirst(); + ColumnTypeMapper mapper = LongMapper.INSTANCE; + Person person = new Person(); + + mapper.databaseToModel(cursor, "DEPENDANTS", person); + assertNull("Dependants", person.getDependants()); + Field field = getField(Person.class, "dependants"); + mapper.databaseToModel(cursor, field, person); + assertNull("Dependants", person.getDependants()); + + mapper.databaseToModel(cursor, "PETS", person); + assertEquals("Pets", 0L, person.getPets()); + field = getField(Person.class, "pets"); + mapper.databaseToModel(cursor, field, person); + assertEquals("Pets", 0L, person.getPets()); + + template.delete(nullPerson); + } + + public void testMapNullFloatDatabaseToModel() + { + final Person nullPerson = new Person("first", "last", 10, 5.5D); + long nullId = template.insert(nullPerson); + Cursor cursor = dataBase.rawQuery("Select * from Person where PERSON_ID = "+nullId, null); + cursor.moveToFirst(); + ColumnTypeMapper mapper = FloatMapper.INSTANCE; + Person person = new Person(); + + mapper.databaseToModel(cursor, "WEIGHT", person); + assertNull("Weight", person.getWeight()); + Field field = getField(Person.class, "weight"); + mapper.databaseToModel(cursor, field, person); + assertNull("Weight", person.getWeight()); + + mapper.databaseToModel(cursor, "SHOE_SIZE", person); + assertEquals("Shoe size", 0F, person.getShoeSize()); + field = getField(Person.class, "shoeSize"); + mapper.databaseToModel(cursor, field, person); + assertEquals("Shoe size", 0F, person.getShoeSize()); + + template.delete(nullPerson); + } + + public void testMapNullDoubleDatabaseToModel() + { + final Person nullPerson = new Person("first", "last", 10, 5.5D); + long nullId = template.insert(nullPerson); + Cursor cursor = dataBase.rawQuery("Select * from Person where PERSON_ID = "+nullId, null); + cursor.moveToFirst(); + ColumnTypeMapper mapper = DoubleMapper.INSTANCE; + Person person = new Person(); + + // TODO needs a nullable Double wrapper + //mapper.databaseToModel(cursor, "HEIGHT", person); + //assertNull("Height", person.getHeight()); + //Field field = getField(Person.class, "height"); + //mapper.databaseToModel(cursor, field, person); + //assertNull("Height", person.getHeight()); + + mapper.databaseToModel(cursor, "WEALTH", person); + assertEquals("Wealth", 0D, person.getWealth()); + Field field = getField(Person.class, "wealth"); + mapper.databaseToModel(cursor, field, person); + assertEquals("Wealth", 0D, person.getWealth()); + + template.delete(nullPerson); + } + + public void testMapNullBooleanDatabaseToModel() + { + final Person nullPerson = new Person("first", "last", 10, 5.5D); + long nullId = template.insert(nullPerson); + Cursor cursor = dataBase.rawQuery("Select * from Person where PERSON_ID = "+nullId, null); + cursor.moveToFirst(); + ColumnTypeMapper mapper = BooleanMapper.INSTANCE; + Person person = new Person(); + + mapper.databaseToModel(cursor, "FEMALE", person); + assertNull("Female", person.getFemale()); + Field field = getField(Person.class, "female"); + mapper.databaseToModel(cursor, field, person); + assertNull("Female", person.getFemale()); + + mapper.databaseToModel(cursor, "STAFF", person); + assertEquals("Staff", false, person.isStaff()); + field = getField(Person.class, "staff"); + mapper.databaseToModel(cursor, field, person); + assertEquals("Staff", false, person.isStaff()); + + template.delete(nullPerson); + } + + // TODO public void testMapNullDateModelToDatabase() + // TODO public void testMapNullBlobModelToDatabase() + + public void testBooleanMapperDatabaseToModel() + { + ColumnTypeMapper mapper = BooleanMapper.INSTANCE; + Field fieldStaff = getField(Person.class, "staff"); + Field fieldFemale = getField(Person.class, "female"); + + assertEquals("DatabaseColumnType", "INTEGER", mapper.getDatabaseColumnType()); + + Person person = new Person(); + Cursor cursor = dataBase.rawQuery("Select * from Person where PERSON_ID = 1", null); + cursor.moveToFirst(); + mapper.databaseToModel(cursor, "STAFF", person); + assertEquals("Staff", true, person.isStaff()); + mapper.databaseToModel(cursor, "FEMALE", person); + assertEquals("Female", Boolean.FALSE, person.getFemale()); + person = new Person(); + mapper.databaseToModel(cursor, fieldStaff, person); + assertEquals("Staff", true, person.isStaff()); + mapper.databaseToModel(cursor, fieldFemale, person); + assertEquals("Female", Boolean.FALSE, person.getFemale()); + + person = new Person(); + cursor = dataBase.rawQuery("Select * from Person where PERSON_ID = 2", null); + cursor.moveToFirst(); + mapper.databaseToModel(cursor, "STAFF", person); + assertEquals("Staff", false, person.isStaff()); + mapper.databaseToModel(cursor, "FEMALE", person); + assertEquals("Female", Boolean.TRUE, person.getFemale()); + person = new Person(); + mapper.databaseToModel(cursor, fieldStaff, person); + assertEquals("Staff", false, person.isStaff()); + mapper.databaseToModel(cursor, fieldFemale, person); + assertEquals("Female", Boolean.TRUE, person.getFemale()); + } + + public void testBooleanMapperModelToDatabase() + { + ColumnTypeMapper mapper = BooleanMapper.INSTANCE; + Field field = getField(Person.class, "staff"); + + Person person = new Person(); + person.setStaff(true); + ContentValues cv = new ContentValues(); + mapper.modelToDatabase("STAFF", person, cv); + doTestContentValues(cv, "STAFF", Integer.valueOf(1)); // maps to 1 in the DB + cv = new ContentValues(); + mapper.modelToDatabase(field, person, cv); + doTestContentValues(cv, "STAFF", Integer.valueOf(1)); + + person = new Person(); + person.setStaff(false); + cv = new ContentValues(); + mapper.modelToDatabase("STAFF", person, cv); + doTestContentValues(cv, "STAFF", Integer.valueOf(0)); + cv = new ContentValues(); + mapper.modelToDatabase(field, person, cv); + doTestContentValues(cv, "STAFF", Integer.valueOf(0)); + } + + public void testStringMapperDatabaseToModel() + { + ColumnTypeMapper mapper = StringMapper.INSTANCE; + Field field = getField(Person.class, "firstName"); + + assertEquals("DatabaseColumnType", "TEXT", mapper.getDatabaseColumnType()); + + Person person = new Person(); + Cursor cursor = dataBase.rawQuery("Select * from Person where PERSON_ID = 1", null); + cursor.moveToFirst(); + mapper.databaseToModel(cursor, Person.COL_FIRST_NAME, person); + assertEquals("First name", "John", person.getFirstName()); + person.setFirstName(null); + mapper.databaseToModel(cursor, field, person); + assertEquals("First name", "John", person.getFirstName()); + } + + public void testStringMapperModelToDatabase() + { + ColumnTypeMapper mapper = StringMapper.INSTANCE; + Field field = getField(Person.class, "firstName"); + + Person person = new Person(); + person.setFirstName("John"); + ContentValues cv = new ContentValues(); + mapper.modelToDatabase(Person.COL_FIRST_NAME, person, cv); + doTestContentValues(cv, Person.COL_FIRST_NAME, "John"); + cv = new ContentValues(); + mapper.modelToDatabase(field, person, cv); + doTestContentValues(cv, Person.COL_FIRST_NAME, "John"); + } + + public void testDoubleMapperDatabaseToModel() + { + ColumnTypeMapper mapper = DoubleMapper.INSTANCE; + + assertEquals("DatabaseColumnType", "REAL", mapper.getDatabaseColumnType()); + + Person person = new Person(); + Cursor cursor = dataBase.rawQuery("Select * from Person where PERSON_ID = 1", null); + cursor.moveToFirst(); + mapper.databaseToModel(cursor, "HEIGHT", person); + assertEquals("Height", Double.valueOf(5.1D), person.getHeight()); + person.setHeight(null); + Field field = getField(Person.class, "height"); + mapper.databaseToModel(cursor, field, person); + assertEquals("Height", Double.valueOf(5.1D), person.getHeight()); + + mapper.databaseToModel(cursor, "WEALTH", person); + assertEquals("Wealth", 210000.00D, person.getWealth()); + person.setWealth(0D); + field = getField(Person.class, "wealth"); + mapper.databaseToModel(cursor, field, person); + assertEquals("Wealth", 210000.00D, person.getWealth()); + } + + public void testDoubleMapperModelToDatabase() + { + ColumnTypeMapper mapper = DoubleMapper.INSTANCE; + Person person = new Person(); + person.setHeight(Double.valueOf(5.2D)); + person.setWealth(Double.valueOf(10000.20D)); + + ContentValues cv = new ContentValues(); + mapper.modelToDatabase("HEIGHT", person, cv); + doTestContentValues(cv, "HEIGHT", "5.2"); + cv = new ContentValues(); + Field field = getField(Person.class, "height"); + mapper.modelToDatabase(field, person, cv); + doTestContentValues(cv, "HEIGHT", "5.2"); + + cv = new ContentValues(); + mapper.modelToDatabase("WEALTH", person, cv); + doTestContentValues(cv, "WEALTH", "10000.2"); + cv = new ContentValues(); + field = getField(Person.class, "wealth"); + mapper.modelToDatabase(field, person, cv); + doTestContentValues(cv, "WEALTH", "10000.2"); + } + + public void testFloatMapperDatabaseToModel() + { + ColumnTypeMapper mapper = FloatMapper.INSTANCE; + + assertEquals("DatabaseColumnType", "REAL", mapper.getDatabaseColumnType()); + + Person person = new Person(); + Cursor cursor = dataBase.rawQuery("Select * from Person where PERSON_ID = 1", null); + cursor.moveToFirst(); + mapper.databaseToModel(cursor, "WEIGHT", person); + assertEquals("Weight", Float.valueOf(175.6F), person.getWeight()); + person.setWeight(null); + Field field = getField(Person.class, "weight"); + mapper.databaseToModel(cursor, field, person); + assertEquals("Weight", Float.valueOf(175.6F), person.getWeight()); + + mapper.databaseToModel(cursor, "SHOE_SIZE", person); + assertEquals("Shoe size", 9.5F, person.getShoeSize()); + person.setShoeSize(0F); + field = getField(Person.class, "shoeSize"); + mapper.databaseToModel(cursor, field, person); + assertEquals("Shoe size", 9.5F, person.getShoeSize()); + } + + public void testFloatMapperModelToDatabase() + { + ColumnTypeMapper mapper = FloatMapper.INSTANCE; + Person person = new Person(); + person.setWeight(Float.valueOf(55.2F)); + person.setShoeSize(7.10F); + + ContentValues cv = new ContentValues(); + mapper.modelToDatabase("WEIGHT", person, cv); + doTestContentValues(cv, "WEIGHT", "55.2"); + cv = new ContentValues(); + Field field = getField(Person.class, "weight"); + mapper.modelToDatabase(field, person, cv); + doTestContentValues(cv, "WEIGHT", "55.2"); + + cv = new ContentValues(); + mapper.modelToDatabase("SHOE_SIZE", person, cv); + doTestContentValues(cv, "SHOE_SIZE", "7.1"); + cv = new ContentValues(); + field = getField(Person.class, "shoeSize"); + mapper.modelToDatabase(field, person, cv); + doTestContentValues(cv, "SHOE_SIZE", "7.1"); + } + + public void testIntegerMapperDatabaseToModel() + { + ColumnTypeMapper mapper = IntegerMapper.INSTANCE; + + assertEquals("DatabaseColumnType", "INTEGER", mapper.getDatabaseColumnType()); + + Person person = new Person(); + Cursor cursor = dataBase.rawQuery("Select * from Person where PERSON_ID = 1", null); + cursor.moveToFirst(); + mapper.databaseToModel(cursor, "AGE", person); + assertEquals("Age", Integer.valueOf(42), person.getAge()); + person.setAge(0); + Field field = getField(Person.class, "age"); + mapper.databaseToModel(cursor, field, person); + assertEquals("Age", Integer.valueOf(42), person.getAge()); + + mapper.databaseToModel(cursor, "JACKET_SIZE", person); + assertEquals("Jacket size", 43, person.getJacketSize()); + person.setJacketSize(0); + field = getField(Person.class, "jacketSize"); + mapper.databaseToModel(cursor, field, person); + assertEquals("Jacket size", 43, person.getJacketSize()); + } + + public void testIntegerMapperModelToDatabase() + { + ColumnTypeMapper mapper = IntegerMapper.INSTANCE; + Person person = new Person(); + person.setAge(Integer.valueOf(55)); + person.setJacketSize(56); + + ContentValues cv = new ContentValues(); + mapper.modelToDatabase("AGE", person, cv); + doTestContentValues(cv, "AGE", "55"); + cv = new ContentValues(); + Field field = getField(Person.class, "age"); + mapper.modelToDatabase(field, person, cv); + doTestContentValues(cv, "AGE", "55"); + + cv = new ContentValues(); + mapper.modelToDatabase("JACKET_SIZE", person, cv); + doTestContentValues(cv, "JACKET_SIZE", "56"); + cv = new ContentValues(); + field = getField(Person.class, "jacketSize"); + mapper.modelToDatabase(field, person, cv); + doTestContentValues(cv, "JACKET_SIZE", "56"); + } + + public void testLongMapperDatabaseToModel() + { + ColumnTypeMapper mapper = LongMapper.INSTANCE; + + assertEquals("DatabaseColumnType", "INTEGER", mapper.getDatabaseColumnType()); + + Person person = new Person(); + Cursor cursor = dataBase.rawQuery("Select * from Person where PERSON_ID = 1", null); + cursor.moveToFirst(); + mapper.databaseToModel(cursor, "DEPENDANTS", person); + assertEquals("Dependants", Long.valueOf(2), person.getDependants()); + person.setDependants(-1L); + Field field = getField(Person.class, "dependants"); + mapper.databaseToModel(cursor, field, person); + assertEquals("Dependants", Long.valueOf(2), person.getDependants()); + + mapper.databaseToModel(cursor, "PETS", person); + assertEquals("Pets", 1L, person.getPets()); + person.setPets(-1L); + field = getField(Person.class, "pets"); + mapper.databaseToModel(cursor, field, person); + assertEquals("Pets", 1l, person.getPets()); + } + + public void testLongMapperModelToDatabase() + { + ColumnTypeMapper mapper = LongMapper.INSTANCE; + Person person = new Person(); + person.setDependants(Long.valueOf(57)); + person.setPets(58L); + + ContentValues cv = new ContentValues(); + mapper.modelToDatabase("DEPENDANTS", person, cv); + doTestContentValues(cv, "DEPENDANTS", "57"); + cv = new ContentValues(); + Field field = getField(Person.class, "dependants"); + mapper.modelToDatabase(field, person, cv); + doTestContentValues(cv, "DEPENDANTS", "57"); + + cv = new ContentValues(); + mapper.modelToDatabase("PETS", person, cv); + doTestContentValues(cv, "PETS", "58"); + cv = new ContentValues(); + field = getField(Person.class, "pets"); + mapper.modelToDatabase(field, person, cv); + doTestContentValues(cv, "PETS", "58"); + } + + public void testDateMapperDatabaseToModel() + { + ColumnTypeMapper mapper = DateMapper.INSTANCE; + + assertEquals("DatabaseColumnType", "INTEGER", mapper.getDatabaseColumnType()); + + Person person = new Person(); + Cursor cursor = dataBase.rawQuery("Select * from Person where PERSON_ID = 1", null); + cursor.moveToFirst(); + mapper.databaseToModel(cursor, "START_DATE", person); + assertEquals("Start Date", new java.sql.Date(2012-1900, 1, 1), person.getStartDate()); + person.setStartDate(null); + Field field = getField(Person.class, "startDate"); + mapper.databaseToModel(cursor, field, person); + assertEquals("Start Date", new java.sql.Date(2012-1900, 1, 1), person.getStartDate()); + } + + public void testDateMapperModelToDatabase() + { + ColumnTypeMapper mapper = DateMapper.INSTANCE; + Person person = new Person(); + + // year specified as year-1900 + person.setStartDate(new java.sql.Date(2012-1900, 4, 5)); + + ContentValues cv = new ContentValues(); + mapper.modelToDatabase("START_DATE", person, cv); + doTestContentValues(cv, "START_DATE", 1336147200000L); + cv = new ContentValues(); + Field field = getField(Person.class, "startDate"); + mapper.modelToDatabase(field, person, cv); + doTestContentValues(cv, "START_DATE", 1336147200000L); + } + // TODO public void testMapBlobDatabaseToModel() + // TODO public void testMapBlobModelToDatabase() + + + private Field getField(Class clazz, String name) + { + try + { + return clazz.getDeclaredField(name); + } + catch (SecurityException e) + { + throw new DataAccessException(e.getMessage()); + } + catch (NoSuchFieldException e) + { + throw new DataAccessException(e.getMessage()); + } + } + + private void doTestContentValues(ContentValues cv, String key, Object value) + { + assertEquals("ContentValues size", 1, cv.size()); + assertTrue("ContentValues key", cv.containsKey(key)); + assertEquals("ContentValues value", value, cv.get(key)); + } + private void doTestNullContentValues(ContentValues cv, String key, boolean expectNull) + { + assertEquals("ContentValues size", expectNull?0:1, cv.size()); + assertEquals("ContentValues key", !expectNull, cv.containsKey(key)); + } + private void doTestNullContentValues(ContentValues cv, Field key, boolean expectNull) + { + doTestNullContentValues(cv, key.getAnnotation(Column.class).value(), expectNull); + } +} diff --git a/test/com/perfectworldprogramming/mobile/orm/test/reflection/DomainClassAnalyzerTests.java b/test/com/perfectworldprogramming/mobile/orm/test/reflection/DomainClassAnalyzerTests.java index b325059..5541b43 100644 --- a/test/com/perfectworldprogramming/mobile/orm/test/reflection/DomainClassAnalyzerTests.java +++ b/test/com/perfectworldprogramming/mobile/orm/test/reflection/DomainClassAnalyzerTests.java @@ -1,7 +1,10 @@ package com.perfectworldprogramming.mobile.orm.test.reflection; import java.lang.reflect.Field; +import java.util.List; +import android.content.ContentValues; +import android.test.ActivityInstrumentationTestCase2; import com.perfectworldprogramming.mobile.orm.annotations.PrimaryKey; import com.perfectworldprogramming.mobile.orm.reflection.DomainClassAnalyzer; @@ -9,9 +12,6 @@ import com.perfectworldprogramming.mobile.orm.test.domain.Address; import com.perfectworldprogramming.mobile.orm.test.domain.Person; -import android.content.ContentValues; -import android.test.ActivityInstrumentationTestCase2; - /** * User: Mark Spritzler * Date: 3/14/11 @@ -24,14 +24,6 @@ public DomainClassAnalyzerTests() { super("org.springframework.mobile.orm.test", Main.class); } - public void setUp() { - try { - super.setUp(); - } catch (Exception e) { - e.printStackTrace(); - } - } - public void testGetPrimaryKeyFieldTest() { Field field = domainClassAnalyzer.getPrimaryKeyField(Person.class); assertNotNull("primary key field should not be null", field); @@ -51,10 +43,10 @@ public void testGetPrimaryKeyTest() { } public void testGetForeignKeyFieldsTest() { - Field[] personForeignKeyFields = domainClassAnalyzer.getForeignKeyFields(Person.class); - assertEquals("", 0, personForeignKeyFields.length); - Field[] addressForeignKeyFields = domainClassAnalyzer.getForeignKeyFields(Address.class); - assertEquals("", 1, addressForeignKeyFields.length); + List personForeignKeyFields = domainClassAnalyzer.getForeignKeyFields(Person.class); + assertEquals("", 0, personForeignKeyFields.size()); + List addressForeignKeyFields = domainClassAnalyzer.getForeignKeyFields(Address.class); + assertEquals("", 1, addressForeignKeyFields.size()); } public void testGetIdFromObjectTest() { diff --git a/test/com/perfectworldprogramming/mobile/orm/test/reflection/QueryParameterTest.java b/test/com/perfectworldprogramming/mobile/orm/test/reflection/QueryParameterTest.java new file mode 100644 index 0000000..375474a --- /dev/null +++ b/test/com/perfectworldprogramming/mobile/orm/test/reflection/QueryParameterTest.java @@ -0,0 +1,185 @@ +package com.perfectworldprogramming.mobile.orm.test.reflection; + +import java.util.List; + +import android.database.sqlite.SQLiteDatabase; +import android.test.ActivityInstrumentationTestCase2; + +import com.perfectworldprogramming.mobile.orm.AndroidSQLiteTemplate; +import com.perfectworldprogramming.mobile.orm.helper.DBHelper; +import com.perfectworldprogramming.mobile.orm.test.AndroidSQLiteTemplateTests; +import com.perfectworldprogramming.mobile.orm.test.Main; +import com.perfectworldprogramming.mobile.orm.test.domain.Account; +import com.perfectworldprogramming.mobile.orm.test.domain.Address; +import com.perfectworldprogramming.mobile.orm.test.domain.Person; +import com.perfectworldprogramming.mobile.orm.test.interfaces.SampleDataHelper; + +/** + * Essentially a copy of {@link AndroidSQLiteTemplateTests} using {@link AndroidSQLiteTemplate.mapQueryParameter()} + * so that database conversion of values is hidden from domain layer (excluding the column name, + * which is already declared in the domain classes). + * @author David O'Meara + * @since 10/06/2012 + * + */ +public class QueryParameterTest extends ActivityInstrumentationTestCase2
{ + + AndroidSQLiteTemplate template; + DBHelper helper; + List sampleAccounts; + + SQLiteDatabase dataBase; + + public QueryParameterTest() { + super("org.springframework.mobile.orm.test", Main.class); + } + + @SuppressWarnings("unchecked") + public void setUp() { + helper = new DBHelper(this.getInstrumentation().getContext(), new Class[]{Person.class, Address.class, Account.class}, "ormtest", 3); + dataBase = helper.getSqlLiteDatabase(); + template = new AndroidSQLiteTemplate(helper.getSqlLiteDatabase()); + SampleDataHelper.addDataToDatabase(template); + } + + @Override + protected void tearDown() throws Exception + { + helper.cleanup(); + } + + // Currently passes + public void testSqlQueryByPrimaryKey() + { + Object[] args = new Object[1]; + args[0] = template.mapQueryParameter(1L, Person.class, Person.PK_PERSON); + + String sql = "SELECT * from Person where "+Person.PK_PERSON+" = ?"; + Person person = template.queryForObject(sql, Person.class, args); + assertNotNull(person); + assertEquals("ID", Long.valueOf(1), person.getId()); + assertEquals("First name", "John", person.getFirstName()); + } + + public void testSqlQueryByString() + { + Object[] args = new Object[1]; + args[0] = template.mapQueryParameter("John", Person.class, Person.COL_FIRST_NAME); + + String sql = "SELECT * from Person where "+Person.COL_FIRST_NAME+" = '?'"; + Person person = template.queryForObject(sql, Person.class, args); + assertNotNull(person); + assertEquals("ID", Long.valueOf(1), person.getId()); + assertEquals("First name", "John", person.getFirstName()); + } + + public void testSqlQueryByInteger() + { + Object[] args = new Object[1]; + args[0] = template.mapQueryParameter(42, Person.class, Person.COL_AGE); + + String sql = "SELECT * from Person where "+Person.COL_AGE+" = ?"; + Person person = template.queryForObject(sql, Person.class, args); + assertNotNull(person); + assertEquals("ID", Long.valueOf(1), person.getId()); + assertEquals("First name", "John", person.getFirstName()); + assertEquals("Age", Integer.valueOf(42), person.getAge()); + + args[0] = template.mapQueryParameter(43, Person.class, Person.COL_JACKET_SIZE); + sql = "SELECT * from Person where "+Person.COL_JACKET_SIZE+" = ?"; + person = template.queryForObject(sql, Person.class, args); + assertNotNull(person); + assertEquals("ID", Long.valueOf(1), person.getId()); + assertEquals("First name", "John", person.getFirstName()); + assertEquals("Age", Integer.valueOf(42), person.getAge()); + assertEquals("Jacket size", 43, person.getJacketSize()); + } + + public void testSqlQueryByLong() + { + Object[] args = new Object[1]; + args[0] = template.mapQueryParameter(2D, Person.class, Person.COL_DEPENDANTS); + + String sql = "SELECT * from Person where "+Person.COL_DEPENDANTS+" = ?"; + Person person = template.queryForObject(sql, Person.class, args); + assertNotNull(person); + assertEquals("ID", Long.valueOf(1), person.getId()); + assertEquals("First name", "John", person.getFirstName()); + + args[0] = template.mapQueryParameter(1D, Person.class, Person.COL_PETS); + sql = "SELECT * from Person where "+Person.COL_PETS+" = ?"; + person = template.queryForObject(sql, Person.class, args); + assertNotNull(person); + assertEquals("ID", Long.valueOf(1), person.getId()); + assertEquals("First name", "John", person.getFirstName()); + } + + public void testSqlQueryByBoolean() + { + Object[] args = new Object[1]; + args[0] = template.mapQueryParameter(true, Person.class, Person.COL_STAFF); + + String sql = "SELECT * from Person where "+Person.COL_STAFF+" = ?"; + Person person = template.queryForObject(sql, Person.class, args); + assertNotNull(person); + assertEquals("ID", Long.valueOf(1), person.getId()); + assertEquals("First name", "John", person.getFirstName()); + + args[0] = template.mapQueryParameter(false, Person.class, Person.COL_FEMALE); + sql = "SELECT * from Person where "+Person.COL_FEMALE+" = ?"; + person = template.queryForObject(sql, Person.class, args); + assertNotNull(person); + assertEquals("ID", Long.valueOf(1), person.getId()); + assertEquals("First name", "John", person.getFirstName()); + } + + public void testSqlQueryByFloat() + { + Object[] args = new Object[1]; + args[0] = template.mapQueryParameter(200F, Person.class, Person.COL_WEIGHT); + + String sql = "SELECT * from Person where "+Person.COL_WEIGHT+" < ?"; + Person person = template.queryForObject(sql, Person.class, args); + assertNotNull(person); + assertEquals("ID", Long.valueOf(1), person.getId()); + assertEquals("First name", "John", person.getFirstName()); + + args[0] = template.mapQueryParameter(10F, Person.class, Person.COL_SHOE_SIZE); + sql = "SELECT * from Person where "+Person.COL_SHOE_SIZE+" < ?"; + person = template.queryForObject(sql, Person.class, args); + assertNotNull(person); + assertEquals("ID", Long.valueOf(1), person.getId()); + assertEquals("First name", "John", person.getFirstName()); + } + + public void testSqlQueryByDouble() + { + Object[] args = new Object[1]; + args[0] = template.mapQueryParameter(6D, Person.class, Person.COL_HEIGHT); + + String sql = "SELECT * from Person where "+Person.COL_HEIGHT+" < ?"; + Person person = template.queryForObject(sql, Person.class, args); + assertNotNull(person); + assertEquals("ID", Long.valueOf(1), person.getId()); + assertEquals("First name", "John", person.getFirstName()); + + args[0] = template.mapQueryParameter(200000D, Person.class, Person.COL_WEALTH); + sql = "SELECT * from Person where "+Person.COL_WEALTH+" > ?"; + person = template.queryForObject(sql, Person.class, args); + assertNotNull(person); + assertEquals("ID", Long.valueOf(1), person.getId()); + assertEquals("First name", "John", person.getFirstName()); + } + + public void testSqlQueryByDate() + { + Object[] args = new Object[1]; + args[0] = template.mapQueryParameter(new java.sql.Date(2012-1900, 2, 1), Person.class, Person.COL_START_DATE); + + String sql = "SELECT * from Person where "+Person.COL_START_DATE+" < ?"; + Person person = template.queryForObject(sql, Person.class, args); + assertNotNull(person); + assertEquals("ID", Long.valueOf(1), person.getId()); + assertEquals("First name", "John", person.getFirstName()); + } +}