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

Add ability to automatically use Micronaut's JsonMapper abstraction for Hibernate's FormatMapper for JSON #2908

Open
graemerocher opened this issue Apr 24, 2024 · 2 comments
Labels
type: enhancement New feature or request

Comments

@graemerocher
Copy link
Contributor

graemerocher commented Apr 24, 2024

Feature description

We should be able to allow Hibernate to use the managed JSON mapper from Hibernate, by default it autodetects either Jackson or JSON-B and instantiates it. In the case of Jackson it calls findModules which results in a classpath scan that sucks for performance.

Here is some code that does it (needs tests and cleanup):

package hibernate.test;

import io.micronaut.configuration.hibernate.jpa.JpaConfiguration;
import io.micronaut.context.event.BeanCreatedEvent;
import io.micronaut.context.event.BeanCreatedEventListener;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.type.Argument;
import io.micronaut.json.JsonMapper;
import jakarta.inject.Singleton;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.format.FormatMapper;

@Singleton
public class JpaJsonConfigurer implements BeanCreatedEventListener<JpaConfiguration> {
    private final JsonMapper jsonMapper;

    public JpaJsonConfigurer(JsonMapper jsonMapper) {
        this.jsonMapper = jsonMapper;
    }

    @Override
    public JpaConfiguration onCreated(@NonNull BeanCreatedEvent<JpaConfiguration> event) {
        JpaConfiguration configuration = event.getBean();
        Map<String, Object> properties = configuration.getProperties();
        if (properties == null) {
            properties = new LinkedHashMap<>();
        }
        configuration.setProperties(properties);
        properties.put(AvailableSettings.JSON_FORMAT_MAPPER, new FormatMapper() {
            @Override
            public <T> T fromString(CharSequence charSequence, JavaType<T> javaType, WrapperOptions wrapperOptions) {
                if ( javaType.getJavaType() == String.class || javaType.getJavaType() == Object.class ) {
                    return (T) charSequence.toString();
                }
                try {
                    return (T) jsonMapper.readValue( charSequence.toString(), Argument.of(javaType.getJavaType()));
                }
                catch (IOException e) {
                    throw new IllegalArgumentException( "Could not deserialize string to java type: " + javaType, e );
                }
            }

            @Override
            public <T> String toString(T value, JavaType<T> javaType, WrapperOptions wrapperOptions) {
                if ( javaType.getJavaType() == String.class || javaType.getJavaType() == Object.class ) {
                    return (String) value;
                }
                try {
                    return jsonMapper.writeValueAsString((Argument<T>) Argument.of(javaType.getJavaType()), value);
                }
                catch (IOException e) {
                    throw new IllegalArgumentException( "Could not serialize object of java type: " + javaType, e );
                }
            }
        });
        return configuration;
    }
}
@graemerocher graemerocher added the type: enhancement New feature or request label Apr 24, 2024
@rorueda
Copy link

rorueda commented May 7, 2024

When I needed this, I found the SettingsSupplier interface. It makes it a bit cleaner, I think.

@Prototype
public class HibernateJsonFormatMapperSettingsSupplier implements SettingsSupplier {
  private final SerdeJsonFormatMapper serdeJsonFormatMapper;

  public HibernateJsonFormatMapperSettingsSupplier(SerdeJsonFormatMapper serdeJsonFormatMapper) {
    this.serdeJsonFormatMapper = serdeJsonFormatMapper;
  }

  @Override
  public Map<String, Object> supply(JpaConfiguration jpaConfiguration) {
    return Collections.singletonMap(AvailableSettings.JSON_FORMAT_MAPPER, serdeJsonFormatMapper);
  }

  @Override
  public int getOrder() {
    return SettingsSupplier.HIGHEST_PRECEDENCE;
  }
}

The SerdeJsonFormatMapper is just a FormatMapper.

@graemerocher
Copy link
Contributor Author

Much cleaner than my implementation! 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: enhancement New feature or request
Projects
No open projects
Status: No status
Development

No branches or pull requests

2 participants