rest api get return HttpStatus.NO_CONTENT - java

i have this entity class to store movie details.
#Table(name = "movie")
public class Movie {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String title;
private String category;
private double rating;
}
Now i also have a rest api to get by movie id
How do i modify my rest api to return http error code 204, to show no content has been found.
#GetMapping("/movie/{id}")
public Movie getMovieById(#PathVariable(value = "id") Integer id) {
if (service.get(id)!=null) {
return service.get(id);
} else {
return HttpStatus.NO_CONTENT;
}
}

204 No Content is not the appropriate status code here; that means "the resource exists/existed but I am not sending contents". This is specifically the case for 404 Not Found. You can either throw an exception (such as ResponseStatusException) or change your controller to return ResponseEntity<Movie>.

In case asked for content is not available you could stick to 404 Resource Not found. Using some other obscure (not 204) HTTP status code is not a good idea as it can cause browser to react differently.
In your case, returning any empty response body, is also fine.

you can do something like below,
#Table(name = "movie")
public class Movie {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String title;
private String category;
private double rating;
public String status;
}
#GetMapping("/movie/{id}")
public ResponseEntity<Movie> getMovieById(#PathVariable(value = "id") Integer id) {
ResponseEntity<Movie> response = null;
if (service.get(id)!=null) {
Movie movie = service.get(id);
ResponseEntity.ok(movie)
} else {
Movie movie = new Movie()
movie.setStatus("FAILURE");
ResponseEntity response = new ResponseEntity<>(movie, HttpStatus.NOT_FOUND)
}
return response
}

Related

Hibernate query returns null, but entity gets loaded on object anyway

This will take some explaining. So, I have an entity called Invoice and a related table called Errors, which is used to store some processing errors.
In a DAO class, I have a query for fetch the errors with some specific criteria:
public Errors loadLastError(Invoice i) {
try (Session session = factory.openSession()) {
Query query = session.createQuery("select er from Errors er" +
" inner join er.invoice i" +
" where er.invoice = :invoice" +
" and i.status <> :code" +
" and i.proccessStatus = :status" +
" order by er.id desc");
query.setParameter("invoice", invoice);
query.setParameter("code", "001");
query.setParameter("status", "form_error");
var result = query.getSingleResult();
return (Errors) result;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
This works fine: will only get results when the conditions match. When they don't, I get the expected null result where this method is called:
this.invoice.setError(loadLastError(this.invoice);
When inspecting the code, I can see that the this.invoice object was updated correctly with a null result.
But, as soon as I pass this object invoice to another class in order to do some proccessing (send notifications basically by JSON), it gets there with a Errors object loaded, as if my original query had actually found something, which it didn't.
The following are a shortened example of my entity classes:
The Invoice:
#Entity
#DynamicUpdate
#Table(name = "data.invoice")
#TypeDef(
name = "pgsql_enum",
typeClass = PostgreSQLEnumType.class
)
public class Invoice implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#JsonIgnore
#Column(name = "proccessStatus")
private String proccessStatus;
#JsonIgnore
#Column(name = "status")
private String status;
#JsonIgnore
#OneToOne(mappedBy = "invoice", fetch = FetchType.LAZY)
private Errors errors;
public Integer getId() {
return id;
}
public String getProccessStatus() {
return proccessStatus;
}
public void setProccessStatus(String proccessStatus) {
this.proccessStatus= proccessStatus;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status= status;
}
public Errors getErrosr() {
return errors;
}
public void setErrors(Errorserrors) {
this.errors= errors;
}
The Errors entity:
#Entity
#Table(name = "data.invoice_errors")
public class Errors implements Serializable {
public Errors() {
}
public Errors(Invoice invoice, String error) {
this.invoice= invoice;
this.error = error;
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#OneToOne
#JoinColumn(name = "id_invoice")
private Invoice invoice;
private String error;
#Column(name = "created_at")
private LocalDateTime createdAt;
public Integer getId() {
return id;
}
public Invoice getInvoice() {
return invoice;
}
public void setInvoice(Invoice invoice) {
this.invoice = invoice;
}
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
This behaviour seems very strange and I don't know how to diagnose it and what may be wrong. Any input would be very appreciated.
What I'm expecting is that the entity don't get updated out of nowhere with a result that wasn't found initially because it simply didn't match the search criteria in the first place.
I'm a colossal idiot. The issue was that the notification class was refreshing the model. Changed the database search to go after the refresh and fixed the problem.

How GET a list of files

I need that every time I list a portfolio it returns all the images that exist in that specific portfolio
I can list 1 by 1 via ID but when I send my endpoint to list all photos belonging to the ID of a specific portfolio it only returns me null
Photo Class
#Entity
public class Foto {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String nomeArquivo;
#ManyToOne(cascade = CascadeType.MERGE)
private Perfil autonomo;
#Column(length = 5_000_000)
private byte[] fotoByte;
private String tipoArquivo;
}//Getters and Setters
AutonomoService
#Autowired
FotosRepository fotosRepository;
#Autowired
PerfisRepository perfisRepository;
public List<byte[]> portfolio(int id){
if (perfisRepository.existsById(id)) {
return fotosRepository.findAllByAutonomoId(id).stream().map(f-> f.getFotoByte()).collect(Collectors.toList());
}
else {
return null;
}
} //Getters and Setters
Controller
#GetMapping("/portfolio/fotos/{id}")
public ResponseEntity<List<byte[]>> getPortfolioAutonomo(#PathVariable int id) throws IOException {
List<byte[]> result = autonomoService.portfolio(id);
return ResponseEntity.status(200).body(result);
}
And this is the way I can get 1 photo by its id
#GetMapping("/portfolio/{id}")
public ResponseEntity getPortfolio(#PathVariable int id){
Optional<Foto> anexoOptional = fotosRepository.findById(id);
if (anexoOptional.isPresent()) {
Foto anexo = anexoOptional.get();
return ResponseEntity.status(200)
.header("content-type", anexo.getTipoArquivo())
.header("content-disposition", "filename=" + anexo.getNomeArquivo())
.body(anexo.getFotoByte());
} else {
return ResponseEntity.status(404).build();
}
}
Instead of
return fotosRepository.findAllByAutonomoId(id).stream().map(f-> f.getFotoByte()).collect(Collectors.toList());
Can you try with
return fotosRepository.findAllById(id).stream().map(f-> f.getFotoByte()).collect(Collectors.toList());
If still this is not working, better to go with #Query implemention.

How to send a Java Long value from json

I'm trying to send a POST request to an Spring RestController with a request body. In the object there is a Long value but it is not arriving to the endpoint with the other parameters.
The class used as #RequestBody is this one:
#Entity
public class Curso {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "titulo")
private String titulo;
#Column(name = "nivel")
private String nivel;
#Column(name = "nhoras")
private String nhoras;
#Column(name = "profesorid")
private Long profesorid;
[...]
public Long getProfesorid() {
return profesorid;
}
public void setProfesorid(Long profesor) {
this.profesorid = profesorid;
}
}
The endpoint in the class annotated with #RestController, is this:
#RequestMapping(value = "/crear-curso", method = RequestMethod.POST)
public void addCurso(#RequestBody Curso curso) {
cursoService.addCurso(curso);
}
And this is the JSON I'm using in the body of the POST request:
{
"titulo": "Git",
"nivel": "Intermedio",
"nhoras": "12",
"profesorid": 1581068174
}
All the other parameters are arriving correctly and the object arrives to the database, but with the profesorid with null value. I stopped the execution y this addCurso method and the value of profesorid is null. The id value is not being sent in the request because it is setted before saving in the database.
Please, anyone can help me and say what is failing here? Many thanks in advance.
public void setProfesorid(Long profesor) {
this.profesorid = profesorid;
}
Look at this setter. You've made a mistake here.
It should be
public void setProfesorid(Long profesor) {
this.profesorid = profesor;
}

Extending/modifying generated entities in Hibernate

I am creating a REST api service for a mysql database. I've generated classes using IntelliJ's persistence tool. It does a pretty good job.
There are some quirks to the schema that I am working with. The users want the endpoints to be accessible by another property other than the "id" primary key column.
Ex: /object/<name property>' versus/object/`.
Here is the catch though. The schema can change. The name property is not going anywhere though so I can safely assume that will always be on the object.
I've learned that you can use Superclasses to force these generated entites to have custom properties without affecting the database schema. I dont want to make a model change in the generated entity and have that update the database table layout as it is not my database.
I have a class called Animal.
#Entity
#Table(name = "animals", schema = "xyz123", catalog = "")
public class AnimalEntity extends AnimalSuperclass {
private Integer id;
private String name;
private String description;
#Id
#Column(name = "id", nullable = false)
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
#Basic
#Column(name = "name", nullable = true, length = 80)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Basic
#Column(name = "description", nullable = true, length = 255)
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
RoleEntity that = (RoleEntity) o;
return Objects.equals(id, that.id) &&
Objects.equals(name, that.name) &&
Objects.equals(description, that.description);
}
#Override
public int hashCode() {
return Objects.hash(id, name, description);
}
}
I have to manually add extends AnimalSuperclass. Which is fine for now. Eventually I am going to try to generate these using .xmls on runtime.
Then I have this superclass..
#MappedSuperclass
public class AnimalSuperclass implements Serializable {
private String testMessage;
private String name;
private Integer id;
#Transient
public String getTestMessage() {
return this.testMessage;
}
public void setTestMessage(String id) {
this.testMessage = testMessage;
}
}
What I want to do is force the #Id annotation to be on the name property from within the superclass. Something like this..
#MappedSuperclass
public class AnimalSuperclass implements Serializable {
private String testMessage;
private String name;
private Integer id;
#Transient
public String getTestMessage() {
return this.testMessage;
}
public void setTestMessage(String id) {
this.testMessage = testMessage;
}
#Basic
#Id
#Column(name = "name", nullable = false, length = 15)
private String getName() {
return name;
}
private void setName(String name) {
this.name = name;
}
#NaturalId
#Column(name = "id", nullable = false)
private Integer getId() {
return id;
}
private void setId(Integer id) {
this.id = id;
}
}
How do I go about doing that? Currently this throws an error when I hit the endpoint: {"cause":null,"message":"Id must be assignable to Serializable!: null"}
Java is not my first language so I am not an expert by any means. But from what I've read, its not possible to override subclass properties from the superclass. Is there a better way to approach this, maybe by using RepositoryRestConfiguration? I am using PagingAndSortingRepository to serve these entities. I cannot extend the entities and use my superclass as a child as that creates a dType property in the schema and I cannot alter the table layout.
There is no hard link between the request and your entity. In your repository you can write methods that can query the data that is brought it from the request.
For example if they are requesting a name you can do something like
Page<AnimalEntity> findByName(String name, Pageable pageable);
in your Repository. Spring will take care of the rest and then you can call this in your controller.
#Service
public class AnimalService {
#Autowired
private AnimalEntityRepository animalRepo;
public Page<AnimalEntity> findAnimal(String name) {
Page<AnimalEntity> animals = animalRepo.findByName(name, new PageRequest(1,20));
return animals;
}
}
One thing to mention is that depending on how you configured Hibernate when sending an entity back to the client and the entity is seralized you might get an failed to lazy initialize error. If that is the case your entities will have to be converted to a POJO (plain old java object) and that sent back.

Cannot deserialize from String value Spring-Boot Restful project

I'm working on a Spring Boot + Maven + Restful + Hibernate project! After creating the RestController for adding new Devices in database i'm getting this error:
2018-03-28 10:15:18.786 WARN 9286 --- [nio-9090-exec-9] .w.s.m.s.DefaultHandlerExceptionResolver : Failed to read HTTP message: org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of `com.hhm.hsy.hibernate.models.Protocol` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('{"id":5,"protocolName":"ProtocolForTesting","port":5202}'); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.hhm.hsy.hibernate.models.Protocol` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('{"id":5,"protocolName":"ProtocolForTesting","port":5202}')
at [Source: (PushbackInputStream); line: 1, column: 52] (through reference chain: com.hhm.hsy.hibernate.models.Device["protocol"])
Here is my first entity:
#Entity
#Table(name = "devices", catalog = "hqm")
public class Device implements Serializable {
private static final long serialVersionUID = -8311225474375837513L;
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "device_id", unique = true, nullable = false)
private Integer id;
#Column(name = "device_name", unique = true, nullable = false)
private String deviceName;
#ManyToOne
#JoinColumn(name = "protocol_id")
private Protocol protocol;
public Device() {
}
public Device(Integer id, String deviceName, Protocol protocol) {
this.id = id;
this.deviceName = deviceName;
this.protocol = protocol;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getDeviceName() {
return deviceName;
}
public void setDeviceName(String deviceName) {
this.deviceName = deviceName;
}
public Protocol getProtocol() {
return protocol;
}
public void setProtocol(Protocol protocol) {
this.protocol = protocol;
}
And the second entity:
#Entity
#Table(name = "protocols", catalog = "hqm")
public class Protocol implements Serializable {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "protocol_id", unique = true, nullable = false)
private Integer id;
#Column(name = "protocol_name", unique = true, nullable = false, length = 45)
private String protocolName;
#Column(name = "port", nullable = false)
private Integer port;
#OneToMany(mappedBy = "protocol", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Device> devices = new HashSet<>();
public Protocol() {
}
public Protocol(Integer id, String protocolName, Integer port) {
this.id = id;
this.protocolName = protocolName;
this.port = port;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getProtocolName() {
return protocolName;
}
public void setProtocolName(String protocolName) {
this.protocolName = protocolName;
}
public Integer getPort() {
return port;
}
public void setPort(Integer port) {
this.port = port;
}
#JsonIgnore
public Set<Device> getDevices() {
return devices;
}
#JsonIgnore
public void setDevices(Set<Device> devices) {
this.devices = devices;
}
}
Controller:
#RestController
#RequestMapping(value = "/api/devices")
#ComponentScan({"com.hhm.hsy.pmcs.*"})
public class DevicesController {
#Autowired
#Qualifier(value = "deviceService")
GenericServiceIntf deviceService;
// get ALL DEVICE
#RequestMapping(value = "", method = RequestMethod.GET)
public Map<String, Object> getDevices() {
Map<String, Object> devicesMap = new HashMap<>();
devicesMap.put("devices", deviceService.getAll());
return devicesMap;
}
//save a new DEVICE
#RequestMapping(value = "", method = RequestMethod.POST, consumes = {"application/json"}, produces = {"application/json"})
#ResponseStatus(HttpStatus.CREATED)
public ResponseEntity<Device> addDevice(#RequestBody Device device) {
deviceService.save(device);
return ResponseEntity.accepted().body(device);
}
}
Service:
#Service("deviceService")
public class DeviceServiceImpl extends GenericServiceAbstractImpl<Device, Integer> implements Serializable{
private static final long serialVersionUID = 697655212967127150L;
#Autowired
public DeviceServiceImpl(#Qualifier("deviceDao") GenericDaoIntf genericDao) {
super(genericDao);
}
}
So when i'm trying to add a new device, i get the error i mentioned upper.I don't know what is causing this exception. When I try to add with post a new Protocol it's working, table is being created in the database correctly and I am getting the data correctly in GET request as well..Please help me, I'm new to springboot and restful... if some more information is required, please just inform me and i will post it! Thank you!
I tried to reproduce your problem: here, but everything works as expected.
I think it can be related with this bug.
You should try to reproduce bug with different jackson version.
EDIT:
One more thing: It looks like you try to construct Protocol instead of Device. Show us your deviceService, if you can.
Failed to read HTTP message:
org.springframework.http.converter.HttpMessageNotReadableException:
JSON parse error: Cannot construct instance of
`com.hhm.hsy.hibernate.models.Protocol

Categories

Resources