Skip to content

Commit 871d586

Browse files
GH-2276 - Support default, primitive Kotlin attributes.
This bugfix allows primitive attributes with defaults in Kotlin based domain classes. It comes at a price however: We cannot throw a mapping exception hinting at the fact a primitive had been tried to be assigned with null in standard java. The ` MappingException`’s cause will than be a `NullpointerException`. The value of supporting Kotlin defaults is in this case higher than the error message, which was "received" with a trick anyway (relying on the conversion service that it throws an exception. This fixes #2276.
1 parent bd9c5c6 commit 871d586

File tree

4 files changed

+61
-6
lines changed

4 files changed

+61
-6
lines changed

src/main/java/org/springframework/data/neo4j/core/mapping/DefaultNeo4jConversionService.java

+2-3
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,9 @@ private Object readValueImpl(@Nullable Value value, TypeInformation<?> type,
9090
.forEach(element -> target.add(conversion.apply(element, type.getComponentType().getType())));
9191
return target;
9292
}
93-
94-
return conversion.apply(valueIsLiteralNullOrNullValue ? null : value, rawType);
93+
return valueIsLiteralNullOrNullValue ? null : conversion.apply(value, rawType);
9594
} catch (Exception e) {
96-
String msg = String.format("Could not convert %s into %s", value, type.toString());
95+
String msg = String.format("Could not convert %s into %s", value, type);
9796
throw new TypeMismatchDataAccessException(msg, e);
9897
}
9998
}

src/test/java/org/springframework/data/neo4j/integration/conversion_imperative/TypeConversionIT.java

+2-3
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,8 @@ void thereShallBeNoDefaultValuesForNonExistingAttributes(@Autowired NonExistingP
117117
.isThrownBy(() -> repository.findById(ID_OF_NON_EXISTING_PRIMITIVES_NODE))
118118
.withMessageMatching(
119119
"Error mapping Record<\\{n: \\{__internalNeo4jId__: \\d+, id: NULL, someBoolean: NULL, __nodeLabels__: \\[\"NonExistingPrimitives\"\\]\\}\\}>")
120-
.withStackTraceContaining(
121-
"org.springframework.dao.TypeMismatchDataAccessException: Could not convert NULL into boolean; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [null] to type [boolean] for value 'null'; nested exception is java.lang.IllegalArgumentException: A null value cannot be assigned to a primitive type")
122-
.withRootCauseInstanceOf(IllegalArgumentException.class);
120+
.withStackTraceContaining("unboxBoolean")
121+
.withRootCauseInstanceOf(NullPointerException.class);
123122
}
124123

125124
@TestFactory

src/test/java/org/springframework/data/neo4j/integration/kotlin/KotlinIT.java

+23
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
import org.springframework.data.neo4j.integration.shared.common.KotlinClubRelationship;
3535
import org.springframework.data.neo4j.integration.shared.common.KotlinPerson;
3636
import org.springframework.data.neo4j.integration.shared.common.KotlinRepository;
37+
import org.springframework.data.neo4j.integration.shared.common.TestPersonEntity;
38+
import org.springframework.data.neo4j.integration.shared.common.TestPersonRepository;
3739
import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories;
3840
import org.springframework.data.neo4j.test.BookmarkCapture;
3941
import org.springframework.data.neo4j.test.Neo4jExtension.Neo4jConnectionSupport;
@@ -67,6 +69,11 @@ void setup(@Autowired BookmarkCapture bookmarkCapture) {
6769
+ " (n)-[:WORKS_IN{since: 2019}]->(:KotlinClub{name: 'Golf club'}) SET n.name = $personName",
6870
Values.parameters("personName", PERSON_NAME))
6971
.consume();
72+
transaction.run("CREATE (p1:TestPerson {id: \"first\", name: \"First name\"})\n"
73+
+ "CREATE (p2:TestPerson {id: \"second\"})\n"
74+
+ "CREATE (d:TestDepartment {id: \"department\", name: \"Test\"})\n"
75+
+ "CREATE (p1)-[:MEMBER_OF]->(d)\n"
76+
+ "CREATE (p2)-[:MEMBER_OF]->(d)\n").consume();
7077
transaction.commit();
7178
bookmarkCapture.seedWith(session.lastBookmark());
7279
}
@@ -83,6 +90,22 @@ void findAllKotlinPersons(@Autowired KotlinRepository repository) {
8390
.extracting(KotlinClub::getName).containsExactly("Golf club");
8491
}
8592

93+
@Test // GH-2272
94+
void primitiveDefaultValuesShouldWork(@Autowired TestPersonRepository repository) {
95+
96+
Iterable<TestPersonEntity> people = repository.findAll();
97+
assertThat(people)
98+
.allSatisfy(p -> {
99+
assertThat(p.getOtherPrimitive()).isEqualTo(32);
100+
assertThat(p.getSomeTruth()).isTrue();
101+
if (p.getId().equalsIgnoreCase("first")) {
102+
assertThat(p.getName()).isEqualTo("First name");
103+
} else {
104+
assertThat(p.getName()).isEqualTo("Unknown");
105+
}
106+
});
107+
}
108+
86109
@Configuration
87110
@EnableNeo4jRepositories(basePackageClasses = KotlinPerson.class)
88111
@EnableTransactionManagement
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2011-2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.neo4j.integration.shared.common
17+
18+
import org.springframework.data.neo4j.core.schema.Id
19+
import org.springframework.data.neo4j.core.schema.Node
20+
import org.springframework.data.neo4j.core.schema.Property
21+
import org.springframework.data.neo4j.repository.Neo4jRepository
22+
23+
@Node("TestPerson")
24+
data class TestPersonEntity(
25+
@Id
26+
val id: String,
27+
@Property("name")
28+
val name: String = "Unknown",
29+
val otherPrimitive: Int = 32,
30+
val someTruth: Boolean = true
31+
)
32+
33+
interface TestPersonRepository : Neo4jRepository<TestPersonEntity, String> {
34+
}

0 commit comments

Comments
 (0)