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

Unable to locate Attribute with the given name on parametrized supertype #3307

Closed
HVollmilch opened this issue Jan 9, 2024 · 7 comments
Closed
Assignees
Labels
type: enhancement A general enhancement

Comments

@HVollmilch
Copy link

Hi there,

during the update to spring boot 3.x i encountered some issues related to generic types used in entities. I reported a bug in hibernate-core (Jira), which was fixed as part of spring boot 3.2.1 (hibternate 6.4.1) but now my jpa repositories are the next ones, that are not happy with my implementation.

Lets assume we have 2 abstract classes Parent and Child, that know each other

@Data
@MappedSuperclass
public abstract class Parent<C extends Child<?>> {

    @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
    private List<C> children = new LinkedList<>();

    public abstract Long getId();

    public abstract void setId(Long id);

}

@Data
@MappedSuperclass
public abstract class Child<P extends Parent<?>> {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(optional = false)
    private P parent;

}

And we have multiple implementations of them

@Data
@Entity
public class ParentA extends Parent<ChildA> {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

}

@Data
@Entity
public class ParentB extends Parent<ChildB> {

    @Id
    private Long id;

}

@Data
@Entity
public class ChildA extends Child<ParentA> {
}

@Data
@Entity
public class ChildB extends Child<ParentB> {
}

The ids of the parents are not specified in the abstract class cause i need different generation strategies depending on the specific type.

And then we have a repository interface for children, all specific-type-repositories are inheriting:

@NoRepositoryBean
public interface ChildRepository<C extends Child<?>> extends JpaRepository<C, Long> {

    boolean existsByIdAndParentId(Long id, Long parentId);

}

I will work with this ChildRepository in an abstract service.

If i try to execute the "extistsByIdAndParentId" method, i'll get the following exception
Caused by: java.lang.IllegalArgumentException: Unable to locate Attribute with the given name [id] on this ManagedType [com.hibernate.data.core.Parent]

But this kind of inheritance worked in my previous version using spring boot version 2.6.1

I've attached a application of this simplified usecase, where you can recreate my problem
spring-data.zip

Thanks!
Denis

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Jan 9, 2024
@quaff
Copy link
Contributor

quaff commented Jan 10, 2024

It should be fixed by #3275

@mp911de
Copy link
Member

mp911de commented Jan 10, 2024

Hibernate 6 changed how the Meta-model behaves. Creating a join on parent returns an underlying model (Join.getModel()) that represents a singular attribute. Upon inspection of the model, the type is pointing to Child respective Parent. There's no retention of generic types.

You can reproduce the issue via:

CriteriaBuilder cb = entityManager.getCriteriaBuilder();

CriteriaQuery<Object> query = cb.createQuery();
Root<ChildA> from = query.from(ChildA.class);
Join<Object, Object> join = from.join("parent");

Bindable<?> model = join.getModel();

if(model instanceof SingularAttribute
    && ((SingularAttribute<?, ?>) model).getType() instanceof ManagedType mt){
   Class<?> type = mt.getJavaType(); // returns Parent instead of ParentA
   mt.getAttribute("id");
}

As we're receiving invalid types in the first place, I don't see how we can fix the issue without compensating for a Hibernate issue.

@mp911de mp911de added for: external-project For an external project and not something we can fix and removed status: waiting-for-triage An issue we've not yet triaged labels Jan 10, 2024
@mp911de mp911de closed this as completed Jan 10, 2024
@mp911de mp911de changed the title Repository: Query path of generic type Unable to locate Attribute with the given name on parametrized supertype Jan 10, 2024
@HVollmilch
Copy link
Author

Hi there,

i've created a hibernate ticket (HHH-17651) and they say, that the problem is not on their side either - which i can understand, because if i'm doing such things:

CriteriaQuery<Long> query = criteriaBuilder.createQuery(Long.class);

Root<ChildA> root = query.from(ChildA.class);
query.select(root.get("id"));

Join<ChildA, ParentA> join = criteriaBuilder.treat(root.join("parent"), ParentA.class);
query.orderBy(criteriaBuilder.asc(join.get("parentAField")));
entityManager.createQuery(query).getResultList();

it seems to work fine and the exception is coming from the QueryUtils class, which cannot locate the attribute.

But i'm just the man in the middle here, that's trying to get his application working again....

@mp911de mp911de added type: enhancement A general enhancement and removed for: external-project For an external project and not something we can fix labels Jan 23, 2024
@mp911de mp911de reopened this Jan 23, 2024
@mp911de
Copy link
Member

mp911de commented Jan 23, 2024

It's a pity that our code has worked for ages. Now that Hibernate has decided to revise their implementation, existing applications and libraries fall apart because they suddenly require CriteriaBuilder.treat(…) wrapping.

Once we have some time, we try to work around Hibernate's changes and fix yet another Hibernate inconvenience on our side.

@HVollmilch
Copy link
Author

Thank you very much for responding !

To be more specific:
I understand the repository-problem and the lack of information here but i can work around that.
My problem starts, when i try to sort records with the criteria api and hand that query over to a repository. Even though i do a treat join with the correct Parent-Type, it fails when it gets executed - so the information is kind of there, and it doesnt bother me that i have to do a treat join (i had to do this in the past versions aswell, even filter out premade, implicit joins on the generic type) but it seems to be ingored now, when the QueryUtils class tries to resolve the property path - i debugged alot but i'm not too deep in your library so forgive me some wrong assumtions...

And for this case, i dont have a clue how i could do a workaround

Have a good day!
Denis

@HVollmilch
Copy link
Author

HVollmilch commented Feb 16, 2024

Hi there,
some new insights from my side - had some time this morning debugging and maybe some positive news

My problem is coming from QueryUtils.requiresOuterJoin. In the context above (stackwise), all joins/treats are working well, which means, the context is aware of a treat for the parent class and will use the correct type (and i have no problem doing a treat join, which i acutally had to do in past versions).

So what actually seems to be the problem is, that "requiresOuterJoin" tries to locate the "id" attribute in the next path recursion (child.parent.id) and fails with an exception in

Line 836: propertyPathModel = (Bindable<?>) managedType.getAttribute(segment),

because getAttribute will ignore the treat and do a null check afterwards. I suppressed this exception using the debugger, and then, the code would - in my case - work correctly! So maybe it would be enough e.g. using findAttribute without the null-check, instead of getAttribute, since the code following this line, is expecting null values.

I'm aware, that the problem might be a bit more complex, since i'm only working with leafProperties that dont need a further join, so there is no "requiresOuterJoin" decision needed. But maybe this helps implementing some kind of fix... For me, i don't think i can workaround this problem somehow - or do you have any idea?

Thanks!
Denis

quaff added a commit to quaff/spring-data-jpa that referenced this issue Feb 20, 2024
ManagedType may be erased type if the attribute is declared as generic, take Hibernate 6.x for example, exception is thrown like:
```
java.lang.IllegalArgumentException: Unable to locate Attribute with the given name [name] on this ManagedType [java.lang.Object]
	at org.hibernate.metamodel.model.domain.AbstractManagedType.checkNotNull(AbstractManagedType.java:225) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
	at org.hibernate.metamodel.model.domain.AbstractManagedType.getAttribute(AbstractManagedType.java:148) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
	at org.hibernate.metamodel.model.domain.AbstractManagedType.getAttribute(AbstractManagedType.java:43) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]
	at org.springframework.data.jpa.repository.query.QueryUtils.requiresOuterJoin(QueryUtils.java:836) ~[spring-data-jpa-3.2.0.jar:3.2.0]
```

Fix spring-projectsGH-3274
Fix spring-projectsGH-3307
@christophstrobl
Copy link
Member

Resolved via #3375

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

Successfully merging a pull request may close this issue.

5 participants