#330 - Added projection sample for how to avoid dedicated SQL result set mappings.
Upgraded to Ingalls snapshots as the sampler needs some tweaks that have not made it into a released version yet.
This commit is contained in:
@@ -11,6 +11,10 @@
|
|||||||
<artifactId>spring-data-jpa-jpa21</artifactId>
|
<artifactId>spring-data-jpa-jpa21</artifactId>
|
||||||
<name>Spring Data JPA - JPA 2.1 specific features</name>
|
<name>Spring Data JPA - JPA 2.1 specific features</name>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<spring-data-releasetrain.version>Ingalls-BUILD-SNAPSHOT</spring-data-releasetrain.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.hibernate</groupId>
|
<groupId>org.hibernate</groupId>
|
||||||
|
|||||||
@@ -21,5 +21,4 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|||||||
* @author Thomas Darimont
|
* @author Thomas Darimont
|
||||||
*/
|
*/
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
class CustomResultSetMappingsConfiguration {
|
class CustomResultSetMappingsConfiguration {}
|
||||||
}
|
|
||||||
|
|||||||
@@ -15,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package example.springdata.jpa.resultsetmappings;
|
package example.springdata.jpa.resultsetmappings;
|
||||||
|
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
@@ -23,39 +25,40 @@ import javax.persistence.ConstructorResult;
|
|||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.GeneratedValue;
|
import javax.persistence.GeneratedValue;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.NamedNativeQueries;
|
||||||
import javax.persistence.NamedNativeQuery;
|
import javax.persistence.NamedNativeQuery;
|
||||||
import javax.persistence.SqlResultSetMapping;
|
import javax.persistence.SqlResultSetMapping;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Thomas Darimont
|
* @author Thomas Darimont
|
||||||
|
* @author Oliver Gierke
|
||||||
*/
|
*/
|
||||||
@Entity
|
@NamedNativeQueries({
|
||||||
@NoArgsConstructor
|
|
||||||
|
// A query using a dedicated SQL result set mapping (see below)
|
||||||
|
@NamedNativeQuery(name = "Subscription.findAllSubscriptionSummaries", //
|
||||||
|
query = "select product_name as productName, count(user_id) as subscriptions from subscription group by product_name order by productName", //
|
||||||
|
resultSetMapping = "subscriptionSummary"),
|
||||||
|
|
||||||
|
// A query using simple projections
|
||||||
|
@NamedNativeQuery(name = "Subscription.findAllSubscriptionProjections", //
|
||||||
|
query = "select product_name as product, count(user_id) as usageCount from subscription group by product_name order by product") })
|
||||||
|
|
||||||
@SqlResultSetMapping( //
|
@SqlResultSetMapping( //
|
||||||
name="subscriptionSummary", //
|
name = "subscriptionSummary", //
|
||||||
classes = @ConstructorResult(
|
classes = @ConstructorResult(targetClass = SubscriptionSummary.class, //
|
||||||
targetClass = SubscriptionSummary.class, //
|
columns = { //
|
||||||
columns={
|
@ColumnResult(name = "productName", type = String.class), //
|
||||||
@ColumnResult(name="productName", type=String.class), //
|
@ColumnResult(name = "subscriptions", type = long.class) //
|
||||||
@ColumnResult(name="subscriptions", type=long.class)
|
}))
|
||||||
}))
|
|
||||||
@NamedNativeQuery(
|
@Entity
|
||||||
name="Subscription.findAllSubscriptionSummaries", //
|
|
||||||
query="select product_name as productName, count(user_id) as subscriptions from subscription group by product_name order by productName", //
|
|
||||||
resultSetMapping = "subscriptionSummary")
|
|
||||||
@Data
|
@Data
|
||||||
|
@NoArgsConstructor(force = true, access = AccessLevel.PRIVATE)
|
||||||
|
@AllArgsConstructor
|
||||||
public class Subscription {
|
public class Subscription {
|
||||||
|
|
||||||
@Id
|
private final @Id @GeneratedValue Long id = null;
|
||||||
@GeneratedValue
|
private String productName;
|
||||||
Long id;
|
private long userId;
|
||||||
|
|
||||||
String productName;
|
|
||||||
|
|
||||||
long userId;
|
|
||||||
|
|
||||||
public Subscription(String productName, long userId) {
|
|
||||||
this.productName = productName;
|
|
||||||
this.userId = userId;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018 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
|
||||||
|
*
|
||||||
|
* http://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 example.springdata.jpa.resultsetmappings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Oliver Gierke
|
||||||
|
*/
|
||||||
|
interface SubscriptionProjection {
|
||||||
|
|
||||||
|
String getProduct();
|
||||||
|
|
||||||
|
long getUsageCount();
|
||||||
|
}
|
||||||
@@ -15,22 +15,35 @@
|
|||||||
*/
|
*/
|
||||||
package example.springdata.jpa.resultsetmappings;
|
package example.springdata.jpa.resultsetmappings;
|
||||||
|
|
||||||
import org.springframework.data.repository.CrudRepository;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.persistence.SqlResultSetMapping;
|
||||||
|
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.CrudRepository;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Thomas Darimont
|
* @author Thomas Darimont
|
||||||
*/
|
*/
|
||||||
public interface SubscriptionRepository extends CrudRepository<Subscription, Long> {
|
public interface SubscriptionRepository extends CrudRepository<Subscription, Long> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an aggregated {@link SubscriptionSummary} by Product.
|
* Returns an aggregated {@link SubscriptionSummary} by Product.
|
||||||
* <p>
|
* <p>
|
||||||
* Note that this example uses a JPA 2.1 Constructor based {@link javax.persistence.SqlResultSetMapping}
|
* Note that this example uses a JPA 2.1 Constructor based {@link javax.persistence.SqlResultSetMapping} in
|
||||||
* in combination with native query defined in {@link Subscription}.
|
* combination with native query defined in {@link Subscription}.
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
List<SubscriptionSummary> findAllSubscriptionSummaries();
|
@Query(nativeQuery = true)
|
||||||
|
List<SubscriptionSummary> findAllSubscriptionSummaries();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an aggregated {@link SubscriptionProjection} using a named native query but does not require a dedicated
|
||||||
|
* {@link SqlResultSetMapping}.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Query(nativeQuery = true)
|
||||||
|
List<SubscriptionProjection> findAllSubscriptionProjections();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import lombok.Value;
|
|||||||
@Value
|
@Value
|
||||||
public class SubscriptionSummary {
|
public class SubscriptionSummary {
|
||||||
|
|
||||||
private final String product;
|
String product;
|
||||||
|
Long usageCount;
|
||||||
private final Long usageCount;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,9 +15,10 @@
|
|||||||
*/
|
*/
|
||||||
package example.springdata.jpa.resultsetmappings;
|
package example.springdata.jpa.resultsetmappings;
|
||||||
|
|
||||||
import static java.util.Arrays.asList;
|
import static java.util.Arrays.*;
|
||||||
import static org.assertj.core.api.Assertions.*;
|
import static org.assertj.core.api.Assertions.*;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -25,8 +26,6 @@ import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
|
|||||||
import org.springframework.test.context.junit4.SpringRunner;
|
import org.springframework.test.context.junit4.SpringRunner;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Thomas Darimont
|
* @author Thomas Darimont
|
||||||
*/
|
*/
|
||||||
@@ -35,26 +34,36 @@ import java.util.List;
|
|||||||
@Transactional
|
@Transactional
|
||||||
public class SubscriptionRepositoryIntegrationTests {
|
public class SubscriptionRepositoryIntegrationTests {
|
||||||
|
|
||||||
private static final String SERVICE_1 = "Service 1";
|
private static final String SERVICE_1 = "Service 1";
|
||||||
private static final String SERVICE_2 = "Service 2";
|
private static final String SERVICE_2 = "Service 2";
|
||||||
|
|
||||||
@Autowired
|
@Autowired SubscriptionRepository repository;
|
||||||
SubscriptionRepository repository;
|
|
||||||
|
|
||||||
@Test
|
@Before
|
||||||
public void shouldReturnCorrectSubscriptionSummary() {
|
public void setUp() {
|
||||||
|
|
||||||
repository.save(new Subscription(SERVICE_1, 1));
|
repository.save(new Subscription(SERVICE_1, 1));
|
||||||
repository.save(new Subscription(SERVICE_1, 2));
|
repository.save(new Subscription(SERVICE_1, 2));
|
||||||
repository.save(new Subscription(SERVICE_1, 3));
|
repository.save(new Subscription(SERVICE_1, 3));
|
||||||
repository.save(new Subscription(SERVICE_2, 3));
|
repository.save(new Subscription(SERVICE_2, 3));
|
||||||
repository.save(new Subscription(SERVICE_2, 4));
|
repository.save(new Subscription(SERVICE_2, 4));
|
||||||
|
}
|
||||||
|
|
||||||
List<SubscriptionSummary> subscriptionSummaries = repository.findAllSubscriptionSummaries();
|
@Test
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void shouldReturnCorrectSubscriptionSummary() {
|
||||||
|
|
||||||
assertThat(subscriptionSummaries) //
|
assertThat(repository.findAllSubscriptionSummaries()) //
|
||||||
.flatExtracting(s -> asList(s.getProduct(), s.getUsageCount()))
|
.flatExtracting(s -> asList(s.getProduct(), s.getUsageCount())) //
|
||||||
.contains(SERVICE_1, 3L, SERVICE_2, 2L);
|
.contains(SERVICE_1, 3L, SERVICE_2, 2L);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void shouldReturnCorrectSubscriptionProjection() {
|
||||||
|
|
||||||
|
assertThat(repository.findAllSubscriptionProjections()) //
|
||||||
|
.flatExtracting(s -> asList(s.getProduct(), s.getUsageCount())) //
|
||||||
|
.contains(SERVICE_1, 3L, SERVICE_2, 2L);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user