cancel
Showing results for 
Search instead for 
Did you mean: 

Head's Up! These forums are read-only. All users and content have migrated. Please join us at community.neo4j.com.

Kotlin Relationships for Spring Data, Nullable, Collection & Individual

wayne
Node Clone

Greetings all,
I wonder if any of you can assist me with a concern.

I have been experimenting with Spring Data Neo4j for Kotlin over the past few days.

Spring Boot version 2.2.4.RELEASE
Kotlin version 1.3.61
Neo4j version 4.0.0

As an example, if I set up a class such as:

@NodeEntity
data class Kennel constructor (@Id @GeneratedValue val id: Long? = null, @Relationship val dog: Dog) {
}

where a Dog is:

@NodeEntity
data class Dog constructor (@Id @GeneratedValue var id: Long? = null, @Relationship var name: String) {
}

and then try to save and load an object of this Kennel class:

@ExtendWith(SpringExtension::class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Transactional
class KennelRepositoryTest @Autowired constructor (val kennelRepository: KennelRepository,
                                                   val session: Session) {
    var dogWrite: Dog = Dog(name = "rover");
    var kennelWrite: Kennel = Kennel(dog = dogWrite)

    @BeforeEach
    fun beforeEach() {
        session.purgeDatabase()
        kennelRepository.save<Kennel>(kennelWrite)
        session.clear()
    }

    @Test
    fun findById() {
        val kennelRead: Kennel = kennelRepository.findById(kennelWrite.id!!).orElseGet (null)
        assertEquals(kennelWrite, kennelRead)
    }
}

I see an IllegalArgumentException on findById of:

java.lang.IllegalArgumentException: Parameter specified as non-null is null: method w.resourceserver.domain.Kennel.<init>, parameter dog

resulting from:

Caused by: org.springframework.data.mapping.model.MappingInstantiationException: Failed to instantiate w.resourceserver.domain.Kennel using constructor fun <init>(kotlin.Long?, w.resourceserver.domain.Dog): w.resourceserver.domain.Kennel with arguments null,null,1,null

If I instead use an alternative Kennel with a Nullable dog

@NodeEntity
data class KennelWithNullable constructor (@Id @GeneratedValue val id: Long? = null, @Relationship val dog: Dog?) {
}

or a dog in a collection:

@NodeEntity
data class KennelWithSet constructor (@Id @GeneratedValue val id: Long? = null, @Relationship val dogSet: Set<Dog>) {
}

the findById method seems to work fine.

Is this expected behaviour or should I try and continue debugging ?

Many thanks for any feedback.

1 ACCEPTED SOLUTION

Unfortunately you are correct and this is expected behaviour we cannot easily fix.
The underlying mapping framework (Neo4j-OGM) has no concept of instantiating the related classes before the main entity. As a result the Kennel will get created (if the Dogs are a collection with an empty one) and the relationship will get filled / set later in the mapping phase.

On another node: If you want to get started with Spring Data Neo4j in general, I suggest to have a look at Spring Data Neo4j RX. It is planned to be the successor of Spring Data Neo4j in the long run. It has already the immutability support you are looking for.

View solution in original post

3 REPLIES 3

Unfortunately you are correct and this is expected behaviour we cannot easily fix.
The underlying mapping framework (Neo4j-OGM) has no concept of instantiating the related classes before the main entity. As a result the Kennel will get created (if the Dogs are a collection with an empty one) and the relationship will get filled / set later in the mapping phase.

On another node: If you want to get started with Spring Data Neo4j in general, I suggest to have a look at Spring Data Neo4j RX. It is planned to be the successor of Spring Data Neo4j in the long run. It has already the immutability support you are looking for.

wayne
Node Clone

Thanks for the clarification. Much appreciated. I will start to look into Spring Data Neo4j RX.

wayne
Node Clone

Also, I have noted that if, on the same setup, I make a slight modification to the Dog to include a Kotlin Int:

@NodeEntity
data class Dog constructor (@Id @GeneratedValue var id: Long? = null, var name: String, var owners:Int) {}

then with the addition in my build.gradle.kts of an:

implementation ("org.springframework.boot:spring-boot-starter-data-rest")

I can realise an error of:

No converter found capable of converting from type [java.lang.Long] to type [int]

even with a Nullable dog or a dog in a collection.

So, are there meant to be inbuilt converters to support Kotlin Int ?
Or am I barking up the wrong tree here, no pun intended.
If not, would it be desirable to have them ?
I should add that Kotlin Longs seem to work fine.