spring boot jpa projections
05 Jan 2017I was using Spring JPA REST features in a project recently when I ran into a problem with nested resources in my REST JSON:
Below is my JPA repository:
@RepositoryRestResource(collectionResourceRel = "user", path = "user", excerptProjection = UserInlineFavorites.class)
public interface UserRepository extends CrudRepository<User, String> {
User findByEmailAndPassword(@Param("email") String email, @Param("password") String password);
}
This resulted in the following JSON:
{
"name" : "test",
"email" : "test@gmail.com",
"password" : "test",
"_links" : {
"self" : {
"href" : "http://localhost:8080/user/test"
},
"user" : {
"href" : "http://localhost:8080/user/test@gmail.com",
"templated" : true
},
"favorites" : {
"href" : "http://localhost:8080/user/test@gmail.com/favorites"
}
}
}
The problem with this is the nested favorites entities have to obtained using another REST call which seemed like a waste to me. After digging around for a while, I came across the concept of Projections to obtain fully resolved entities:
Create a projection interface to define the structure of the resolved entity:
package com.kkrishna.gymtime.dao;
import java.util.List;
import org.springframework.data.rest.core.config.Projection;
@Projection(name = "inlineFavorites", types = { User.class })
public interface UserInlineFavorites {
public String getName() ;
public String getEmail();
public String getPassword() ;
public List<Gym> getFavorites() ;
}
Add the below to the Spring configuration to force it to always use the projection:
config.getProjectionConfiguration().addProjection(UserInlineFavorites.class, User.class);
Finally change the repository to add the projection:
@RepositoryRestResource(collectionResourceRel = "user", path = "user", excerptProjection = UserInlineFavorites.class)
public interface UserRepository extends CrudRepository<User, String> {
User findByEmailAndPassword(@Param("email") String email, @Param("password") String password);
}
Now the resulting json is:
{
"name" : "test",
"favorites" : [ {
"latLong" : "33.64_-117.86",
"name" : "Pilates Plus Newport Beach",
"address" : "1220 Bison Ave, Newport Beach, CA 92660"
}, {
"latLong" : "33.65_-117.84",
"name" : "Yoga Shakti Wellness Center",
"address" : "4249 Campus Dr B140, Irvine, CA 92612"
}, {
"latLong" : "33.66_-117.86",
"name" : "Equinox Newport Beach",
"address" : "19540 Jamboree Rd, Irvine, CA 92612"
}, {
"latLong" : "33.63_-117.83",
"name" : "Merage Jewish Community Center of Orange County",
"address" : "1 Federation Way #200, Irvine, CA 92603"
} ],
"email" : "test@gmail.com",
"password" : "mirage2000",
"_links" : {
"self" : {
"href" : "http://localhost:8080/user/test@gmail.com"
},
"user" : {
"href" : "http://localhost:8080/user/test@gmail.com{?projection}",
"templated" : true
},
"favorites" : {
"href" : "http://localhost:8080/user/test@gmail.com/favorites"
}
}
}
The caveat to this is that to retrieve single resources you have to add the projection to the URL:
http://localhost:8080/user/test@gmail.com?projection=inlineFavorites
more discussion on StackOverflow