Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
michiruf opened this issue Jan 9, 2023 · 1 comment

Comments

@michiruf
Copy link

michiruf commented Jan 9, 2023

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.

@j256
Copy link
Owner

j256 commented Jan 10, 2023

Wow. Thanks for the level of detail here. Good idea. I'm sure an edge use case but support for this is very isolated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants