Hi,
In my latest project, I created an extended version of JpaDataStore to handle an error when deleting a record with related other records. Unfortunately, it works only with MSSQL database. I think such functionality would be useful in the next version of jmix with support for other databases
Regards
Marcin
@Component
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
@Primary
class ExtJpaDataStore(private val messages: Messages) : JpaDataStore() {
override fun beforeSaveTransactionCommit(
context: SaveContext,
savedEntities: MutableCollection<Any>,
removedEntities: MutableCollection<Any>
) {
if (context.isJoinTransaction) {
val entities: MutableList<Any> = ArrayList(savedEntities)
entities.addAll(removedEntities)
val eventsInfo: List<EntityChangedEventInfo>
val em = storeAwareLocator.getEntityManager(storeName)
val softDeletionBefore = PersistenceHints.isSoftDeletion(em)
try {
em.setProperty(PersistenceHints.SOFT_DELETION, context.hints[PersistenceHints.SOFT_DELETION])
persistenceSupport.processFlush(em, false)
eventsInfo = entityChangedEventManager.collect(persistenceSupport.getInstances(em))
(em.delegate as EntityManager).flush()
} catch (e: PersistenceException) {
val exception = e.toString()
val pattern = uniqueConstraintViolationPattern
val matcher = pattern.matcher(exception)
if (matcher.find()) {
throw UniqueConstraintViolationException(e.message, resolveConstraintName(matcher), e)
} else {
val pattern2 =
Regex("""The DELETE statement conflicted with the REFERENCE constraint "(.+?)"\. The conflict occurred in database "(.+?)", table "(.+?)\.(.+?)", column '(.+?)'""")
pattern2.find(exception)?.let { matchResult ->
val (constraintName, dbName, schemaName, tableName, columnName) = matchResult.destructured
metadata.session.classes.find { metaClazz ->
metadataTools.getDatabaseTable(metaClazz)?.compareTo(tableName, false) == 0
}?.let { metaClazz ->
metaClazz.properties
.find { prop -> metadataTools.getDatabaseColumn(prop)?.compareTo(columnName) == 0 }
?.let { prop ->
var name =
messages.getMessage(metaClazz.getJavaClass<Any>().name.replaceLast('.', '/'))
throw ReferenceConstraintViolationException(
"You cannot delete a record because it is linked to another record in the table: $name",
e
)
}
}
}
}
throw e
} finally {
em.setProperty(PersistenceHints.SOFT_DELETION, softDeletionBefore)
}
val events: MutableList<EntityChangedEvent<*>> = ArrayList(eventsInfo.size)
for (info in eventsInfo) {
events.add(
EntityChangedEvent(
info.source,
Id.of(info.entity), info.type, info.changes, info.originalMetaClass
)
)
}
for (entity in entities) {
detachEntity(em, entity, context.fetchPlans[entity], true)
}
entityChangedEventManager.publish(events)
}
}
}