diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java index 7a4fed2ea8..e29ef48886 100644 --- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java +++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java @@ -92,6 +92,7 @@ * @author Donghun Shin * @author Pranav HS * @author Eduard Dudar + * @author Yanming Zhou */ public abstract class QueryUtils { @@ -844,7 +845,15 @@ private static boolean requiresOuterJoin(From from, PropertyPath property, 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(); } diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Book.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Book.java new file mode 100644 index 0000000000..66ff3e375a --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Book.java @@ -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 { + + @Id + @GeneratedValue + private Long id; + + public Long getId() { + return id; + } +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Owner.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Owner.java new file mode 100644 index 0000000000..7661f038a1 --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/Owner.java @@ -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 { + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/OwnerContainer.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/OwnerContainer.java new file mode 100644 index 0000000000..177b8b48ba --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/domain/sample/OwnerContainer.java @@ -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 { + + @ManyToOne + T owner; + + public T getOwner() { + return owner; + } + + public void setOwner(T owner) { + this.owner = owner; + } +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/generics/EclipseLinkGenericsIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/generics/EclipseLinkGenericsIntegrationTests.java new file mode 100644 index 0000000000..ed5a466545 --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/generics/EclipseLinkGenericsIntegrationTests.java @@ -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 { + +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/generics/GenericsIntegrationTests.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/generics/GenericsIntegrationTests.java new file mode 100644 index 0000000000..d24f328273 --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/generics/GenericsIntegrationTests.java @@ -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); + } + +} diff --git a/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/BookRepository.java b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/BookRepository.java new file mode 100644 index 0000000000..dd4af2f2d6 --- /dev/null +++ b/spring-data-jpa/src/test/java/org/springframework/data/jpa/repository/sample/BookRepository.java @@ -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 { + + List findAllByOwnerName(String ownerName); +} diff --git a/spring-data-jpa/src/test/resources/META-INF/persistence.xml b/spring-data-jpa/src/test/resources/META-INF/persistence.xml index c5e0e90b06..1c3be472e0 100644 --- a/spring-data-jpa/src/test/resources/META-INF/persistence.xml +++ b/spring-data-jpa/src/test/resources/META-INF/persistence.xml @@ -12,6 +12,7 @@ org.springframework.data.jpa.domain.sample.AuditableUser org.springframework.data.jpa.domain.sample.AuditableEntity org.springframework.data.jpa.domain.sample.AuditableEmbeddable + org.springframework.data.jpa.domain.sample.Book org.springframework.data.jpa.domain.sample.Category org.springframework.data.jpa.domain.sample.Child org.springframework.data.jpa.domain.sample.ConcreteType1 @@ -33,6 +34,7 @@ org.springframework.data.jpa.domain.sample.MailSender org.springframework.data.jpa.domain.sample.MailUser org.springframework.data.jpa.domain.sample.Order + org.springframework.data.jpa.domain.sample.Owner org.springframework.data.jpa.domain.sample.Parent org.springframework.data.jpa.domain.sample.PersistableWithIdClass org.springframework.data.jpa.domain.sample.PersistableWithSingleIdClass diff --git a/spring-data-jpa/src/test/resources/META-INF/persistence2.xml b/spring-data-jpa/src/test/resources/META-INF/persistence2.xml index 683457aac8..f4f7adb6b2 100644 --- a/spring-data-jpa/src/test/resources/META-INF/persistence2.xml +++ b/spring-data-jpa/src/test/resources/META-INF/persistence2.xml @@ -8,6 +8,7 @@ org.springframework.data.jpa.domain.sample.AuditableUser org.springframework.data.jpa.domain.sample.AuditableEntity org.springframework.data.jpa.domain.sample.AuditableEmbeddable + org.springframework.data.jpa.domain.sample.Book org.springframework.data.jpa.domain.sample.Category org.springframework.data.jpa.domain.sample.CustomAbstractPersistable org.springframework.data.jpa.domain.sample.EntityWithAssignedId @@ -20,6 +21,7 @@ org.springframework.data.jpa.domain.sample.Role org.springframework.data.jpa.domain.sample.Site org.springframework.data.jpa.domain.sample.SpecialUser + org.springframework.data.jpa.domain.sample.Owner org.springframework.data.jpa.domain.sample.User org.springframework.data.jpa.domain.sample.Dummy true