public static <R extends UpdatableRecord<R>, T1, T2, T3> R tryCreate(final DSLContext context,
                                                                         final R record,
                                                                         final TableField<R, T1> keyField1,
                                                                         final TableField<R, T2> keyField2,
                                                                         final TableField<R, T3> keyField3,
                                                                         final Consumer<R> onCreateAction) {
        R persistedRecord;
        LOGGER.debug(() -> "Creating a " + record.getTable() + " record if it doesn't already exist:\n" + record);
        record.attach(context.configuration());
        try {
            // Attempt to write the record, which may already be there
            record.insert();
            persistedRecord = record;
            if (onCreateAction != null) {
                onCreateAction.accept(persistedRecord);
            }
        } catch (RuntimeException e) {
            if (isDuplicateKeyException(e)) {
                LOGGER.debug(e::getMessage, e);

                // In theory, we could get the unique key fields from record.getTable().getKeys()
                // but this is a bit fragile if the table has multiple unique keys, so better to let
                // the caller make the decision as to which fields to use
                final List<Condition> conditionList = new ArrayList<>();
                // For now support up to three fields in a compound key
                if (keyField1 != null) {
                    conditionList.add(keyField1.eq(record.get(keyField1)));
                }
                if (keyField2 != null) {
                    conditionList.add(keyField2.eq(record.get(keyField2)));
                }
                if (keyField3 != null) {
                    conditionList.add(keyField3.eq(record.get(keyField3)));
                }
                if (conditionList.isEmpty()) {
                    throw new RuntimeException("You must supplied at least one key field so the persisted record can " +
                                               "be retrieved.");
                }

                final Table<R> table = record.getTable();
                LOGGER.debug("Re-fetching existing record from {} with conditions: {}", table, conditionList);
                // Now need to re-fetch the record using the key fields so we have the full record with ids
                persistedRecord = context.selectFrom(table)
                        .where(conditionList.toArray(new Condition[0]))
                        .fetchOne();
            } else {
                // Some other error so just re-throw
                LOGGER.error(e::getMessage, e);
                throw e;
            }
        }
        return persistedRecord;
    }
<p>
Tries to insert record into the database. If it doesn't already exist it will insert the
record and return the record updated with any auto-generated columns (e.g. ID).
If it already exists then the unique key violation will be swallowed and the keyField arguments
will be used to retrieve the existing record for return. The keyField arguments are one to three columns
that make up a unique key.
</p>
<p>
If you don't need the persisted state from the database then use
{@link JooqUtil#tryCreate(DSLContext, UpdatableRecord)} instead.
</p>

@param onCreateAction Called if the record does not already exist and is actually inserted.
@return The record containing the latest state in the database.
stroom-db-util/src/main/java/stroom/db/util/JooqUtil.java