Skip to content

Commit 59f2fd3

Browse files
radovanradicdstepanov
authored andcommitted
Fix DTO projection with embedded id in JPA (#2794)
(cherry picked from commit aa4da36)
1 parent a3dbcc4 commit 59f2fd3

File tree

5 files changed

+97
-0
lines changed

5 files changed

+97
-0
lines changed

data-hibernate-jpa/src/test/groovy/io/micronaut/data/hibernate/DtoSpec.groovy

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ import io.micronaut.data.model.Pageable
1919
import io.micronaut.data.tck.entities.AuthorBooksDto
2020
import io.micronaut.data.tck.entities.Book
2121
import io.micronaut.data.tck.entities.BookDto
22+
import io.micronaut.data.tck.entities.Shipment
23+
import io.micronaut.data.tck.entities.ShipmentId
2224
import io.micronaut.test.extensions.spock.annotation.MicronautTest
2325
import org.hibernate.Hibernate
2426
import spock.lang.Shared
@@ -33,10 +35,15 @@ class DtoSpec extends Specification {
3335
@Inject
3436
@Shared
3537
BookRepository bookRepository
38+
3639
@Inject
3740
@Shared
3841
BookDtoRepository bookDtoRepository
3942

43+
@Inject
44+
@Shared
45+
JpaShipmentRepository shipmentRepository
46+
4047
def setup() {
4148
bookRepository.saveAuthorBooks([
4249
new AuthorBooksDto("Stephen King", Arrays.asList(
@@ -95,4 +102,26 @@ class DtoSpec extends Specification {
95102
result.content.every { it.title.startsWith("The")}
96103
}
97104

105+
void "test dto projection with embedded id"() {
106+
given:
107+
def id = new ShipmentId("a", "b")
108+
shipmentRepository.save(new Shipment(id, "test"))
109+
110+
def id2 = new ShipmentId("a", "c")
111+
shipmentRepository.save(new Shipment(id2, "test2"))
112+
113+
def id3 = new ShipmentId("b", "d")
114+
shipmentRepository.save(new Shipment(id3, "test3"))
115+
when:
116+
def shipments = shipmentRepository.findAllByShipmentIdCountry("a")
117+
def shipmentDtos = shipmentRepository.queryAllByShipmentIdCountry("a")
118+
then:
119+
shipmentDtos.size() == 2
120+
shipmentDtos.every{ it.shipmentId()}
121+
shipmentDtos.every{ it.shipmentId().country == "a"}
122+
shipments.size() == 2
123+
cleanup:
124+
shipmentRepository.deleteAll()
125+
}
126+
98127
}

data-hibernate-jpa/src/test/java/io/micronaut/data/hibernate/JpaShipmentRepository.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,11 @@
1818
import io.micronaut.data.annotation.Repository;
1919
import io.micronaut.data.repository.CrudRepository;
2020
import io.micronaut.data.tck.entities.Shipment;
21+
import io.micronaut.data.tck.entities.ShipmentDto;
2122
import io.micronaut.data.tck.entities.ShipmentId;
2223

24+
import java.util.List;
25+
2326
@Repository
2427
public interface JpaShipmentRepository extends CrudRepository<Shipment, ShipmentId> {
2528

@@ -28,4 +31,8 @@ public interface JpaShipmentRepository extends CrudRepository<Shipment, Shipment
2831
Shipment findByShipmentIdCountryAndShipmentIdCity(String country, String city);
2932

3033
long countDistinct();
34+
35+
List<Shipment> findAllByShipmentIdCountry(String country);
36+
37+
List<ShipmentDto> queryAllByShipmentIdCountry(String country);
3138
}

data-model/src/main/java/io/micronaut/data/model/query/builder/jpa/JpaQueryBuilder.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,18 @@ protected void appendCompoundAssociationProjection(QueryState queryState, String
294294
queryString.append(joinAlias).append(AS_CLAUSE).append(alias != null ? alias : association.getName());
295295
}
296296

297+
@Override
298+
protected void appendCompoundPropertyProjection(QueryState queryState, StringBuilder queryString, PersistentProperty property, PersistentPropertyPath propertyPath, String columnAlias) {
299+
if (property instanceof Embedded) {
300+
queryString.append(queryState.getRootAlias()).append(DOT).append(propertyPath.getPath());
301+
if (columnAlias != null) {
302+
queryString.append(AS_CLAUSE).append(columnAlias);
303+
}
304+
return;
305+
}
306+
super.appendCompoundPropertyProjection(queryState, queryString, property, propertyPath, columnAlias);
307+
}
308+
297309
@Override
298310
protected NamingStrategy getNamingStrategy(PersistentEntity entity) {
299311
return JPA_NAMING_STRATEGY;

data-processor/src/test/groovy/io/micronaut/data/processor/visitors/DtoSpec.groovy

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import io.micronaut.data.model.PersistentEntity
2222
import io.micronaut.data.model.entities.Person
2323
import io.micronaut.data.model.query.builder.jpa.JpaQueryBuilder
2424

25+
import static io.micronaut.data.processor.visitors.TestUtils.getQuery
26+
2527
class DtoSpec extends AbstractDataSpec {
2628

2729
void "test build DTO with raw @Query method doesn't fail to compile"() {
@@ -403,4 +405,24 @@ class AuthorDto {
403405
listAllMethod.isTrue(DataMethod, DataMethod.META_MEMBER_DTO)
404406
}
405407

408+
void "test embedded id in DTO"() {
409+
given:
410+
def repository = buildRepository('test.TestRepository', '''
411+
import io.micronaut.data.annotation.Repository;
412+
import io.micronaut.data.repository.GenericRepository;
413+
import io.micronaut.data.tck.entities.Shipment;
414+
import io.micronaut.data.tck.entities.ShipmentDto;
415+
import io.micronaut.data.tck.entities.ShipmentId;
416+
@Repository
417+
interface TestRepository extends GenericRepository<Shipment, ShipmentId> {
418+
List<ShipmentDto> searchByShipmentIdCountry(String country);
419+
}
420+
''')
421+
expect:"The repository to compile"
422+
repository != null
423+
when:
424+
def queryFindByText = getQuery(repository.getRequiredMethod("searchByShipmentIdCountry", String))
425+
then:
426+
queryFindByText == 'SELECT shipment_.shipmentId AS shipmentId,shipment_.field AS field FROM io.micronaut.data.tck.entities.Shipment AS shipment_ WHERE (shipment_.shipmentId.country = :p1)'
427+
}
406428
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright 2017-2024 original authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.micronaut.data.tck.entities;
17+
18+
import io.micronaut.core.annotation.Introspected;
19+
20+
@Introspected
21+
public record ShipmentDto(
22+
23+
ShipmentId shipmentId,
24+
25+
String field
26+
) {
27+
}

0 commit comments

Comments
 (0)