Fallback to Path.getModel() if ManagedType information was erased.

ManagedType information may be erased if the attribute is declared as generic - With Hibernate 6.x for example an IllegalArgumentException is thrown.

java.lang.IllegalArgumentException: Unable to locate Attribute with the given name [name] on this ManagedType [java.lang.Object]
	at o.h.m.model.domain.AbstractManagedType.checkNotNull(AbstractManagedType.java:225) ~[hibernate-core-6.3.1.Final.jar:6.3.1.Final]

Resolves: #3274
Original Pull Request: #3375
This commit is contained in:
Yanming Zhou
2023-12-18 10:49:54 +08:00
committed by Christoph Strobl
parent c78483ed8d
commit 48e7b62579
9 changed files with 256 additions and 1 deletions

View File

@@ -92,6 +92,7 @@ import org.springframework.util.StringUtils;
* @author Donghun Shin
* @author Pranav HS
* @author Eduard Dudar
* @author Yanming Zhou
*/
public abstract class QueryUtils {
@@ -844,7 +845,15 @@ public abstract class QueryUtils {
managedType = (ManagedType<?>) ((SingularAttribute<?, ?>) model).getType();
}
if (managedType != null) {
propertyPathModel = (Bindable<?>) managedType.getAttribute(segment);
try {
propertyPathModel = (Bindable<?>) managedType.getAttribute(segment);
} catch (IllegalArgumentException ex) {
// ManagedType may be erased type for some vendor if the attribute is declared as generic
// see: https://hibernate.atlassian.net/browse/HHH-16144
// see: https://github.com/hibernate/hibernate-orm/pull/7630
// see: https://github.com/jakartaee/persistence/issues/562
propertyPathModel = from.get(segment).getModel();
}
} else {
propertyPathModel = from.get(segment).getModel();
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2008-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.jpa.domain.sample;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import org.springframework.data.jpa.domain.AbstractPersistable;
/**
* @author Yanming Zhou
*/
@Entity
public class Book extends OwnerContainer<Owner> {
@Id
@GeneratedValue
private Long id;
public Long getId() {
return id;
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2008-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.jpa.domain.sample;
import jakarta.persistence.Entity;
import org.springframework.data.jpa.domain.AbstractPersistable;
/**
* @author Yanming Zhou
*/
@Entity
public class Owner extends AbstractPersistable<Long> {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2008-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.jpa.domain.sample;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.MappedSuperclass;
/**
* @author Yanming Zhou
*/
@MappedSuperclass
public class OwnerContainer<T> {
@ManyToOne
T owner;
public T getOwner() {
return owner;
}
public void setOwner(T owner) {
this.owner = owner;
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2014-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.jpa.repository.generics;
import jakarta.persistence.EntityManager;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.sample.Book;
import org.springframework.data.jpa.domain.sample.Owner;
import org.springframework.data.jpa.repository.sample.BookRepository;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.transaction.annotation.Transactional;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Yanming Zhou
*/
@Transactional
@ExtendWith(SpringExtension.class)
@ContextConfiguration({ "classpath:eclipselink.xml", "classpath:config/namespace-application-context.xml" })
class EclipseLinkGenericsIntegrationTests extends GenericsIntegrationTests {
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright 2014-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.jpa.repository.generics;
import jakarta.persistence.EntityManager;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.AbstractPersistable;
import org.springframework.data.jpa.domain.sample.Book;
import org.springframework.data.jpa.domain.sample.CustomAbstractPersistable;
import org.springframework.data.jpa.domain.sample.Owner;
import org.springframework.data.jpa.repository.sample.BookRepository;
import org.springframework.data.jpa.repository.sample.CustomAbstractPersistableRepository;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.transaction.annotation.Transactional;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Yanming Zhou
*/
@Transactional
@ExtendWith(SpringExtension.class)
@ContextConfiguration(locations = { "classpath:config/namespace-autoconfig-context.xml" })
class GenericsIntegrationTests {
@Autowired
BookRepository repository;
@Autowired
EntityManager entityManager;
@BeforeEach
void setUp() {
Owner owner = new Owner();
owner.setName("owner");
entityManager.persist(owner);
Book book = new Book();
book.setOwner(owner);
entityManager.persist(book);
}
@Test
void findAllByGenericAssociationProperty() {
assertThat(repository.findAllByOwnerName("owner")).hasSize(1);
}
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2014-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.jpa.repository.sample;
import org.springframework.data.jpa.domain.sample.Book;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
/**
* @author Yanming Zhou
*/
public interface BookRepository extends JpaRepository<Book, Long> {
List<Book> findAllByOwnerName(String ownerName);
}

View File

@@ -12,6 +12,7 @@
<class>org.springframework.data.jpa.domain.sample.AuditableUser</class>
<class>org.springframework.data.jpa.domain.sample.AuditableEntity</class>
<class>org.springframework.data.jpa.domain.sample.AuditableEmbeddable</class>
<class>org.springframework.data.jpa.domain.sample.Book</class>
<class>org.springframework.data.jpa.domain.sample.Category</class>
<class>org.springframework.data.jpa.domain.sample.Child</class>
<class>org.springframework.data.jpa.domain.sample.ConcreteType1</class>
@@ -33,6 +34,7 @@
<class>org.springframework.data.jpa.domain.sample.MailSender</class>
<class>org.springframework.data.jpa.domain.sample.MailUser</class>
<class>org.springframework.data.jpa.domain.sample.Order</class>
<class>org.springframework.data.jpa.domain.sample.Owner</class>
<class>org.springframework.data.jpa.domain.sample.Parent</class>
<class>org.springframework.data.jpa.domain.sample.PersistableWithIdClass</class>
<class>org.springframework.data.jpa.domain.sample.PersistableWithSingleIdClass

View File

@@ -8,6 +8,7 @@
<class>org.springframework.data.jpa.domain.sample.AuditableUser</class>
<class>org.springframework.data.jpa.domain.sample.AuditableEntity</class>
<class>org.springframework.data.jpa.domain.sample.AuditableEmbeddable</class>
<class>org.springframework.data.jpa.domain.sample.Book</class>
<class>org.springframework.data.jpa.domain.sample.Category</class>
<class>org.springframework.data.jpa.domain.sample.CustomAbstractPersistable</class>
<class>org.springframework.data.jpa.domain.sample.EntityWithAssignedId</class>
@@ -20,6 +21,7 @@
<class>org.springframework.data.jpa.domain.sample.Role</class>
<class>org.springframework.data.jpa.domain.sample.Site</class>
<class>org.springframework.data.jpa.domain.sample.SpecialUser</class>
<class>org.springframework.data.jpa.domain.sample.Owner</class>
<class>org.springframework.data.jpa.domain.sample.User</class>
<class>org.springframework.data.jpa.domain.sample.Dummy</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>