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

Support hibernate-enhance-maven-plugin plugin #148

Open
hurelhuyag opened this issue Dec 15, 2021 · 8 comments
Open

Support hibernate-enhance-maven-plugin plugin #148

hurelhuyag opened this issue Dec 15, 2021 · 8 comments
Labels
pr-needed Feature request for which PR likely needed (no active development but idea is workable)

Comments

@hurelhuyag
Copy link

hurelhuyag commented Dec 15, 2021

Compile-time class enhancement has advantages. When we use it hibernate does not need to create new classes at runtime. But this module is not working with the hibernate-enhance-maven-plugin. Because this enhancer plugin adds proxy to getters and Jackson serializer calls getter before detecting property state. We should find a way to detect property that is lazily loaded before invoking the getter method.

I have created a minimal project for demonstrating this behaviour.

https://github.com/hurelhuyag/demo-jackson-hibernate5-mininal

@cowtowncoder cowtowncoder added the pr-needed Feature request for which PR likely needed (no active development but idea is workable) label Jun 28, 2022
@cowtowncoder
Copy link
Member

This sounds useful! I don't have time or personal itch to work on this but I hope someone has -- and I will be happy to help improvement(s) merged in once ready, and to help with Jackson side aspects (I am not very Hibernaty savvy).

@hurelhuyag
Copy link
Author

hurelhuyag commented Jan 12, 2023

@cowtowncoder I looked at the source code of this project. Then I found out It didn't use Hibernate's provided Hibernate.isInitialized, Hibernate.isPropertyInitialized methods. Is there a specific reason not to use those methods?

@hurelhuyag
Copy link
Author

I can create PR. But I need to understand how Jackson works.
Can you guide me here?

@hurelhuyag
Copy link
Author

I just created a maven profile to test with hibernate-enhance-maven-plugin
#167

@cowtowncoder
Copy link
Member

@hurelhuyag I did not write this extension module and never used Hibernate so unfortunately I do not know the logic. If I had to guess, I'd suggest that perhaps this method was not available in some (earlier) versions of Hibernate.

@hurelhuyag
Copy link
Author

hurelhuyag commented Jan 14, 2023

After days of digging. I found a fairly simple solution. Unfortunately, This solution can't be applied by Module.

    @JsonFilter("lazyPropertyFilter")
    public interface LazyPropertyFilterMixin {}

    @Bean
    Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
        return builder -> builder
            .mixIn(ManagedEntity.class, LazyPropertyFilterMixin.class)
            .mixIn(HibernateProxy.class, LazyPropertyFilterMixin.class)
            .filters(new SimpleFilterProvider(Map.of("lazyPropertyFilter", new LazyPropertyFilter())));
    }

@cowtowncoder
Copy link
Member

Modules can actually do such registration (although with bit of casting). But requirement to overwrite FilterProvider is bit of a blocker; modules should not do that as it prevents users from providing their own filters.

One possibility would be to add a separate helper module to be registered along with main module. Or helper class.

One request: could you include LazyPropertyFilter implementation along with code sample (I know it's probably part of the test project).

@hurelhuyag
Copy link
Author

My Latest LazyPropertyFilter implementation:

public class LazyPropertyFilter implements PropertyFilter {

    @Override
    public void serializeAsField(Object pojo, JsonGenerator gen, SerializerProvider prov, PropertyWriter writer) throws Exception {
        var initialized = isPropertyInitialized((BeanPropertyWriter) writer, pojo);
        if (initialized) {
            writer.serializeAsField(pojo, gen, prov);
        } else if (!gen.canOmitFields()) { // since 2.3
            writer.serializeAsOmittedField(pojo, gen, prov);
        }
    }

    @Override
    public void serializeAsElement(Object elementValue, JsonGenerator gen, SerializerProvider prov, PropertyWriter writer) {
        throw new RuntimeException("LazyPropertyFilter.serializeAsElement() currently unsupported");
    }

    @SuppressWarnings("deprecation")
    @Override
    public void depositSchemaProperty(PropertyWriter writer, ObjectNode propertiesNode, SerializerProvider provider) throws JsonMappingException {
        writer.depositSchemaProperty(propertiesNode, provider);
    }

    @Override
    public void depositSchemaProperty(PropertyWriter writer, JsonObjectFormatVisitor objectVisitor, SerializerProvider provider) throws JsonMappingException {
        writer.depositSchemaProperty(objectVisitor, provider);
    }

    public static boolean isPropertyInitialized(BeanPropertyWriter prop, Object bean) throws Exception {
        return Hibernate.isPropertyInitialized(bean, prop.getName())
            && isInitialized(prop, bean, ManyToOne.class, ManyToOne::fetch)
            && isInitialized(prop, bean, ElementCollection.class, ElementCollection::fetch)
            && isInitialized(prop, bean, OneToMany.class, OneToMany::fetch)
            && isInitialized(prop, bean, Basic.class, Basic::fetch);
    }

    public static <A extends Annotation> boolean isInitialized(
        BeanPropertyWriter prop, Object bean, Class<A> type, Function<A, FetchType> fetch) throws Exception {
        var ann = prop.getAnnotation(type);
        if (ann == null) {
            return true;
        }
        var fetchType = fetch.apply(ann);
        if (fetchType == FetchType.EAGER) {
            return true;
        }
        var value = prop.get(bean);
        return Hibernate.isInitialized(value);
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pr-needed Feature request for which PR likely needed (no active development but idea is workable)
Projects
None yet
Development

No branches or pull requests

2 participants