Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding distinct to query fix #252

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions cayenne-server/src/main/java/org/apache/cayenne/DataRow.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@

package org.apache.cayenne;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicLong;

import org.apache.cayenne.map.DbRelationship;
Expand Down Expand Up @@ -149,4 +151,52 @@ public String getEntityName() {
public void setEntityName(String entityName) {
this.entityName = entityName;
}

public int hashCode() {
int h = 0;
for (Entry<String, Object> curr : entrySet()) {
if (curr.getValue() instanceof byte[]) {
h += Objects.hashCode(curr.getKey()) ^ Arrays.hashCode((byte[]) curr.getValue());
} else {
h += curr.hashCode();
}
}
return h;
}

public boolean equals(Object o) {
if (o == this)
return true;

if (!(o instanceof Map))
return false;
Map<?,?> m = (Map<?,?>) o;
if (m.size() != size())
return false;

try {
for (Entry<String, Object> e : entrySet()) {
String key = e.getKey();
Object value = e.getValue();
if (value == null) {
if (!(m.get(key) == null && m.containsKey(key)))
return false;
} else {
if (e.getValue() instanceof byte[] && m.get(key) instanceof byte[]) {
if (!Arrays.equals((byte[]) value, (byte[]) m.get(key))) {
return false;
}
} else {
if (!value.equals(m.get(key)))
return false;
}
}

}
} catch (ClassCastException | NullPointerException unused) {
return false;
}

return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.Arrays;

/**
* A ResultIterator that does in-memory filtering of rows to return only
Expand Down Expand Up @@ -144,13 +145,20 @@ private void checkNextUniqueRow() {
while (delegate.hasNextRow()) {
T next = delegate.nextRow();

if (fetchedIds.add(next)) {
if (isUnique(next)) {
this.nextDataRow = next;
break;
}
}
}

private boolean isUnique(T next) {
if(next instanceof Object[]){
return fetchedIds.add(new ObjectArrayWrapper((Object[]) next));
}
return fetchedIds.add(next);
}

private void checkNextRowWithUniqueId() {
nextDataRow = null;
while (delegate.hasNextRow()) {
Expand All @@ -165,11 +173,37 @@ private void checkNextRowWithUniqueId() {
id.put(pk.getName(), ((DataRow)next).get(pk.getName()));
}

if (fetchedIds.add(id)) {
if (isUnique((T) id)) {
this.nextDataRow = next;
break;
}
}
}

class ObjectArrayWrapper{
private Object[] objects;

ObjectArrayWrapper(Object[] objects){
this.objects = objects;
}

Object[] getObjects(){
return objects;
}

public int hashCode(){
return Arrays.deepHashCode(objects);
}

public boolean equals(Object o){
if (o == this)
return true;

if (!(o instanceof DistinctResultIterator.ObjectArrayWrapper))
return false;

return Arrays.deepEquals(this.getObjects(),((ObjectArrayWrapper)o).getObjects());
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ <T> List<ColumnDescriptor> appendOverriddenColumns(List<ColumnDescriptor> column
int type = getJdbcTypeForProperty(property);
ColumnDescriptor descriptor;
if(property.getType() != null) {
descriptor = new ColumnDescriptor(builder.toString(), type, property.getType().getName());
descriptor = new ColumnDescriptor(builder.toString(), type, property.getType().getCanonicalName());
} else {
descriptor = new ColumnDescriptor(builder.toString(), type);
}
Expand Down
52 changes: 52 additions & 0 deletions cayenne-server/src/test/java/org/apache/cayenne/DataRowTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import org.junit.Test;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertEquals;

public class DataRowTest {

Expand All @@ -35,4 +37,54 @@ public void testVersion() throws Exception {
assertFalse(s3.getVersion() == s1.getVersion());
}

@Test
public void testEquals(){
DataRow d1 = new DataRow(1);
d1.put("FIELD", "test".getBytes());

assertTrue(d1.equals(d1));

DataRow d2 = new DataRow(1);
d2.put("FIELD", "test".getBytes());

assertTrue(d1.equals(d2));
assertTrue(d2.equals(d1));

DataRow d3 = new DataRow(1);
d3.put("FIELD", "test".getBytes());

assertTrue(d2.equals(d3));
assertTrue(d1.equals(d3));

assertFalse(d1.equals(null));

DataRow d4 = new DataRow(1);
d4.put("FIELD1", "test".getBytes());

for(int i = 0; i < 5; i++) {
assertFalse(d3.equals(d4));
}

DataRow d5 = new DataRow(1);
d5.put("FIELD", "test1".getBytes());

DataRow d6 = new DataRow(1);
d6.put("FIELD", "test".getBytes());

assertFalse(d5.equals(d6));
}

@Test
public void testHashCode(){
DataRow d1 = new DataRow(1);
d1.put("FIELD", "test".getBytes());

assertEquals(d1.hashCode(), d1.hashCode());

DataRow d2 = new DataRow(1);
d2.put("FIELD", "test".getBytes());

assertTrue(d1.equals(d2));
assertEquals(d1.hashCode(), d2.hashCode());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,23 @@
package org.apache.cayenne.access.jdbc;

import org.apache.cayenne.DataRow;
import org.apache.cayenne.ObjectContext;
import org.apache.cayenne.access.DataContext;
import org.apache.cayenne.configuration.server.ServerRuntime;
import org.apache.cayenne.di.Inject;
import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.exp.ExpressionFactory;
import org.apache.cayenne.query.CapsStrategy;
import org.apache.cayenne.query.ObjectSelect;
import org.apache.cayenne.query.SQLTemplate;
import org.apache.cayenne.query.SelectQuery;
import org.apache.cayenne.query.EJBQLQuery;
import org.apache.cayenne.test.jdbc.DBHelper;
import org.apache.cayenne.test.jdbc.TableHelper;
import org.apache.cayenne.testdo.lob.ClobTestEntity;
import org.apache.cayenne.testdo.lob.ClobTestRelation;
import org.apache.cayenne.testdo.lob.DistEntity;
import org.apache.cayenne.testdo.lob.DistEntityRel;
import org.apache.cayenne.unit.UnitDbAdapter;
import org.apache.cayenne.unit.di.server.CayenneProjects;
import org.apache.cayenne.unit.di.server.ServerCase;
Expand All @@ -44,6 +53,12 @@ public class SelectActionIT extends ServerCase {
@Inject
private DataContext context;

@Inject
private ServerRuntime serverRuntime;

@Inject
private DBHelper dbHelper;

@Inject
private UnitDbAdapter accessStackAdapter;

Expand Down Expand Up @@ -80,6 +95,67 @@ public void testColumnSelect_DistinctResultIterator() throws Exception {
}
}

@Test
public void testAddingDistinctToQuery() throws Exception{
if (accessStackAdapter.supportsLobs()){

TableHelper tDistEntity = new TableHelper(dbHelper, "DIST_ENTITY");
tDistEntity.setColumns("ID", "NAME", "FIELD");

for (int i = 1; i <= 3; i++) {
tDistEntity.insert(i, "dist_entity" + i, ("dist_entity" + i).getBytes());
}

TableHelper tDistEntityRel = new TableHelper(dbHelper, "DIST_ENTITY_REL");
tDistEntityRel.setColumns("ID", "DIST_ID", "NUM");

for (int i = 1; i <= 10; i++) {
if(i < 5) {
tDistEntityRel.insert(i, 1, i);
}
else{
tDistEntityRel.insert(i, 3, i);
}
}

ObjectContext objectContext = serverRuntime.newContext();

SQLTemplate select = new SQLTemplate(DistEntity.class, "SELECT t0.FIELD, t0.NAME, t0.ID FROM DIST_ENTITY t0 JOIN DIST_ENTITY_REL t1 ON (t0.ID = t1.DIST_ID) WHERE (t1.NUM > 0) AND (t0.NAME LIKE 'dist_entity1')");
select.setColumnNamesCapitalization(CapsStrategy.UPPER);
List<DistEntity> list1 = objectContext.performQuery(select);

assertEquals(4, list1.size());

List<DistEntity> list2 = ObjectSelect.query(DistEntity.class)
.where(DistEntity.DIST_REL.dot(DistEntityRel.NUM).gt(0))
.and(DistEntity.NAME.like("dist_entity1"))
.select(objectContext);

assertEquals(1,list2.size());

EJBQLQuery query1 = new EJBQLQuery("select d FROM DistEntity d JOIN d.distRel r where d.name='dist_entity1' and r.num>0");
List<DistEntity> list3 = context.performQuery(query1);

assertEquals(4,list3.size());

List<String> list4 = ObjectSelect
.columnQuery(DistEntity.class, DistEntity.NAME)
.where(DistEntity.DIST_REL.dot(DistEntityRel.NUM).gt(0))
.and(DistEntity.NAME.eq("dist_entity1"))
.select(context);

assertEquals(1,list4.size());

List<Object[]> list5 = ObjectSelect
.columnQuery(DistEntity.class, DistEntity.NAME, DistEntity.FIELD)
.where(DistEntity.DIST_REL.dot(DistEntityRel.NUM).gt(0))
.and(DistEntity.NAME.eq("dist_entity1"))
.select(context);

assertEquals(1,list5.size());
}
}

protected void insertClobDb() {
ClobTestEntity obj;
for (int i = 0; i < 80; i++) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.apache.cayenne.testdo.lob;

import org.apache.cayenne.testdo.lob.auto._DistEntity;

public class DistEntity extends _DistEntity {

private static final long serialVersionUID = 1L;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.apache.cayenne.testdo.lob;

import org.apache.cayenne.testdo.lob.auto._DistEntityRel;

public class DistEntityRel extends _DistEntityRel {

private static final long serialVersionUID = 1L;

}
Loading