Skip to content

Commit c97998d

Browse files
committed
Fix the issue #106 (#106)
When use the cursor method getColumnIndex(String columnName) and have multiple columns with same name, the android implementation not return the first index, so the model entity is filled with wrong data. Create a custom search of index and replace the usages. Also add a junit test to check the issue and the fix.
1 parent 12c6937 commit c97998d

File tree

3 files changed

+107
-2
lines changed

3 files changed

+107
-2
lines changed

src/com/activeandroid/Model.java

+8-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
import com.activeandroid.util.ReflectionUtils;
2929

3030
import java.lang.reflect.Field;
31+
import java.util.ArrayList;
32+
import java.util.Arrays;
3133
import java.util.List;
3234

3335
@SuppressWarnings("unchecked")
@@ -176,10 +178,15 @@ public static <T extends Model> T load(Class<T> type, long id) {
176178
// Model population
177179

178180
public final void loadFromCursor(Cursor cursor) {
181+
/**
182+
* Obtain the columns ordered to fix issue #106 (https://github.com/pardom/ActiveAndroid/issues/106)
183+
* when the cursor have multiple columns with same name obtained from join tables.
184+
*/
185+
List<String> columnsOrdered = new ArrayList<String>(Arrays.asList(cursor.getColumnNames()));
179186
for (Field field : mTableInfo.getFields()) {
180187
final String fieldName = mTableInfo.getColumnName(field);
181188
Class<?> fieldType = field.getType();
182-
final int columnIndex = cursor.getColumnIndex(fieldName);
189+
final int columnIndex = columnsOrdered.indexOf(fieldName);
183190

184191
if (columnIndex < 0) {
185192
continue;

src/com/activeandroid/util/SQLiteUtils.java

+9-1
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,11 @@
2727
import com.activeandroid.annotation.Column.ConflictAction;
2828
import com.activeandroid.serializer.TypeSerializer;
2929

30+
import java.lang.Long;
31+
import java.lang.String;
3032
import java.lang.reflect.Constructor;
3133
import java.lang.reflect.Field;
34+
import java.util.Arrays;
3235
import java.util.ArrayList;
3336
import java.util.HashMap;
3437
import java.util.List;
@@ -327,8 +330,13 @@ public static <T extends Model> List<T> processCursor(Class<? extends Model> typ
327330
Constructor<?> entityConstructor = type.getConstructor();
328331

329332
if (cursor.moveToFirst()) {
333+
/**
334+
* Obtain the columns ordered to fix issue #106 (https://github.com/pardom/ActiveAndroid/issues/106)
335+
* when the cursor have multiple columns with same name obtained from join tables.
336+
*/
337+
List<String> columnsOrdered = new ArrayList<String>(Arrays.asList(cursor.getColumnNames()));
330338
do {
331-
Model entity = Cache.getEntity(type, cursor.getLong(cursor.getColumnIndex(idName)));
339+
Model entity = Cache.getEntity(type, cursor.getLong(columnsOrdered.indexOf(idName)));
332340
if (entity == null) {
333341
entity = (T) entityConstructor.newInstance();
334342
}

tests/src/com/activeandroid/test/ModelTest.java

+90
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,15 @@
1919
import com.activeandroid.Cache;
2020
import com.activeandroid.Model;
2121
import com.activeandroid.TableInfo;
22+
import com.activeandroid.annotation.Column;
2223
import com.activeandroid.annotation.Table;
2324
import com.activeandroid.query.Select;
2425

2526
import java.lang.reflect.Field;
27+
import java.util.ArrayList;
28+
import java.util.Date;
2629
import java.util.HashSet;
30+
import java.util.List;
2731
import java.util.Set;
2832

2933
/**
@@ -167,11 +171,97 @@ public void testBooleanColumnType() {
167171
assertNull( new Select().from(MockModel.class).where("booleanField = ?", 1).executeSingle() );
168172

169173
assertNull( new Select().from(MockModel.class).where("booleanField = ?", true).executeSingle() );
174+
}
175+
176+
/**
177+
* Test to check the join of two (or more) tables with some fields in common when not use a projection on select.
178+
* Test the issue #106 (https://github.com/pardom/ActiveAndroid/issues/106)
179+
*/
180+
public void testJoinWithSameNames(){
181+
//create a parent entity and store
182+
ParentJoinMockModel parent = new ParentJoinMockModel();
183+
parent.booleanField = true;
184+
parent.dateField = new Date();
185+
parent.doubleField = 2.0;
186+
parent.intField = 1;
187+
parent.save();
188+
189+
//the values to assign to child
190+
Date dateValue = new Date();
191+
double doubleValue = 30.0;
192+
int intValue = 3;
193+
194+
//create two child entities, relate with parent and save
195+
ChildMockModel child1 = new ChildMockModel();
196+
child1.booleanField = false;
197+
child1.dateField = dateValue;
198+
child1.doubleField = doubleValue;
199+
child1.intField = intValue;
200+
child1.parent = parent;
201+
child1.save();
202+
203+
ChildMockModel child2 = new ChildMockModel();
204+
child2.booleanField = false;
205+
child2.dateField = dateValue;
206+
child2.doubleField = doubleValue;
207+
child2.intField = intValue;
208+
child2.parent = parent;
209+
child2.save();
210+
211+
//Store the ids assigned to child entities when persists
212+
List<Long> ids = new ArrayList<Long>();
213+
ids.add(child1.getId());
214+
ids.add(child2.getId());
215+
216+
//make the query with a join
217+
List<ChildMockModel> result = new Select().from(ChildMockModel.class).
218+
join(ParentJoinMockModel.class).on("ParentJoinMockModel.Id = ChildMockModel.parent").execute();
219+
220+
//check result
221+
assertNotNull(result);
222+
assertEquals(result.size(), 2);
223+
for(ChildMockModel currentModel : result){
224+
assertFalse(currentModel.booleanField);
225+
assertEquals(currentModel.intField, intValue);
226+
assertEquals(currentModel.doubleField, doubleValue);
227+
assertTrue(ids.contains(currentModel.getId()));
228+
}
229+
170230
}
171231

172232
/**
173233
* Mock model as we need 2 different model classes.
174234
*/
175235
@Table(name = "AnotherMockTable")
176236
public static class AnotherMockModel extends Model {}
237+
238+
/**
239+
* Mock model to test joins with same names.
240+
* It's a copy from MockModel.
241+
*/
242+
@Table(name = "ParentJoinMockModel")
243+
public static class ParentJoinMockModel extends Model {
244+
@Column
245+
public Date dateField;
246+
247+
@Column
248+
public double doubleField;
249+
250+
@Column
251+
public int intField;
252+
253+
@Column
254+
public boolean booleanField;
255+
}
256+
257+
/**
258+
* Mock model to test joins with same names.
259+
* Extends from ParentJoinMockModel to have the same columns.
260+
* Have a relationship with ParentJoinMockModel to make te join query.
261+
*/
262+
@Table(name = "ChildMockModel")
263+
public static class ChildMockModel extends ParentJoinMockModel {
264+
@Column
265+
ParentJoinMockModel parent;
266+
}
177267
}

0 commit comments

Comments
 (0)