0

I have two entities; municipality and city. Municipalities are assumed to have unique names whereas cities are assumed to be unique within their municipality.

Is there a way to set up a constraint for cities so that the combination of its name and its municipality's name must be unique?


Entities

class XdCity(entity: Entity) : XdEntity(entity) {
    companion object : XdNaturalEntityType<XdCity>()

    var name by xdRequiredStringProp()
    var municipality: XdMunicipality by xdLink1(
        XdMunicipality::cities,
        onDelete = OnDeletePolicy.CLEAR,
        onTargetDelete = OnDeletePolicy.CASCADE
    )
}
class XdMunicipality(entity: Entity) : XdEntity(entity) {
    companion object : XdNaturalEntityType<XdMunicipality>()

    var name by xdRequiredStringProp(unique = true)
    val cities by xdLink1_N(
        XdCity::municipality,
        onDelete = OnDeletePolicy.CASCADE,
        onTargetDelete = OnDeletePolicy.CLEAR
    )
}

Test case

@Test
fun testAddSameCityName() {
    Database.store.transactional {
        val municipality = XdMunicipality.new("Mun 1")
        val city = XdCity.new("City")
        city.municipality = municipality
    }

    // Allow insertion of same city name in other municipality
    Database.store.transactional {
        val municipality = XdMunicipality.new("Mun 2")
        val city = XdCity.new("City")
        city.municipality = municipality
    }

    // Do not allow insertion of existing city name in municipality
    assertFailsWith<ConstraintsValidationException> {
        Database.store.transactional {
            val municipality = XdMunicipality.find("Mun 1")
            val city = XdCity.new("City")
            city.municipality = municipality
        }
    }
}
Felix ZY
  • 674
  • 6
  • 14

1 Answers1

1

The advised approach is to use xdParent and xdChildren relations between XdCity and XdMunicipality.

It may be not so easy to change relation when a database has history. To solve the problem with unique names in scope of municipality you can use composite indexes for entity types like here:

        companion object : XdNaturalEntityType<XdCity>() {
            override val compositeIndices = listOf(
                    listOf(XdCity:: municipality, ...may goes anything like XdCity::name)
            )
        }
lehvolk
  • 233
  • 1
  • 4
  • Database history is not an issue in the current case fortunately. I was looking at using `XdParent`/`XdChildren` - are you implying that alone would solve the issue? I decided against using parent/children as my case involves three layers and the parent/children implementation allowed the creation of the middle layer without any children or parents. – Felix ZY Mar 06 '20 at 12:00
  • Let me rephrase the answer then. Parent/Children relation doesn't bring constraints working in scope of parent property. It's just simplify domain model in case of situation when domain entity targeted to another one. For you situation `composite indexes` will work perfect. But keep in mind that this constraint will be checked only on transactional flush method called. – lehvolk Mar 06 '20 at 12:30