Skip to content

If using different configurable database types, the DatabaseField's columnDefinition is very static #279

@michiruf

Description

@michiruf

I tried to setup ORMLite with different kinds of databases, where some huge JSON strings should get persisted into.
When using MySQL, the solution to those long strings (for me) was to set the columnDefinition to LONGTEXT, where as for Postgres, this value LONGTEXT is invalid. This is of cause just a very special edge case, however, there may exist alot of other use cases, where it is necessary to overwrite the DatabaseField annotation values for different database types, e.g. if one needs to specify a different persister/datatype.

Maybe there should be a built in way, to overwrite the DatabaseField annotation data, as well as the one from javax.persistance equivalents for different types of database?

Because I needed this feature in my project, here is my own solution for others having the same need:

Example usage

    @DatabaseField
    @DatabaseTypeSpecificDatabaseField({
            @DatabaseTypeSpecificOverload(
                    typeName = "MySQL",
                    databaseField = @DatabaseField(columnDefinition = "LONGTEXT")
            )
    })
    public JsonElement veryLargeData = new JsonObject();

Annotations

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DatabaseTypeSpecificDatabaseField {

    DatabaseTypeSpecificOverload[] value();
}

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DatabaseTypeSpecificOverload {

    String typeName();

    DatabaseField databaseField();
}

Custom config "processor"

public class OverloadableDatabaseTableConfig {

    /**
     * @see DatabaseTableConfig#fromClass(DatabaseType, Class)
     */
    public static <T> DatabaseTableConfig<T> fromClass(DatabaseType databaseType, Class<T> clazz) throws SQLException {
        var tableName = DatabaseTableConfig.extractTableName(databaseType, clazz);
        return new DatabaseTableConfig<>(databaseType, clazz, collectDatabaseFieldConfigs(databaseType, clazz, tableName));
    }

    /**
     * @see DatabaseTableConfig#extractFieldTypes(DatabaseType, Class, String)
     * @see DatabaseFieldConfig#fromDatabaseField(DatabaseType, String, Field, DatabaseField)
     */
    private static <T> List<DatabaseFieldConfig> collectDatabaseFieldConfigs(DatabaseType databaseType, Class<T> clazz, String tableName) throws SQLException {
        var fieldConfigs = new ArrayList<DatabaseFieldConfig>();

        for (Class<?> classWalk = clazz; classWalk != null; classWalk = classWalk.getSuperclass()) {
            Field[] fields = classWalk.getDeclaredFields();
            for (Field field : fields) {
                var specificFieldConfig = getDatabaseFieldConfigFromSpecificDatabaseFieldAnnotation(databaseType, field, tableName);
                if (specificFieldConfig != null) {
                    fieldConfigs.add(specificFieldConfig);
                    continue;
                }

                var fieldConfig = DatabaseFieldConfig.fromField(databaseType, tableName, field);
                if (fieldConfig != null) {
                    fieldConfigs.add(fieldConfig);
                }
            }
        }

        if (fieldConfigs.isEmpty()) {
            throw new IllegalArgumentException("No fields have a " + DatabaseField.class.getSimpleName() + " annotation in " + clazz);
        }

        return fieldConfigs;
    }

    private static DatabaseFieldConfig getDatabaseFieldConfigFromSpecificDatabaseFieldAnnotation(DatabaseType databaseType, Field field, String tableName) {
        var databaseSpecificField = field.getAnnotation(DatabaseTypeSpecificDatabaseField.class);
        if (databaseSpecificField != null) {
            for (DatabaseTypeSpecificOverload databaseTypeSpecificOverload : databaseSpecificField.value()) {
                if (databaseTypeSpecificOverload.typeName().equals(databaseType.getDatabaseName())) {
                    return DatabaseFieldConfig.fromDatabaseField(databaseType, tableName, field, databaseTypeSpecificOverload.databaseField());
                }
            }
        }

        return null;
    }
}

Example setup

    var myEntitiyConfig = OverloadableDatabaseTableConfig.fromClass(
            connection.getDatabaseType(), MyEntity.class);
    TableUtils.createTableIfNotExists(connection, myEntitiyConfig);
    myEntityDao = DaoManager.createDao(connection, myEntitiyConfig);

Note that its needed to may call methods of ORMLite always with this configuration, since the configuration may get cached. Figured out because I tried to call TableUtils.dropTable(conn, MyEntity.class) before using OverloadableDatabaseTableConfig.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions