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

Entity literal preferred over path in parsing when entity name and attribute names conflict #1294

Closed
garfieldcoked opened this issue Apr 20, 2021 · 9 comments · Fixed by #1298
Assignees
Milestone

Comments

@garfieldcoked
Copy link

garfieldcoked commented Apr 20, 2021

Description

Hi, I recently discovered Blaze Persistence and so far has been great at solving some issues I was having. I have arrived at a particular issue that I cant seem to find the solution for.
I am trying to use nested view for ManyToOne mapping but gets an error, this is not a bug report but more of a request for help.

Entities

Some fields removed for simplicity

Subject

@Audited
@Entity(name = "curriculum_subject")
public class SubjectImpl implements Subject {

    private String uid;

    @JsonDeserialize(as = SchoolImpl.class)
    @JsonFilter(JsonFilterConstants.UID_ONLY)
    private School school;

    @Id
    @Column(name = "id")
    @GenericGenerator(name  = "uid64", strategy = "com.-.data.util.UID64Generator")
    @GeneratedValue(generator = "uid64")
    public String getUid() {
        return uid;
    }

    public void setUid(String uid) {
        this.uid = uid;
    }

    @ManyToOne(targetEntity = SchoolImpl.class, optional = false, fetch = FetchType.LAZY)
    public School getSchool() {
        return school;
    }

    public void setSchool(School school) {
        this.school = school;
    }
}

School

@Audited
@Entity(name = "school")
@NoArgsConstructor
public class SchoolImpl implements School {

    private String uid;

    private String name;

    @Id
    @Column(name = "id")
    @GenericGenerator(name  = "uid64", strategy = "com.-.data.util.UID64Generator")
    @GeneratedValue(generator = "uid64")
    public String getUid() {
        return uid;
    }

    public void setUid(String uid) {
        this.uid = uid;
    }

    @Column(name = "school_name")
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Views

These are the complete views, nothing removed

Subject

@EntityView(SubjectImpl.class)
public interface SubjectView extends DomainView {
    String getName();
    String getShortName();
    SchoolView getSchool();
}

School

@EntityView(SchoolImpl.class)
public interface SchoolView extends DomainView {
    String getName();
}

Domain

public interface DomainView {
    @IdMapping
    String getUid();
}

Stacktrace

Whenever I try to build I get the following error

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'createEntityViewManager' defined in class path resource [com/-/main/configuration/BlazePersistenceConfig.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.blazebit.persistence.view.EntityViewManager]: Factory method 'createEntityViewManager' threw exception; nested exception is java.lang.IllegalArgumentException: There are error(s) in entity views!
The resolved possible types [javax.persistence.metamodel.EntityType] are not assignable to the given expression type 'com.-.domain.modules.school.entity.impl.SchoolImpl' of the mapping expression declared by the attribute school[com.-.domain.modules.curriculum.view.SubjectView.getSchool]!
	at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:656)
	at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:636)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1338)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1177)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:557)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:226)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
	at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:276)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1306)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1226)
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:640)
	... 95 more
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.blazebit.persistence.view.EntityViewManager]: Factory method 'createEntityViewManager' threw exception; nested exception is java.lang.IllegalArgumentException: There are error(s) in entity views!
The resolved possible types [javax.persistence.metamodel.EntityType] are not assignable to the given expression type 'com.-.domain.modules.school.entity.impl.SchoolImpl' of the mapping expression declared by the attribute school[com.-.domain.modules.curriculum.view.SubjectView.getSchool]!
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185)
	at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651)
	... 108 more
Caused by: java.lang.IllegalArgumentException: There are error(s) in entity views!
The resolved possible types [javax.persistence.metamodel.EntityType] are not assignable to the given expression type 'com.-.domain.modules.school.entity.impl.SchoolImpl' of the mapping expression declared by the attribute school[com.-.domain.modules.curriculum.view.SubjectView.getSchool]!
	at com.blazebit.persistence.view.impl.EntityViewManagerImpl.<init>(EntityViewManagerImpl.java:275)
	at com.blazebit.persistence.view.impl.EntityViewConfigurationImpl.createEntityViewManager(EntityViewConfigurationImpl.java:206)
	at com.-.main.configuration.BlazePersistenceConfig.createEntityViewManager(BlazePersistenceConfig.java:35)
	at com.-.main.configuration.BlazePersistenceConfig$$EnhancerBySpringCGLIB$$76fc933b.CGLIB$createEntityViewManager$0(<generated>)
	at com.-.main.configuration.BlazePersistenceConfig$$EnhancerBySpringCGLIB$$76fc933b$$FastClassBySpringCGLIB$$39a92948.invoke(<generated>)
	at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244)
	at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331)
	at com.-.main.configuration.BlazePersistenceConfig$$EnhancerBySpringCGLIB$$76fc933b.createEntityViewManager(<generated>)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154)
	... 109 more

With the above error while building application I can see the following generated classes

SubjectView_

@Generated(value = "com.blazebit.persistence.view.processor.EntityViewAnnotationProcessor")
@StaticMetamodel(SubjectView.class)
public abstract class SubjectView_ {
    public static volatile MethodSingularAttribute<SubjectView, String> name;
    public static volatile SchoolViewRelation<SubjectView, MethodSingularAttribute<SubjectView, SchoolView>> school;
    public static volatile MethodSingularAttribute<SubjectView, String> shortName;
    public static volatile MethodSingularAttribute<SubjectView, String> uid;

    public static final String NAME = "name";
    public static final String SCHOOL = "school";
    public static final String SHORT_NAME = "shortName";
    public static final String UID = "uid";
    public static final String SCHOOLNAME = "school.name";
    public static final String SCHOOLUID = "school.uid";

    public static EntityViewSetting<SubjectView, CriteriaBuilder<SubjectView>> createSettingInit() {
        return EntityViewSetting.create(SubjectView.class, "init");
    }
    public static EntityViewSetting<SubjectView, PaginatedCriteriaBuilder<SubjectView>> createPaginatedSettingInit(int firstResult, int maxResults) {
        return EntityViewSetting.create(SubjectView.class, firstResult, maxResults, "init");
    }
}

SubjectViewImpl

@Generated(value = "com.blazebit.persistence.view.processor.EntityViewAnnotationProcessor")
@StaticImplementation(SubjectView.class)
public class SubjectViewImpl implements SubjectView, EntityViewProxy {

    public static volatile EntityViewManager ENTITY_VIEW_MANAGER;
    public static final SerializableEntityViewManager SERIALIZABLE_ENTITY_VIEW_MANAGER = new SerializableEntityViewManager(SubjectViewImpl.class, ENTITY_VIEW_MANAGER);

    private final String name;
    private final SchoolView school;
    private final String shortName;
    private final String uid;

    public SubjectViewImpl(SubjectViewImpl noop, Map<String, Object> optionalParameters) {
        this.name = null;
        this.school = null;
        this.shortName = null;
        this.uid = null;
    }

    public SubjectViewImpl(        String uid) {
        this.$$_kind = (byte) 1;
        this.name = null;
        this.school = null;
        this.shortName = null;
        this.uid = uid;
    }

    public SubjectViewImpl(
        String uid,
        String name,
        SchoolView school,
        String shortName
    ) {
        super();
        this.name = name;
        this.school = school;
        this.shortName = shortName;
        this.uid = uid;
    }

    public SubjectViewImpl(SubjectViewImpl noop, int offset, Object[] tuple) {
        super();
        this.name = (String) tuple[offset + 1];
        this.school = (SchoolView) tuple[offset + 2];
        this.shortName = (String) tuple[offset + 3];
        this.uid = (String) tuple[offset + 0];
    }

    public SubjectViewImpl(SubjectViewImpl noop, int offset, int[] assignment, Object[] tuple) {
        super();
        this.name = (String) tuple[offset + assignment[1]];
        this.school = (SchoolView) tuple[offset + assignment[2]];
        this.shortName = (String) tuple[offset + assignment[3]];
        this.uid = (String) tuple[offset + assignment[0]];
    }


    @Override
    public String getName() {
        return name;
    }
    @Override
    public SchoolView getSchool() {
        return school;
    }
    @Override
    public String getShortName() {
        return shortName;
    }
    @Override
    public String getUid() {
        return uid;
    }
...

Environment

Version: 1.5.1
JPA-Provider: Hibernate 5.4.15.Final
DBMS: Postgres 11
Application Server: Java EE with Spring Data

If I remove getSchool() from the SubjectView the error goes away. Any idea what I am doing wrong or missing. I am still going through the documentations and appreciate any pointers that could help in my troubles. Hopefully the information provided is enough, thank you.

@beikov
Copy link
Member

beikov commented Apr 20, 2021

Hi,

this could be an issue with the targetEntity configuration in @ManyToOne(targetEntity = SchoolImpl.class). We actually have tests for these kinds of mappings, so I am a bit surprised that this doesn't work in your case. Do you think you could create a reproducible test case with this test case template? https://github.com/Blazebit/blaze-persistence-test-case-template

@garfieldcoked
Copy link
Author

Hi @beikov, thank you for the quick response. I will create one and get back to you.
Thanks

@beikov
Copy link
Member

beikov commented Apr 22, 2021

The problem is that the entity name of SchoolImpl is school and the attribute name in SubjectImpl is also called school. The parser treats the string school as entity literal rather than path expression. I'll work on a fix for that, but for now, you would have to rename the attribute or the entity names. I would suggest you use upper case entity names (the default) to avoid this.

@beikov beikov added component: core kind: bug worth: medium Implementing this has a medium worth and removed question labels Apr 22, 2021
@beikov beikov added this to the 1.6.0 milestone Apr 22, 2021
@beikov beikov self-assigned this Apr 22, 2021
@beikov beikov changed the title Not assignable to the given expression type error Entity literal preferred over path in parsing when entity name and attribute names conflict Apr 22, 2021
@beikov
Copy link
Member

beikov commented Apr 22, 2021

You could workaround the type checking issue by using the VIEW macro, which will qualify the path:

@EntityView(SubjectImpl.class)
public interface SubjectView extends DomainView {
    String getName();
    String getShortName();
    @Mapping("VIEW(school)")
    SchoolView getSchool();
}

beikov added a commit to beikov/blaze-persistence that referenced this issue Apr 22, 2021
beikov added a commit to beikov/blaze-persistence that referenced this issue Apr 22, 2021
beikov added a commit to beikov/blaze-persistence that referenced this issue Apr 22, 2021
@garfieldcoked
Copy link
Author

@beikov, thank you for this feedback. I was troubleshooting and arrived at the same conclusion that it was being treated as a literal (even though I did not know what that meant :D)

Screenshot 2021-04-21 at 23 15 17

I dug a bit further into why it was being treated as a literal and found that the delate of instance ExpressionFactoryImpl had a list of entity types (I assumed this fetched from the EntityManager used for CriteriaBuilderFactory) had some instances of my entities twice; 1 with the fully qualified name and another with the just the entity name. I also don't the reason for this either

Screenshot 2021-04-23 at 08 33 25

_This screenshot represents some different entities but wanted to show what this map look like while creating the CriteriaBuilderFactory_

After seeing this I made a change to my SchoolImpl, which removed the startup error as the duplicate within the Type resolver was gone

From:

@Audited
@Entity(name = "school")
public class SchoolImpl implements School {

To:

@Audited
@Entity(name = "com.myschool.sms.domain.modules.school.entity.impl.SchoolImpl")
@Table(name = "school")
public class SchoolImpl implements School {

At this point I assumed it was an issue with my setup but I will also try your suggestions to see the effects.

@beikov
Copy link
Member

beikov commented Apr 23, 2021

So in JPQL/HQL there are two ways to refer to an entity. Through the FQCN and the entity name. The name you specify in @Entity(name = "") is the entity name. This is mostly important for doing type checks e.g. WHERE TYPE(e) = School in polymorphic scenarios. Anyway, usually, people use upper case entity names and if you omit the name, it will use the simple class name as entity name by default i.e. SchoolImpl.

@garfieldcoked
Copy link
Author

Confirming that changing to uppercase also removed the error

@garfieldcoked
Copy link
Author

@beikov, thanks again for your help with this...really appreciate it! And thanks for a great library, wish I found it sooner!

@beikov
Copy link
Member

beikov commented Apr 23, 2021

Spread the word ;)

beikov added a commit to beikov/blaze-persistence that referenced this issue Nov 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants