Projections in Mongodb not working as intended - java

By following the official tutorial for projections in spring data mongodb https://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#projections will get an
java.lang.IllegalArgumentException: Couldn't find PersistentEntity for
type class com.sun.proxy.$Proxy109!
for the NamesOnly Projection:
interface NamesOnly {
String getFirstname();
String getLastname();
}
#RepositoryRestResource
interface PersonRepository extends Repository<Person, UUID> {
Collection<NamesOnly> findByLastname(#Param("lastName") String lastname);
}
Can one get this example to work?

You need to define a #RestController class and call the findByLastname repository method from the controller, like:
#RestController
#RequestMapping("/api")
public class PersonController {
#Autowired
private PersonRepository personRepository;
#GetMapping(path = "/persons/findByLastname")
public Collection<NamesOnly> findByLastname(#Param("lastName") final String lastName) {
Collection<NamesOnly> result = personRepository.findByLastname(lastName);
return result;
}
}

Related

How to expose custom methods for all repositories in Spring Data REST?

I need to add a query endpoint to all of my Spring Data REST repositories. Something like this:
/api/users/query?query=...
/api/issues/query?query=...
/api/projects/query?query=...
...
or
/api/users/search/query?query=...
/api/issues/search/query?query=...
/api/projects/search/query?query=...
...
The URL format doesn't matter.
I implemented a custom base repository:
#NoRepositoryBean
public interface QueryableRepository<T, ID> extends JpaRepository<T, ID> {
Page<T> findAllByQuery(String query, Pageable pageable);
}
public class CustomRepository<T, ID> extends SimpleJpaRepository<T, ID> implements QueryableRepository<T, ID> {
public CustomRepository(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) {
super(entityInformation, entityManager);
}
#Override
public Page<T> findAllByQuery(String query, Pageable pageable) {
return findAll(pageable); // Some omitted implementation here
}
}
#EnableJpaRepositories(repositoryBaseClass = CustomRepository.class)
For sure findAllByQuery method is not exposed by Spring Data REST:
https://stackoverflow.com/a/21502510/632199
https://stackoverflow.com/a/25217113/632199
I can implement a controller for each entity type exposing such a method:
#RepositoryRestController
public class UserController {
private final UserRepository userRepository;
public UserController(UserRepository userRepository) {
this.userRepository = userRepository;
}
#GetMapping(path = "/users/search/query")
public ResponseEntity<CollectionModel<PersistentEntityResource>> findAllByQuery(
String query,
Pageable pageable,
PagedResourcesAssembler<Object> pagedAssembler,
PersistentEntityResourceAssembler resourceAssembler) {
return ResponseEntity.ok(pagedAssembler.toModel(
userRepository.findAllByQuery(value, pageable).map(Object.class::cast),
resourceAssembler));
}
}
But is it possible to add this method to all entities once, without creation of dosens of same controllers?
The following works for me:
#RepositoryRestController
public class QueryController {
private final ApplicationContext context;
public QueryController(ApplicationContext context) {
this.context = context;
}
#GetMapping(value = "/{repository}/query")
public ResponseEntity<Object> findAllByQuery(
RootResourceInformation resourceInformation,
String query, Pageable pageable,
PagedResourcesAssembler<Object> pagedAssembler,
PersistentEntityResourceAssembler resourceAssembler) {
Repositories repositories = new Repositories(context);
Optional<QueryableRepository<?, ?>> repository = repositories
.getRepositoryFor(resourceInformation.getDomainType())
.filter(QueryableRepository.class::isInstance)
.map(QueryableRepository.class::cast);
if (repository.isPresent()) {
return ResponseEntity.ok(pagedAssembler.toModel(
repository.get()
.findAllByQuery(query, pageable)
.map(Object.class::cast),
resourceAssembler));
} else {
return ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED).build();
}
}
}

Methods in service layer are recognized as static

I built a Spring Boot application and added several methods in the service layer. Then I autowired their class into the Controller. IDEA shows an error that
Non-static method 'findAll()' cannot be referenced from a static
context.
#Autowired
public UserMapper Usermanager;
public List<UserEntity> findAll() {
List<UserEntity> list = Usermanager.findALL();
return list;
}
public List<UserEntity> findByName() {
List<UserEntity> list = Usermanager.findByName();
return list;
}
Static value is belong to the class instead of the object. For this reason if we use a static value NPE(NonePointerException) will happen.
The reason of this problem is that I invoked the class instead of instance object. All methods in class is static, they are absolutely not able to be invoke. There are codes in my controller.
#Autowired
UserService userService;
#Autowired
UserMapper userMapper;
//查找
#GetMapping("/findall")
public List<UserEntity> findAll() {
//used to be:return UserService.findAll();
return userService.findAll();
}
#GetMapping("/find/{name}")
public List<UserEntity> findByName(#PathVariable String name) {
//used to be:return UserService.findByName();
return userService.findByName();
}

How to assign a #Projection to a #GetMapping spring servlet endpoint?

I have an #Entity Person class, and want to expose that via a webservice. There should be a method just exposing all details, and and endpoint only exposing a view excerpt.
Can I use Spring #Projection for that purpose without having to manually extract the fields I want to expose? I'd prefer just returning a List<Person> but render only certain details for certain endpoints.
#RestController
public class BookingInfoServlet {
#Autowired
private PersonRepository dao;
#GetMapping("/persons")
public List<Person> persons() {
return dao.findAll();
}
//TODO how can I assign the Projection here?
#GetMapping("/personsView")
public List<Person> persons() {
return dao.findAll();
}
//only expose certain properties
#Projection(types = Person.class)
public interface PersonView {
String getLastname();
}
}
#Entity
public class Person {
#id
long id;
String firstname, lastname, age, etc;
}
interface PersonRepository extends CrudRepository<Person, Long> {
}
Note that #Projection only works with spring data rest. I believe you could try this:
#Projection(name = "personView", types = Person.class)
public interface PersonView {
String getLastname();
}
And on your repo, you need something like this:
#RepositoryRestResource(excerptProjection = PersonView.class)
interface PersonRepository extends CrudRepository<Person, Long> {
}

Override default save Entity on Spring Data Rest

I would like to override the default CrudRepository save method that is also exported to Rest api:
#RepositoryRestResource(path = "users")
public interface UserRepository extends JpaRepository<User, Long> {
#Override
#RestResource(exported=false)
User save(User user);
}
In my ApiController I have set up a requestmapping like this:
#RequestMapping(value = "/", produces = "application/json", method = RequestMethod.POST)
#ResponseBody
public ResponseEntity<Resource<User>> registerUser(
#RequestParam("name") String name,
#RequestParam("alias") String alias,
#RequestParam("email") String email,
#RequestParam("password") String password,
#RequestParam("dateOfBirth") String dateOfBirth,
#RequestParam("imageIdentifier") String imageIdentifier) {
User user = new User();
//try {
// userReposiotry.save(user);
//} catch (Exception e) {
//}
Resource<User> resource = toResource(user);
return new ResponseEntity<Resource<User>>(resource, HttpStatus.OK);
}
The problem is when I try to POST to localhost:8080/api/users it returns a "Method Not allowed" which is good because it was set "exported=false"
But how can I implement my own POST for localhost:8080/api/users ?
Thanks
Another way to do it is to create a custom repository implementation like so:
#RepositoryRestResource(path = "users")
public interface UserRepository extends JpaRepository<User, Long>, UserRepositoryCustom {
#Override
#RestResource(exported=false)
User save(User user);
}
public interface UserRepositoryCustom {
<S extends User> S save(T entity);
}
public UserRepositoryImpl implements UserRepositoryCustom {
<S extends User> S save(T entity) {
// implementation code...
}
}
If you look at the CrudRepository you will find a method <S extends T> S save(S entity);, that's where I got the save(..) from, just changed the extends T to extends User.
The other thing that I would pay attention to is the naming of the classes/interfaces, try to be consistent. The way I named them should work for you, the UserRepositoryImpl must have that name in order for this to work.
Doing this you won't have to set exported=false and you can just use the save() method as you would do normal.
Found a solution:
#BasePathAwareController
#RequestMapping("/users")
public class RestApiController implements ResourceProcessor<Resource<User>>{
#Autowired
private EntityLinks entityLinks;
#RequestMapping(method=RequestMethod.POST)
#ResponseBody
public ResponseEntity<Resource<User>> saveUser(#Param("name") String name) {
// Testing
System.out.println(name);
Resource<User> resource = new Resource<>(new User());
return new ResponseEntity<>(resource , HttpStatus.OK);
}
#Override
public Resource<User> process(Resource<User> resource) {
LinkBuilder lb = entityLinks.linkFor(User.class);
resource.add(new Link(lb.toString()));
return resource;
}
}
The CrudRepository save is still set as exported=false as in my question.

Getting "No mapping found for HTTP request with URI [/system/people]..." when try to access jpa data via rest

This is the what I have for the repo:
#RepositoryRestResource(collectionResourceRel = "people", path = "people")
public interface PersonRepo extends PagingAndSortingRepository<Person, Long> {
List<Person> findByName(#Param("name") String name);
}
http://localhost:8080/system/people is what I'm trying to access, where "http://localhost:8080/system" is the base url for the spring MVC project
I've found the solution for it, according to the http://docs.spring.io/spring-data/rest/docs/2.3.0.RELEASE/reference/html/#getting-started.introduction, I added a class for the data access configuration:
#Configuration
public class CustomizedRestMvcConfiguration extends RepositoryRestMvcConfiguration {
#Override
public RepositoryRestConfiguration config() {
RepositoryRestConfiguration config = super.config();
config.setBasePath("/api");
return config;
}
}
In addition, I have also added #Import(CustomizedRestMvcConfiguration.class) in my spring applications config class. And now whenever I do a http://localhost:8080/system/api/people call, the database entries can be retrieved.
try
#RequestMapping(value="/people/" , method=RequestMethod.GET)
public interface PersonRepo extends PagingAndSortingRepository<Person, Long> {
List<Person> findByName(#Param("name") String name);
}

Categories

Resources