Skip to content

Commit 5e1f481

Browse files
Add an example for Kotlins inline classes.
Kotlin provides so called „Inline classes“ (https://kotlinlang.org/docs/reference/inline-classes.html), wrapping standard types into a dedicated type. This makes them a nice fit for IDs etc. The object mapping process works ootb, the parameter mapping needs some extra work: One has to provide custom serializers for all data classes being used (see `IdTypes`). Those serializers can be registered with a dedicated `SimpleModule`. This module needs to be added to the `ObjectMapper` instance used by OGM to convert parameters: ``` ObjectMapperFactory.objectMapper() .registerModule(KotlinModule()) .registerModule(IdTypesModule()) ``` The kotlin jackson module may detect them at some point in the future itself (See FasterXML/jackson-module-kotlin#199), but until than that work is necessary. This closes #822.
1 parent 7425b15 commit 5e1f481

File tree

5 files changed

+111
-1
lines changed

5 files changed

+111
-1
lines changed

neo4j-ogm-tests/neo4j-ogm-integration-tests/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,11 @@
142142
<groupId>org.jetbrains.kotlin</groupId>
143143
<artifactId>kotlin-stdlib-jdk8</artifactId>
144144
</dependency>
145+
<dependency>
146+
<groupId>com.fasterxml.jackson.module</groupId>
147+
<artifactId>jackson-module-kotlin</artifactId>
148+
</dependency>
149+
145150
<dependency>
146151
<groupId>org.jetbrains.kotlin</groupId>
147152
<artifactId>kotlin-test</artifactId>
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright (c) 2002-2020 "Neo4j,"
3+
* Neo4j Sweden AB [http://neo4j.com]
4+
*
5+
* This file is part of Neo4j.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
package org.neo4j.ogm.domain.gh822
20+
21+
import com.fasterxml.jackson.core.JsonGenerator
22+
import com.fasterxml.jackson.databind.SerializerProvider
23+
import com.fasterxml.jackson.databind.ser.std.StdSerializer
24+
import java.io.IOException
25+
import java.io.Serializable
26+
27+
inline class StringID(val value: String) : Serializable
28+
29+
private class StringIDSerializer : StdSerializer<StringID>(StringID::class.java) {
30+
@Throws(IOException::class)
31+
override fun serialize(s: StringID, jsonGenerator: JsonGenerator,
32+
serializerProvider: SerializerProvider) {
33+
jsonGenerator.writeObject(s.value)
34+
}
35+
}
36+
37+
class IdTypesModule : com.fasterxml.jackson.databind.module.SimpleModule() {
38+
39+
init {
40+
addSerializer(StringIDSerializer())
41+
}
42+
}
43+
44+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright (c) 2002-2020 "Neo4j,"
3+
* Neo4j Sweden AB [http://neo4j.com]
4+
*
5+
* This file is part of Neo4j.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
package org.neo4j.ogm.domain.gh822
20+
21+
import org.neo4j.ogm.annotation.Id
22+
import org.neo4j.ogm.annotation.NodeEntity
23+
24+
@NodeEntity
25+
data class User(
26+
@Id var userId: StringID? = null,
27+
val name: String
28+
)

neo4j-ogm-tests/neo4j-ogm-integration-tests/src/test/kotlin/org/neo4j/ogm/kotlin/KotlinInteropTest.kt

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
*/
1919
package org.neo4j.ogm.kotlin
2020

21+
import com.fasterxml.jackson.module.kotlin.KotlinModule
2122
import org.assertj.core.api.Assertions.assertThat
2223
import org.junit.*
2324
import org.neo4j.driver.AuthTokens
@@ -27,6 +28,7 @@ import org.neo4j.driver.Values
2728
import org.neo4j.harness.ServerControls
2829
import org.neo4j.harness.TestServerBuilders
2930
import org.neo4j.ogm.config.Configuration
31+
import org.neo4j.ogm.config.ObjectMapperFactory
3032
import org.neo4j.ogm.cypher.ComparisonOperator
3133
import org.neo4j.ogm.cypher.Filter
3234
import org.neo4j.ogm.cypher.Filters
@@ -38,6 +40,7 @@ import org.neo4j.ogm.domain.delegation.KotlinAImpl
3840
import org.neo4j.ogm.domain.gh696.Lion
3941
import org.neo4j.ogm.domain.gh696.Zebra
4042
import org.neo4j.ogm.domain.gh696.ZooKotlin
43+
import org.neo4j.ogm.domain.gh822.*
4144
import org.neo4j.ogm.session.*
4245
import kotlin.test.assertNotNull
4346

@@ -67,8 +70,13 @@ class KotlinInteropTest {
6770
sessionFactory = SessionFactory(ogmConfiguration,
6871
MyNode::class.java.`package`.name,
6972
KotlinAImpl::class.java.`package`.name,
70-
ZooKotlin::class.java.`package`.name
73+
ZooKotlin::class.java.`package`.name,
74+
User::class.java.`package`.name
7175
)
76+
77+
ObjectMapperFactory.objectMapper()
78+
.registerModule(KotlinModule())
79+
.registerModule(IdTypesModule())
7280
}
7381

7482
@AfterClass
@@ -228,4 +236,20 @@ class KotlinInteropTest {
228236
val numberOfMyNodes = sessionFactory.openSession().count<MyNode>(listOf(Filter("name", ComparisonOperator.EQUALS, "Brian")))
229237
assertThat(numberOfMyNodes).isEqualTo(1)
230238
}
239+
240+
@Test // GH-822
241+
fun `inline classes should work with dedicated serializers`() {
242+
243+
val userId = StringID("aUserId")
244+
val userToSave = User(userId, "Danger Dan");
245+
246+
sessionFactory.openSession().save(userToSave);
247+
248+
val nameFilter = Filter("userId", ComparisonOperator.EQUALS, userId).ignoreCase()
249+
val loadedUsers = sessionFactory.openSession().loadAll(User::class.java, nameFilter);
250+
assertThat(loadedUsers).hasSize(1).extracting<StringID>(User::userId).containsOnly(userId)
251+
252+
val loadedUser = sessionFactory.openSession().queryForObject<User>("MATCH (u:User) RETURN u", mapOf(Pair("userId", userId)))!!
253+
assertThat(loadedUser).isNotNull().extracting(User::userId).isEqualTo(userId)
254+
}
231255
}

neo4j-ogm-tests/neo4j-ogm-integration-tests/src/test/kotlin/org/neo4j/ogm/kotlin/KotlinMetaDataTest.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,13 @@ class KotlinMetaDataTest {
6767
assertThat(kotlinZoo.getFieldInfo("animals").typeDescriptor).isEqualTo("org.neo4j.ogm.domain.gh696.Animal")
6868
assertThat(kotlinZoo.getFieldInfo("zookeepers").typeDescriptor).isEqualTo("org.neo4j.ogm.domain.gh696.Zookeeper")
6969
}
70+
71+
@Test // GH-822
72+
fun `OGM should detect the inlines class underlying value type`() {
73+
val metaData = MetaData("org.neo4j.ogm.domain.gh822")
74+
val userClassInfo = metaData.classInfo("User")
75+
76+
assertThat(userClassInfo).isNotNull
77+
assertThat(userClassInfo.getFieldInfo("userId").typeDescriptor).isEqualTo("java.lang.String")
78+
}
7079
}

0 commit comments

Comments
 (0)