HATEOAS on Spring Flux/Mono response - spring

I've been using Spring HATEOAS following the guidelines:
https://spring.io/guides/gs/rest-hateoas/#initial
package hello;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.*;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
#RestController
public class GreetingController {
private static final String TEMPLATE = "Hello, %s!";
#RequestMapping("/greeting")
public HttpEntity<Greeting> greeting(#RequestParam(value = "name", required = false, defaultValue = "World") String name) {
Greeting greeting = new Greeting(String.format(TEMPLATE, name));
greeting.add(linkTo(methodOn(GreetingController.class).greeting(name)).withSelfRel());
return new ResponseEntity<Greeting>(greeting, HttpStatus.OK);
}
}
Now I want to use a repository and output a Flux/Mono response:
#RestController
class PersonController {
private final PersonRepository people;
public PersonController(PersonRepository people) {
this.people = people;
}
#GetMapping("/people")
Flux<String> namesByLastname(#RequestParam Mono<String> lastname) {
Flux<Person> result = repository.findByLastname(lastname);
return result.map(it -> it.getFullName());
}
}
How can I use Spring HATEOAS on a Flux/Mono response? Is it possible at all?

Update as there is support to use HATEOAS with Spring Web Flux.
public class Person extends ResourceSupport
{
public Person(Long uid, String name, String age) {
this.uid = uid;
this.name = name;
this.age = age;
}
private Long uid;
private String name;
private String age;
public Long getUid() {
return uid;
}
public void setUid(Long uid) {
this.uid = uid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
Using the above Person in Controller as follows
#GetMapping("/all")
public Flux getAllPerson() {
Flux<List<Person>> data = Flux.just(persons);
return data.map(x -> mapPersonsRes(x));
}
private List<Resource<Person>> mapPersonsRes(List<Person> persons) {
List<Resource<Person>> resources = persons.stream()
.map(x -> new Resource<>(x,
linkTo(methodOn(PersonController.class).getPerson(x.getUid())).withSelfRel(),
linkTo(methodOn(PersonController.class).getAllPerson()).withRel("person")))
.collect(Collectors.toList());
return resources;
}
Or if you want for one person, you can also use Mono
#GetMapping("/{id}")
public Mono<Resource<Person>> getPerson(#PathVariable("id") Long id){
Mono<Person> data = Mono.justOrEmpty(persons.stream().filter(x -> x.getUid().equals(id)).findFirst());
Mono person = data.map(x -> {
x.add(linkTo(methodOn(PersonController.class).getPerson(id)).withSelfRel());
return x;
});
return person;
}
This is simple use of .map function provided by Flux/Mono.
I hope this will be helpful for later viewers.

I think this project does not support (yet?) the new reactive support in Spring Framework. Your best bet is to reach out to the maintainers and contribute to the project (creating an issue and explaining what you're trying to achieve is a start!).

Related

identifier of an instance of ...was altered from

i found many response about this title "identifier of an instance of ...was altered from ..." but none of this give me a solution.
i am using PostgreSQL
with just 2 column id_type and libelle.
here is my Model level :
package com.stev.pillecons.pilleCons.models;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
#Entity(name = "type_pille")
#JsonIgnoreProperties({"hibernateLazyInitializer","handler"})
public class LePille {
#Id
#GeneratedValue (strategy = GenerationType.IDENTITY)
private int id_type;
private String libelle;
public LePille(){}
public String getLibelle() {
return libelle;
}
public void setLibelle(String libelle) {
this.libelle = libelle;
}
public int getId_type() {
return id_type;
}
public void setId_type(int id_type) {
this.id_type = id_type;
}
}
My Service level :
#Override
public LePille updatePille(Integer id, LePille Sourcepille) {
Optional<LePille> existingSession = pilleRepo.findById(id);
if (existingSession.isPresent())
{
LePille Targetpile = existingSession.get();
BeanUtils.copyProperties(Sourcepille, Targetpile);
return pilleRepo.saveAndFlush(Targetpile);
}
else
{
throw new PilleException("pille not found");
}
}
when i debug it, with the data
{"id_type":10,"libelle":"dsf"}
with postman
the value of TargetPille is : {"id_type":10,"libelle":"dsf"}
and the value of SourcePille : {"id_type":0,"libelle":"popo"}
last but not least is Controller level:
#RequestMapping(value = "{id}", method = RequestMethod.PUT)
public ResponseEntity update(#PathVariable Integer id, #RequestBody LePille session) {
LePille updPille = pilleService.updatePille(id, session);
return new ResponseEntity<LePille>(updPille, HttpStatus.OK);
}
it is strange because juste update that not working, Create, Read and Delete works fine.
thanks in advance
i just change the code like this:
BeanUtils.copyProperties(Sourcepille, Targetpile, "id_type");
just add the id_type to ignore variable

Java Spring Boot Entites not saving to JpaRepository

That's my first project with Spring Boot implemented. I tried going step by step with official Spring tutorial but I'm stuck with a problem that I can't find any answer about.
Whenever I try to call findAll() or find() method on my repository it returns empty array [].
Even with manual preloading enitites like done in tutorial and immediately trying to display database content I get the same result.
I can guess I'm missing something silly, but I can't figure it out for some hours now. What's the cause? Tomcat/jpa/spring version mismatch? Missing annotation somewhere?
Here's my AnimalRepository.java
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface AnimalRepository extends JpaRepository<Animal, Long> {
}
LoadDatabase.java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.Transactional;
#Configuration
class LoadDatabase {
private static final Logger log = LoggerFactory.getLogger(LoadDatabase.class);
#Bean
CommandLineRunner initDatabase(AnimalRepository repository) {
return args -> {
log.info("Preloading " + repository.save(new Lion("Bilbo")));
log.info("Preloading " + repository.save(new Lion("Frodo")));
log.info(repository.findAll().toString()); //try to log content to console
};
}
}
The logging above basically ends up with this console output:
logging result
Probably not as important, but AnimalController.java
import org.springframework.web.bind.annotation.*;
import java.util.List;
#RestController
public class AnimalController {
private final AnimalRepository repository;
AnimalController(AnimalRepository repository) {
this.repository = repository;
}
#GetMapping("/animals")
List<Animal> all() {
repository.save(new Lion("Bilbo")); //this doesn't work either
return repository.findAll();
}
#PostMapping("/animals")
Animal newAnimal(#RequestBody Animal newAnimal) {
return repository.save(newAnimal);
}
#GetMapping("/animals/{id}")
Animal one(#PathVariable Long id) {
return repository.findById(id)
.orElseThrow(() -> new AnimalNotFoundException(id));
}
#PutMapping("/animals/{id}")
Animal replaceAnimal(#RequestBody Animal newAnimal, #PathVariable Long id) {
return repository.findById(id)
.map(animal -> {
animal.setName(newAnimal.getName());
animal.setSpecies(newAnimal.getSpecies());
return repository.save(animal);
})
.orElseGet(() -> {
newAnimal.setId(id);
return repository.save(newAnimal);
});
}
#DeleteMapping("/animals/{id}")
void deleteAnimal(#PathVariable Long id) {
repository.deleteById(id);
}
}
And the finally Lion.java
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import java.util.Objects;
#Entity
public class Lion implements Animal {
private #Id
#GeneratedValue Long id;
private String name;
private String species;
private int requiredFood;
//private Zone zone;
public Lion(String name) {
this.name = name;
this.species = this.getClass().getSimpleName();
this.requiredFood = LION_REQUIRED_FOOD;
}
public Lion() {
}
#Override
public Long getId() {
return id;
}
#Override
public void setId(Long id) {
this.id = id;
}
#Override
public String getName() {
return name;
}
#Override
public void setName(String name) {
this.name = name;
}
#Override
public String getSpecies() {
return species;
}
#Override
public void setSpecies(String species) {
this.species = species;
}
#Override
public int getRequiredFood() {
return requiredFood;
}
#Override
public void setRequiredFood(int requiredFood) {
this.requiredFood = requiredFood;
}
#Override
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof Animal animal))
return false;
return Objects.equals(this.id, animal.getId()) && Objects.equals(this.name, animal.getName())
&& Objects.equals(this.species, animal.getSpecies());
}
#Override
public int hashCode() {
return Objects.hash(this.id, this.name, this.species);
}
#Override
public String toString() {
return "Animal{" + "id=" + this.id + ", name='" + this.name + '\'' + ", species='" + this.species + '\'' + '}';
}
}
I tried switching JpaRepository to CrudRepository, but that didn't work out.
I think the problem here is that your data haven't been saved to the database, because of the transaction. try to change your repository.save() to repository.saveAndFlush()

#OneToOne(cascade = {CascadeType.ALL}) problem

I have a problem with my code. I do a little CarRent webservice and when i try to assign Errand to the car, errand which i want to assign is add to list of errands...
this is controller of errand
package com.RentCar.Rent_A_car.domain.Controllers;
import com.RentCar.Rent_A_car.domain.Car;
import com.RentCar.Rent_A_car.domain.Errand;
import com.RentCar.Rent_A_car.domain.Services.CarService;
import com.RentCar.Rent_A_car.domain.Services.ErrandService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
#Controller
public class ErrandController {
#Autowired
CarService carService;
#Autowired
ErrandService errandService;
#RequestMapping("/assignErrand")
public String assignQuest(#RequestParam("plate") String plate, Model model){
Car car = carService.getCar(plate);
List<Errand> errandList = errandService.getAllCurrentErrands();
model.addAttribute("car",car);
model.addAttribute("errands",errandList);;
return "assignErrand";
}
#RequestMapping(value = "/assignErrand", method = RequestMethod.POST)
public String assignQuest(Car car){
carService.updateCar(car);
System.out.println("assignquest");
return "redirect:/cars";
}
These are my repositories
#Repository
public class CarsRepository {
// class to do CRUD operation on cars and errands
Map<String, Car> CarList = new HashMap<>();
#PersistenceContext
private EntityManager em;
#Transactional
public void AddCar (String mark, String plate, int mileage){
Car newCar = new Car(mark, plate, mileage);
em.persist(newCar);
}
//pobiera aktualne id i dodaje do mapy
#Transactional
public void AddCar(Car car) {
em.persist(car);
}
// dla każdego "powyzej" usuniętego trzeba zmniejszyć id o 1 ; pętla o dlugosci pozostałych aut id--; KONIECZNE FINALNIE, do budowy nie
#Transactional
public void DellateCar(String plate){
Car c = em.find(Car.class, plate);
em.remove(c);
}
public Car getCar(String plate){
Car car = em.createQuery("from Car c where c.plate=:plate", Car.class)
.setParameter("plate", plate).getSingleResult();
return car;
}
public Collection<Car> getAllCars() {
return em.createQuery("from Car",Car.class).getResultList();
}
#Transactional
public void updateCar(Car car) {
em.merge(car);
}
// public String getPlate() {
// if (CarList.isEmpty()) {
// return "Car list is empty";
// } else {
// return (String)CarList.keySet();
// }
// }
// #PostConstruct
// public void build() {
// AddCar("BMW","DW",100000);
// AddCar("Opel","DTR",100043);
// AddCar("Toyota","WB",1012300);
// AddCar("Audi","DST",102340);
//
// }
#Override
public String toString() {
return "CarsRepository{" +
"CarList=" +
'}' + "\n";
}
}
Nr
#Repository
public class ErrandRepository {
#PersistenceContext
private EntityManager ee;
List<Errand> ErrandList = new ArrayList<>();
#Transactional
public void createErrand(String description) {
Errand newErrand = new Errand(description);
ee.persist(newErrand);
System.out.println(newErrand);
}
public void createErrand(Errand errand){ErrandList.add(errand);}
public List<Errand> getAll () {
return ee.createQuery("from Errand", Errand.class).getResultList();
}
public void delateErrand(Errand errand) {
ee.remove(errand);
}
#Transactional
public void delateErrand(int x) {
ErrandList.remove(x);
}
public Errand getErrandId(Integer x){
return ee.find(Errand.class, x );
}
#PostConstruct
#Transactional
public void init(){
Errand e1 = new Errand("zlecenie na wrocław");
Errand e2 = new Errand("zlecenie na wasdw");
Errand e3 = new Errand("zlecenie na wfgasfdław");
}
#Override
public String toString() {
return "ErrandRepository{" +
"ErrandList=" + ErrandList +
'}';
}
}
this is a part of CarService
public void updateCar(Car car) {
carsRepository.updateCar(car);
System.out.println("tu sie wywołuje updatecar");
}
i don't know what is a problem... can you help me ?
edit, i add car and errand class
package com.RentCar.Rent_A_car.domain;
import javax.persistence.*;
import javax.persistence.Entity;
#Entity
public class Car {
// #Id
// #GeneratedValue(strategy = GenerationType.AUTO)
// public int id;
String mark;
#Id
String plate;
int mileage;
#OneToOne(cascade = CascadeType.ALL)
public Errand errand;
public Car() { }
public Car(String mark, String plate, int mileage) {
this.mark = mark;
this.plate = plate;
this.mileage = mileage;
}
public void setErrand(Errand errand) {
this.errand = errand;
}
public String getMark() {
return mark;
}
public void setMark(String mark) {
this.mark = mark;
}
public String getPlate() {
return plate;
}
public void setPlate(String plate) {
this.plate = plate;
}
public int getMileage() {
return mileage;
}
public void setMileage(int mileage) {
this.mileage = mileage;
}
public Errand getErrand() {
return errand;
}enter code here
And errand class
package com.RentCar.Rent_A_car.domain;
import net.bytebuddy.dynamic.loading.InjectionClassLoader;
import javax.persistence.*;
import javax.persistence.Entity;
#Entity
public class Errand {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public int Id;
public String description;
public Errand() {
}
public Errand(String description) {
this.description = description;
}
#Override
public String toString() {
return description;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
I'm not quite sure about what is the actual problem. But anyway, I can make you some recommendations:
use plural nouns for REST uris, like:
#GetMapping("cars")
#GetMapping("errands")
and so on. Not verbs, like: assignErrand
If you split Repositories and Services, do not put login in repos, like
#Transactional
public void createErrand(String description) {
Errand newErrand = new Errand(description);
ee.persist(newErrand);
System.out.println(newErrand);
}
In the repos you should do just the persistence job. Leave the logic to the service
To add errands, I would go with something like this:
#PostMapping("/cars/{carId}/errands")
public Car addErranr(#PathVariable int carId, #RequestBody Errand errand){
Car car = this.carService.findById(carId);
car.getErrands().add(errand);
return this.carService.save(car);
}

MapStruct does not detect setters in builder

I am building a simple REST service using spring. I separated my entities from DTOs and I made the DTOs immutable using Immutables. I needed mapping between DTOs and DAOs, so I chose MapStruct. The Mapper is not able to detect the setters I have defined in my DAOs.
The problem is exactly similar to this question. This question does not have an accepted answer and I have tried all of the suggestions in that question and they don't work. I don't want to try this answer because I feel it defeats the purpose for which I am using Immutables. #marc-von-renteln summarizes this reason nicely in the comment here
I tried the answer provided by #tobias-schulte. But that caused a different problem. In the Mapper class in the answer, trying to return Immutable*.Builder from the mapping method throws an error saying the Immutable type cannot be found.
I have exhaustively searched issues logged against MapStruct and Immutables and I haven't been able to find a solution. Unfortunately there are hardly few examples or people using a combination of MapStruct and Immutables. The mapstruct-examples repository also doesn't have an example for working with Immutables.
I even tried defining separate Mapper interfaces for each of the DtTOs (like UserStatusMapper). I was only making it more complicated with more errors.
I have created a sample spring project to demonstrate the problem.
GitHub Repo Link. This demo app is almost same as the REST service I am creating. All database (spring-data-jpa , hibernate) stuff is removed and I am using mock data.
If you checkout the project and run the demo-app you can make two API calls.
GetUser:
Request:
http://localhost:8080/user/api/v1/users/1
Response:
{
"id": 0,
"username": "TestUser",
"email": "TestUser#demo.com",
"userStatus": {
"id": 1,
"status": 1,
"statusName": "Active"
}
Createuser: PROBLEM HERE
http://localhost:8080/user/api/v1/users/create
Sample Input:
{
"username": "TestUser",
"email": "TestUser#demo.com",
"userStatus": {
"id": 1,
"status": 1,
"statusName": "Active"
}
}
Response:
{
"timestamp": "2019-04-28T09:29:24.933+0000",
"status": 500,
"error": "Internal Server Error",
"message": "Type definition error: [simple type, class com.immutablesmapstruct.demo.dto.model.ImmutableUserDto$Builder]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `com.immutablesmapstruct.demo.dto.model.ImmutableUserDto$Builder`, problem: Cannot build UserDto, some of required attributes are not set [username, email, userStatus]\n at [Source: (PushbackInputStream); line: 9, column: 1]",
"path": "/user/api/v1/users/create"
}
Below are important pieces of code related to problem:
Daos:
1. UserDao
public class User {
// Primary Key. Something that is annotated with #Id
private int id;
private String username;
private String email;
private UserStatus userStatus;
private User(Builder builder) {
id = builder.id;
username = builder.username;
email = builder.email;
userStatus = builder.userStatus;
}
public static Builder builder() {
return new Builder();
}
public int getId() {
return id;
}
public String getUsername() {
return username;
}
public String getEmail() {
return email;
}
public UserStatus getUserStatus() {
return userStatus;
}
public static final class Builder {
private int id;
private String username;
private String email;
private UserStatus userStatus;
private Builder() {
}
public Builder setId(int id) {
this.id = id;
return this;
}
public Builder setUsername(String username) {
this.username = username;
return this;
}
public Builder setEmail(String email) {
this.email = email;
return this;
}
public Builder setUserStatus(UserStatus userStatus) {
this.userStatus = userStatus;
return this;
}
public User build() {
return new User(this);
}
2. UserStatusDao:
package com.immutablesmapstruct.demo.dao.model;
/**
* Status of user.
* Example: Active or Inactive
*/
public class UserStatus {
// Primary Key. Something that is annotated with #Id
private int id;
// A value of 1 or 0
private int status;
// Active , InActive
private String statusName;
private UserStatus(Builder builder) {
id = builder.id;
status = builder.status;
statusName = builder.statusName;
}
public static Builder builder() {
return new Builder();
}
public int getId() {
return id;
}
public int getStatus() {
return status;
}
public String getStatusName() {
return statusName;
}
public static final class Builder {
private int id;
private int status;
private String statusName;
private Builder() {
}
public Builder setId(int id) {
this.id = id;
return this;
}
public Builder setStatus(int status) {
this.status = status;
return this;
}
public Builder setStatusName(String statusName) {
this.statusName = statusName;
return this;
}
public UserStatus build() {
return new UserStatus(this);
}
}
}
DTOs
1. UserDto:
package com.immutablesmapstruct.demo.dto.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.immutables.value.Value;
#Value.Immutable
#Value.Style(defaults = #Value.Immutable(copy = false), init = "set*")
#JsonSerialize(as = ImmutableUserDto.class)
#JsonDeserialize(builder = ImmutableUserDto.Builder.class)
public abstract class UserDto {
#Value.Default
#JsonProperty
public int id() {
return 0;
}
#JsonProperty
public abstract String username();
#JsonProperty
public abstract String email();
#JsonProperty
public abstract UserStatusDto userStatus();
2. UserStatusDto:
package com.immutablesmapstruct.demo.dto.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.immutables.value.Value;
#Value.Immutable
#Value.Style(defaults = #Value.Immutable(copy = false), init = "set*")
#JsonSerialize(as = ImmutableUserStatusDto.class)
#JsonDeserialize(builder = ImmutableUserStatusDto.Builder.class)
public abstract class UserStatusDto {
#JsonProperty
public abstract int id();
#JsonProperty
public abstract int status();
#JsonProperty
public abstract String statusName();
}
MapStruct UserMapper:
package com.immutablesmapstruct.demo.dto.mapper;
import com.immutablesmapstruct.demo.dao.model.User;
import com.immutablesmapstruct.demo.dao.model.UserStatus;
import com.immutablesmapstruct.demo.dto.model.UserDto;
import com.immutablesmapstruct.demo.dto.model.UserStatusDto;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
#Mapper(componentModel = "spring")
public interface UserMapper {
UserMapper USER_MAPPER_INSTANCE = Mappers.getMapper(UserMapper.class);
UserDto userDaoToDto(User user);
//Problem here.
User userDtoToDao(UserDto userDto);
UserStatusDto userStatusDaoToDto(UserStatus userStatusDao);
UserStatus userStatusDtoToDao(UserStatusDto userStatusDto);
}
If I look at the concrete method generated by MapStruct for userDtoToDao I can clearly see that the setters are not being recognized.
package com.immutablesmapstruct.demo.dto.mapper;
#Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2019-04-28T02:29:03-0700",
comments = "version: 1.3.0.Final, compiler: javac, environment: Java 1.8.0_191 (Oracle Corporation)"
)
#Component
public class UserMapperImpl implements UserMapper {
...
...
#Override
public User userDtoToDao(UserDto userDto) {
if ( userDto == null ) {
return null;
}
com.immutablesmapstruct.demo.dao.model.User.Builder user = User.builder();
return user.build();
}
....
....
}
Mapstruct doesn't recognize your getters in UserDto and UserStatusDto.
When you change the existing methods (like public abstract String username()) in these abstract classes to classic getters like
#JsonProperty("username")
public abstract String getUsername();
the MapperImpl will contain the required calls. Note, that the #JsonProperty needs to have the attributes name itself afterwards (because of the changed method name).
Here are the complete classes UserDto and UserStatusDto with said changes:
package com.immutablesmapstruct.demo.dto.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.immutables.value.Value;
#Value.Immutable
#Value.Style(defaults = #Value.Immutable(copy = false), init = "set*")
#JsonSerialize(as = ImmutableUserDto.class)
#JsonDeserialize(builder = ImmutableUserDto.Builder.class)
public abstract class UserDto {
#Value.Default
#JsonProperty("id")
public int getId() {
return 0;
}
#JsonProperty("username")
public abstract String getUsername();
#JsonProperty("email")
public abstract String getEmail();
#JsonProperty("userStatus")
public abstract UserStatusDto getUserStatus();
}
package com.immutablesmapstruct.demo.dto.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.immutables.value.Value;
#Value.Immutable
#Value.Style(defaults = #Value.Immutable(copy = false), init = "set*")
#JsonSerialize(as = ImmutableUserStatusDto.class)
#JsonDeserialize(builder = ImmutableUserStatusDto.Builder.class)
public abstract class UserStatusDto {
#JsonProperty("id")
public abstract int getId();
#JsonProperty("status")
public abstract int getStatus();
#JsonProperty("statusName")
public abstract String getStatusName();
}

Couchbase query exception on runtime Unsupported parameter type for key: class com.couchbase.client.protocol.views.Query

I am getting this exception every time i try to query a view on Couchbase DB from my spring boot application.
Unsupported parameter type for key: class com.couchbase.client.protocol.views.Query.
I was setting a string on setKey() method of Query class, got an exception. But then I checked the API and provided a json to setKey, still not working. Have searched a lot but could not get this to work.
I am sharing the code snippet in this post as well.
Application.properties
spring.couchbase.bucket.password=
spring.couchbase.bucket.name=default
spring.couchbase.bootstrap-hosts=127.0.0.1
spring.data.couchbase.repositories.enabled=true
PlayerRepository
public interface PlayerRepository extends CouchbaseRepository<Player, Integer>
{
#View(designDocument = "player", viewName = "all")
public List<Player> findAll();
#View(designDocument = "player", viewName = "by_Name")
public Player findByName(Query query);
#View(designDocument = "player", viewName = "by_TeamId")
public List<Player> findByTeamId(Query query);
}
Player.java
#Document
public class Player
{
#Id
int playerId;
#Field
String name;
#Field
String type;
#Field
String country;
#Field
String playingHand;
#Field
String era;
#Field
int teamId;
#Field
int odiCenturies;
#Field
int testCenturies;
public Player(){}
public Player(int playerId, String name, String type, String country, String playingHand, String era, int teamId,
int odiCenturies, int testCenturies) {
super();
this.playerId = playerId;
this.name = name;
this.type = type;
this.country = country;
this.playingHand = playingHand;
this.era = era;
this.teamId = teamId;
this.odiCenturies = odiCenturies;
this.testCenturies = testCenturies;
}
SpringBootApplication class
#SpringBootApplication
public class CricketTeamSelectionMain
{
/**
* #param args
*/
public static void main(String[] args)
{
SpringApplication.run(CricketTeamSelectionMain.class, args);
}
#Configuration
#EnableCouchbaseRepositories
public static class DBConfig extends AbstractCouchbaseConfiguration
{
#Value("${spring.couchbase.bucket.name}")
private String bucketName;
#Value("${spring.couchbase.bucket.password}")
private String password;
#Value("${spring.couchbase.bootstrap-hosts}")
private String ip;
#Override
public String getBucketName() {
return this.bucketName;
}
#Override
public String getBucketPassword() {
return this.password;
}
#Override
public List<String> getBootstrapHosts() {
return Arrays.asList(this.ip);
}
}
}
PlayerService class
package org.ups.fantasyCricket.service;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.ups.fantasyCricket.CricketTeamSelectionMain.DBConfig;
import org.ups.fantasyCricket.Model.Player;
import org.ups.fantasyCricket.Repository.PlayerRepository;
import com.couchbase.client.CouchbaseClient;
import com.couchbase.client.protocol.views.Query;
import com.couchbase.client.protocol.views.View;
import com.couchbase.client.protocol.views.ViewResponse;
#Service
public class PlayerService
{
#Autowired
PlayerRepository playerRepo;
private CouchbaseClient client;
public List<Player> getAllPlayers()
{
List<Player> allPlayerLists = new ArrayList<Player>();
/*allPlayerLists.addAll((Collection<? extends Player>) playerRepo.findAll());
return allPlayerLists;*/
playerRepo.findAll().forEach(allPlayerLists::add);
return allPlayerLists;
}
public Player getPlayerByName(String name)
{
DBConfig dbCon = new DBConfig();
try
{
Query query = new Query();
query.setIncludeDocs(true);
query.setKey(name);
Player player = playerRepo.findByName(query);
return player;
}
catch(Exception e)
{
e.printStackTrace();
System.out.println(e.getMessage());
}
return null;
}
public String addPlayer(Player player)
{
playerRepo.save(player);
return "Success";
}
public String updatePlayer(Player player, int id)
{
playerRepo.save(player);
return "Success";
}
public List<Player> getPlayersByTeamId(int teamId)
{
List<Player> allPlayerLists = new ArrayList<Player>();
Query query = new Query();
query.setKey(String.valueOf(teamId));
playerRepo.findByTeamId(query).forEach(allPlayerLists::add);
return allPlayerLists;
}
public String addPlayers(List<Player> players)
{
playerRepo.save(players);
return "Success";
}
}
View by_Name on CouchBase DB
function (doc) {
emit(doc.name, doc);
}
Which version of spring-data-couchbase are you using? Starting with 2.x, the #Query annotation uses query derivation and you cannot use a ViewQuery as a parameter anymore... Have a look at the docs, on query derivation with a view.
You could probably use the CouchbaseTemplate to perform a manual query though.

Resources