Hibernate OneToMany wrong binding from parent

525 views Asked by At

I have a simple REST spring boot app written on Kotlin.

It has next schema: Map hasOne Channel and Channel hasMany Headers

I have only one action of controller, and want to save all my models in a single request like this:

{
    "channel": {
        "headers": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
        ]
    }
}

If header is not set, all models relate correctly and DB insert order is correct too: Channel created first and after creating a Map with this Channel

But when i add some Header list - Hibernate makes additional Channel (with ID=2) row in DB and bind all Header models to this channel (with ID=2)

Map model:

...
@ManyToOne(cascade = arrayOf(CascadeType.ALL))
@JoinColumn(name = "channel_id")
var channel: Channel = Channel(),
...

Channel model:

...
@OneToMany(mappedBy = "channel", cascade = arrayOf(CascadeType.ALL), )
var headers: MutableSet<Header> = mutableSetOf(),
...
@JsonIgnore
@OneToMany(mappedBy = "channel", cascade = arrayOf(CascadeType.ALL))
var maps: MutableSet<Map> = mutableSetOf(),
...

Header model:

...
@JsonIgnore
@ManyToOne(cascade = arrayOf(CascadeType.ALL))
@JoinColumn(name = "channel_id")
var channel: Channel = Channel(),
...

Maybe some annotation should be fixed. Will be glad to hear any advise. Thanks for all!

UPDATE

Save model like that:

@PostMapping("/maps")
fun post(@RequestBody body: Map) = repo.save(body)

UPDATE 2

If make an object manually, and declare necessary properties type as nullable - channel_id will be null

add this code to action

val channel = Channel(
        ...
)

body.channel!!.headers.forEach {
    val header = Header(
            name = it.name,
            value = it.value
    )

    channel.headers.add(header)
}

val map = Map(
        channel = channel
)

return repo.save(map)
2

There are 2 answers

0
qwert_ukg On BEST ANSWER

ANSWER

it found here

just removed mappedBy = "channel" from collection property and add @JoinColumn(name = "channel_id")

5
Maroš Šeleng On

I guess it's because in your Header, you are creating new Channel each time an instance of Header is created. Moreover, you set the CascadeType to ALL which means that this newly created Channel is persisted also.

You may set the type of channel in Header to Channel? and set the respective channel afterwards.

Edit To answer question from comment: channel_id will be set automatically after you assign the respective channel and save this Header.

Edit 2 First of all, I'd suggest editing models:

Map:

var channel: Channel? = null

And Header:

var channel: Channel? = null

Relevant part of code:

val channel = ... // your Channel entity that is already persisted, for example map.channel (if map was saved before)
val header = Header(..., channel, ...) // create a new Header and set the channel
channel.headers.add(header) // it's bidirectional
repo.save(channel) // this should be enough as you have CascadeType.ALL set, so header should be persisted automatically