Skip to content

Commit 7a31336

Browse files
committed
Avoid duplicate selection of columns.
When the key column of a MappedCollection is also present in the contained entity that column got select twice. We now check if the column already gets selected before adding it to the selection. This only works properly if the column names derived for the entity property and the key column match exactly, which might require use of a `@Column` annotation depending on the used NamingStrategy. Closes #1073
1 parent 5b6bbc1 commit 7a31336

File tree

5 files changed

+134
-27
lines changed

5 files changed

+134
-27
lines changed

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/SqlGenerator.java

+12-12
Original file line numberDiff line numberDiff line change
@@ -381,11 +381,11 @@ private String createAcquireLockById(LockMode lockMode) {
381381
Table table = this.getTable();
382382

383383
Select select = StatementBuilder //
384-
.select(getIdColumn()) //
385-
.from(table) //
386-
.where(getIdColumn().isEqualTo(getBindMarker(ID_SQL_PARAMETER))) //
387-
.lock(lockMode) //
388-
.build();
384+
.select(getIdColumn()) //
385+
.from(table) //
386+
.where(getIdColumn().isEqualTo(getBindMarker(ID_SQL_PARAMETER))) //
387+
.lock(lockMode) //
388+
.build();
389389

390390
return render(select);
391391
}
@@ -395,10 +395,10 @@ private String createAcquireLockAll(LockMode lockMode) {
395395
Table table = this.getTable();
396396

397397
Select select = StatementBuilder //
398-
.select(getIdColumn()) //
399-
.from(table) //
400-
.lock(lockMode) //
401-
.build();
398+
.select(getIdColumn()) //
399+
.from(table) //
400+
.lock(lockMode) //
401+
.build();
402402

403403
return render(select);
404404
}
@@ -727,9 +727,9 @@ static final class Join {
727727

728728
Join(Table joinTable, Column joinColumn, Column parentId) {
729729

730-
Assert.notNull( joinTable,"JoinTable must not be null.");
731-
Assert.notNull( joinColumn,"JoinColumn must not be null.");
732-
Assert.notNull( parentId,"ParentId must not be null.");
730+
Assert.notNull(joinTable, "JoinTable must not be null.");
731+
Assert.notNull(joinColumn, "JoinColumn must not be null.");
732+
Assert.notNull(parentId, "ParentId must not be null.");
733733

734734
this.joinTable = joinTable;
735735
this.joinColumn = joinColumn;

spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/SqlGeneratorUnitTests.java

+18-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import java.util.Map;
2424
import java.util.Set;
2525

26-
import org.assertj.core.api.SoftAssertions;
2726
import org.junit.jupiter.api.BeforeEach;
2827
import org.junit.jupiter.api.Test;
2928
import org.springframework.data.annotation.Id;
@@ -374,6 +373,23 @@ public void findAllByPropertyWithKeyOrdered() {
374373
+ "WHERE dummy_entity.backref = :backref " + "ORDER BY key-column");
375374
}
376375

376+
@Test // GH-1073
377+
public void findAllByPropertyAvoidsDuplicateColumns() {
378+
379+
final SqlGenerator sqlGenerator = createSqlGenerator(ReferencedEntity.class);
380+
final String sql = sqlGenerator.getFindAllByProperty(
381+
Identifier.of(quoted("id"), "parent-id-value", DummyEntity.class), //
382+
quoted("X_L1ID"), // this key column collides with the name derived by the naming strategy for the id of
383+
// ReferencedEntity.
384+
false);
385+
386+
final String id = "referenced_entity.x_l1id AS x_l1id";
387+
assertThat(sql.indexOf(id)) //
388+
.describedAs(sql) //
389+
.isEqualTo(sql.lastIndexOf(id));
390+
391+
}
392+
377393
@Test // DATAJDBC-219
378394
public void updateWithVersion() {
379395

@@ -709,6 +725,7 @@ static class DummyEntity {
709725
Set<Element> elements;
710726
Map<Integer, Element> mappedElements;
711727
AggregateReference<OtherAggregate, Long> other;
728+
Map<Long, ReferencedEntity> mappedReference;
712729
}
713730

714731
static class VersionedEntity extends DummyEntity {

spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/AbstractSegment.java

+15-12
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,15 @@
1515
*/
1616
package org.springframework.data.relational.core.sql;
1717

18+
import java.util.Arrays;
19+
1820
import org.springframework.util.Assert;
1921

2022
/**
2123
* Abstract implementation to support {@link Segment} implementations.
2224
*
2325
* @author Mark Paluch
26+
* @author Jens Schauder
2427
* @since 1.1
2528
*/
2629
abstract class AbstractSegment implements Segment {
@@ -47,21 +50,21 @@ public void visit(Visitor visitor) {
4750
visitor.leave(this);
4851
}
4952

50-
/*
51-
* (non-Javadoc)
52-
* @see java.lang.Object#hashCode()
53-
*/
5453
@Override
55-
public int hashCode() {
56-
return toString().hashCode();
54+
public boolean equals(Object o) {
55+
56+
if (this == o) {
57+
return true;
58+
}
59+
if (o == null || getClass() != o.getClass()) {
60+
return false;
61+
}
62+
AbstractSegment that = (AbstractSegment) o;
63+
return Arrays.equals(children, that.children);
5764
}
5865

59-
/*
60-
* (non-Javadoc)
61-
* @see java.lang.Object#equals(java.lang.Object)
62-
*/
6366
@Override
64-
public boolean equals(Object obj) {
65-
return obj instanceof Segment && toString().equals(obj.toString());
67+
public int hashCode() {
68+
return Arrays.hashCode(children);
6669
}
6770
}

spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Column.java

+44
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*/
1616
package org.springframework.data.relational.core.sql;
1717

18+
import java.util.Objects;
19+
1820
import org.springframework.lang.Nullable;
1921
import org.springframework.util.Assert;
2022

@@ -364,6 +366,27 @@ String getPrefix() {
364366
return prefix;
365367
}
366368

369+
@Override
370+
public boolean equals(Object o) {
371+
372+
if (this == o) {
373+
return true;
374+
}
375+
if (o == null || getClass() != o.getClass()) {
376+
return false;
377+
}
378+
if (!super.equals(o)) {
379+
return false;
380+
}
381+
Column column = (Column) o;
382+
return name.equals(column.name) && table.equals(column.table);
383+
}
384+
385+
@Override
386+
public int hashCode() {
387+
return Objects.hash(super.hashCode(), name, table);
388+
}
389+
367390
/**
368391
* {@link Aliased} {@link Column} implementation.
369392
*/
@@ -419,5 +442,26 @@ public Column from(Table table) {
419442
public String toString() {
420443
return getPrefix() + getName() + " AS " + getAlias();
421444
}
445+
446+
@Override
447+
public boolean equals(Object o) {
448+
449+
if (this == o) {
450+
return true;
451+
}
452+
if (o == null || getClass() != o.getClass()) {
453+
return false;
454+
}
455+
if (!super.equals(o)) {
456+
return false;
457+
}
458+
AliasedColumn that = (AliasedColumn) o;
459+
return alias.equals(that.alias);
460+
}
461+
462+
@Override
463+
public int hashCode() {
464+
return Objects.hash(super.hashCode(), alias);
465+
}
422466
}
423467
}

spring-data-relational/src/main/java/org/springframework/data/relational/core/sql/Table.java

+45-2
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,18 @@
1515
*/
1616
package org.springframework.data.relational.core.sql;
1717

18+
import java.util.Objects;
19+
1820
import org.springframework.util.Assert;
1921

2022
/**
21-
* Represents a table reference within a SQL statement. Typically used to denote {@code FROM} or {@code JOIN} or to
23+
* Represents a table reference within a SQL statement. Typically, used to denote {@code FROM} or {@code JOIN} or to
2224
* prefix a {@link Column}.
2325
* <p/>
2426
* Renders to: {@code <name>} or {@code <name> AS <name>}.
2527
*
2628
* @author Mark Paluch
29+
* @author Jens Schauder
2730
* @since 1.1
2831
*/
2932
public class Table extends AbstractSegment implements TableLike {
@@ -107,7 +110,6 @@ public Table as(SqlIdentifier alias) {
107110
return new AliasedTable(name, alias);
108111
}
109112

110-
111113
/*
112114
* (non-Javadoc)
113115
* @see org.springframework.data.relational.core.sql.Named#getName()
@@ -135,6 +137,26 @@ public String toString() {
135137
return name.toString();
136138
}
137139

140+
@Override
141+
public boolean equals(Object o) {
142+
if (this == o) {
143+
return true;
144+
}
145+
if (o == null || getClass() != o.getClass()) {
146+
return false;
147+
}
148+
if (!super.equals(o)) {
149+
return false;
150+
}
151+
Table table = (Table) o;
152+
return name.equals(table.name);
153+
}
154+
155+
@Override
156+
public int hashCode() {
157+
return Objects.hash(super.hashCode(), name);
158+
}
159+
138160
/**
139161
* {@link Aliased} {@link Table} implementation.
140162
*/
@@ -184,5 +206,26 @@ public SqlIdentifier getReferenceName() {
184206
public String toString() {
185207
return getName() + " AS " + getAlias();
186208
}
209+
210+
@Override
211+
public boolean equals(Object o) {
212+
213+
if (this == o) {
214+
return true;
215+
}
216+
if (o == null || getClass() != o.getClass()) {
217+
return false;
218+
}
219+
if (!super.equals(o)) {
220+
return false;
221+
}
222+
AliasedTable that = (AliasedTable) o;
223+
return alias.equals(that.alias);
224+
}
225+
226+
@Override
227+
public int hashCode() {
228+
return Objects.hash(super.hashCode(), alias);
229+
}
187230
}
188231
}

0 commit comments

Comments
 (0)