Skip to content

Commit 0816f41

Browse files
schaudergregturn
authored andcommitted
DATAJDBC-266 - Entities referenced by 1:1 don't need an Id.
The id-property was used to determine if there is an instance at all, or if it was null. For entities that don't have an id that purpose is now fulfilled by selecting the backreference and checking it against null. See also: DATAJDBC-223.
1 parent f46a01d commit 0816f41

File tree

8 files changed

+125
-12
lines changed

8 files changed

+125
-12
lines changed

src/main/java/org/springframework/data/jdbc/core/EntityRowMapper.java

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121

2222
import org.springframework.core.convert.converter.Converter;
2323
import org.springframework.data.mapping.MappingException;
24-
import org.springframework.data.mapping.PersistentProperty;
2524
import org.springframework.data.mapping.PersistentPropertyAccessor;
2625
import org.springframework.data.mapping.PreferredConstructor;
2726
import org.springframework.data.relational.core.conversion.RelationalConverter;
@@ -118,29 +117,30 @@ private T populateProperties(T result, ResultSet resultSet) {
118117
@Nullable
119118
private Object readFrom(ResultSet resultSet, RelationalPersistentProperty property, String prefix) {
120119

121-
try {
122-
123-
if (property.isEntity()) {
124-
return readEntityFrom(resultSet, property);
125-
}
120+
if (property.isEntity()) {
121+
return readEntityFrom(resultSet, property);
122+
}
126123

127-
return converter.readValue(resultSet.getObject(prefix + property.getColumnName()), property.getTypeInformation());
124+
Object value = getObjectFromResultSet(resultSet, prefix + property.getColumnName());
125+
return converter.readValue(value, property.getTypeInformation());
128126

129-
} catch (SQLException o_O) {
130-
throw new MappingException(String.format("Could not read property %s from result set!", property), o_O);
131-
}
132127
}
133128

134129
@Nullable
135-
private <S> S readEntityFrom(ResultSet rs, PersistentProperty<?> property) {
130+
private <S> S readEntityFrom(ResultSet rs, RelationalPersistentProperty property) {
136131

137132
String prefix = property.getName() + "_";
138133

139134
@SuppressWarnings("unchecked")
140135
RelationalPersistentEntity<S> entity = (RelationalPersistentEntity<S>) context
141136
.getRequiredPersistentEntity(property.getActualType());
142137

143-
if (readFrom(rs, entity.getRequiredIdProperty(), prefix) == null) {
138+
RelationalPersistentProperty idProperty = entity.getIdProperty();
139+
140+
if ((idProperty != null //
141+
? readFrom(rs, idProperty, prefix) //
142+
: getObjectFromResultSet(rs, prefix + property.getReverseColumnName()) //
143+
) == null) {
144144
return null;
145145
}
146146

@@ -155,6 +155,16 @@ private <S> S readEntityFrom(ResultSet rs, PersistentProperty<?> property) {
155155
return instance;
156156
}
157157

158+
@Nullable
159+
private Object getObjectFromResultSet(ResultSet rs, String backreferenceName) {
160+
161+
try {
162+
return rs.getObject(backreferenceName);
163+
} catch (SQLException o_O) {
164+
throw new MappingException(String.format("Could not read value %s from result set!", backreferenceName), o_O);
165+
}
166+
}
167+
158168
private <S> S createInstance(RelationalPersistentEntity<S> entity, ResultSet rs, String prefix) {
159169

160170
return converter.createInstance(entity, parameter -> {

src/main/java/org/springframework/data/jdbc/core/SqlGenerator.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,17 @@ private void addColumnsAndJoinsForOneToOneReferences(SelectBuilder builder) {
201201
.as(joinAlias + "_" + refProperty.getColumnName()) //
202202
);
203203
}
204+
205+
// if the referenced property doesn't have an id, include the back reference in the select list.
206+
// this enables determining if the referenced entity is present or null.
207+
if (!refEntity.hasIdProperty()) {
208+
209+
builder.column( //
210+
cb -> cb.tableAlias(joinAlias) //
211+
.column(property.getReverseColumnName()) //
212+
.as(joinAlias + "_" + property.getReverseColumnName()) //
213+
);
214+
}
204215
}
205216
}
206217

src/test/java/org/springframework/data/jdbc/core/AggregateTemplateIntegrationTests.java

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,52 @@ public void changeReferencedEntity() {
210210
assertThat(reloadedLegoSet.manual.content).isEqualTo("new content");
211211
}
212212

213+
@Test // DATAJDBC-266
214+
public void oneToOneChildWithoutId() {
215+
216+
OneToOneParent parent = new OneToOneParent();
217+
218+
parent.content = "parent content";
219+
parent.child = new OneToOneChildNoId();
220+
parent.child.content = "child content";
221+
222+
template.save(parent);
223+
224+
OneToOneParent reloaded = template.findById(parent.id, OneToOneParent.class);
225+
226+
assertThat(reloaded.child.content).isEqualTo("child content");
227+
}
228+
229+
@Test // DATAJDBC-266
230+
public void oneToOneNullChildWithoutId() {
231+
232+
OneToOneParent parent = new OneToOneParent();
233+
234+
parent.content = "parent content";
235+
parent.child = null;
236+
237+
template.save(parent);
238+
239+
OneToOneParent reloaded = template.findById(parent.id, OneToOneParent.class);
240+
241+
assertThat(reloaded.child).isNull();
242+
}
243+
244+
@Test // DATAJDBC-266
245+
public void oneToOneNullAttributes() {
246+
247+
OneToOneParent parent = new OneToOneParent();
248+
249+
parent.content = "parent content";
250+
parent.child = new OneToOneChildNoId();
251+
252+
template.save(parent);
253+
254+
OneToOneParent reloaded = template.findById(parent.id, OneToOneParent.class);
255+
256+
assertThat(reloaded.child).isNotNull();
257+
}
258+
213259
private static LegoSet createLegoSet() {
214260

215261
LegoSet entity = new LegoSet();
@@ -241,6 +287,18 @@ static class Manual {
241287

242288
}
243289

290+
static class OneToOneParent {
291+
292+
@Id private Long id;
293+
private String content;
294+
295+
private OneToOneChildNoId child;
296+
}
297+
298+
static class OneToOneChildNoId {
299+
private String content;
300+
}
301+
244302
@Configuration
245303
@Import(TestConfiguration.class)
246304
static class Config {

src/test/java/org/springframework/data/jdbc/core/SqlGeneratorUnitTests.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,19 @@ public void getInsertForEmptyColumnList() {
186186
assertThat(insert).endsWith("()");
187187
}
188188

189+
@Test // DATAJDBC-266
190+
public void joinForOneToOneWithoutIdIncludesTheBackReferenceOfTheOuterJoin() {
191+
192+
SqlGenerator sqlGenerator = createSqlGenerator(ParentOfNoIdChild.class);
193+
194+
String findAll = sqlGenerator.getFindAll();
195+
196+
assertThat(findAll).containsSequence(
197+
"SELECT",
198+
"child.parent_of_no_id_child AS child_parent_of_no_id_child",
199+
"FROM");
200+
}
201+
189202
private PersistentPropertyPath<RelationalPersistentProperty> getPath(String path, Class<?> base) {
190203
return PersistentPropertyPathTestUtils.getPath(context, path, base);
191204
}
@@ -220,6 +233,15 @@ static class Element {
220233
String content;
221234
}
222235

236+
static class ParentOfNoIdChild {
237+
@Id Long id;
238+
NoIdChild child;
239+
}
240+
241+
static class NoIdChild {
242+
243+
}
244+
223245
private static class PrefixingNamingStrategy implements NamingStrategy {
224246

225247
@Override

src/test/resources/org.springframework.data.jdbc.core/AggregateTemplateIntegrationTests-hsql.sql

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@ CREATE TABLE MANUAL ( id BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) P
33

44
ALTER TABLE MANUAL ADD FOREIGN KEY (LEGO_SET)
55
REFERENCES LEGO_SET(id);
6+
7+
CREATE TABLE ONE_TO_ONE_PARENT ( id BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY, content VARCHAR(30));
8+
CREATE TABLE One_To_One_Child_No_Id (ONE_TO_ONE_PARENT INTEGER PRIMARY KEY, content VARCHAR(30));

src/test/resources/org.springframework.data.jdbc.core/AggregateTemplateIntegrationTests-mariadb.sql

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@ CREATE TABLE MANUAL ( id BIGINT AUTO_INCREMENT PRIMARY KEY, LEGO_SET BIGINT, CON
33

44
ALTER TABLE MANUAL ADD FOREIGN KEY (LEGO_SET)
55
REFERENCES LEGO_SET(id);
6+
7+
CREATE TABLE ONE_TO_ONE_PARENT ( id BIGINT AUTO_INCREMENT PRIMARY KEY, content VARCHAR(30));
8+
CREATE TABLE One_To_One_Child_No_Id (ONE_TO_ONE_PARENT INTEGER PRIMARY KEY, content VARCHAR(30));

src/test/resources/org.springframework.data.jdbc.core/AggregateTemplateIntegrationTests-mysql.sql

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@ CREATE TABLE MANUAL ( id BIGINT AUTO_INCREMENT PRIMARY KEY, LEGO_SET BIGINT, CON
33

44
ALTER TABLE MANUAL ADD FOREIGN KEY (LEGO_SET)
55
REFERENCES LEGO_SET(id);
6+
7+
CREATE TABLE ONE_TO_ONE_PARENT ( id BIGINT AUTO_INCREMENT PRIMARY KEY, content VARCHAR(30));
8+
CREATE TABLE One_To_One_Child_No_Id (ONE_TO_ONE_PARENT INTEGER PRIMARY KEY, content VARCHAR(30));

src/test/resources/org.springframework.data.jdbc.core/AggregateTemplateIntegrationTests-postgres.sql

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,6 @@ CREATE TABLE MANUAL ( id SERIAL PRIMARY KEY, LEGO_SET BIGINT, CONTENT VARCHAR(20
66

77
ALTER TABLE MANUAL ADD FOREIGN KEY (LEGO_SET)
88
REFERENCES LEGO_SET(id);
9+
10+
CREATE TABLE ONE_TO_ONE_PARENT ( id SERIAL PRIMARY KEY, content VARCHAR(30));
11+
CREATE TABLE One_To_One_Child_No_Id (ONE_TO_ONE_PARENT INTEGER PRIMARY KEY, content VARCHAR(30));

0 commit comments

Comments
 (0)