4

I have a Car entity. I want to expose its data in a REST API. I'm currently using Spring Data REST to do this. After reading this post, I became comfortable doing this instead of creating a CarDto and passing an instance of that back to the client:

The Car Entity

@Entity
@Data
public class Car {

  @Id
  private Long id;
  
  private BigDecimal askingPrice;
  
  // other fields

}

Spring Data Repository

public interface CarRepository extends CrudRepository<Car, Long> {
}

Gradle file has the following dependency to enable clients to access Cars at /api/cars

implementation 'org.springframework.boot:spring-boot-starter-data-rest'

Now, I have a new Car property. Its value is not stored in the same database as Car entity. I get its value from a web service. I seem to be in a similar situation that is described in this post that advocates the use of DTOs.

Do I need to abandon Spring Data REST?

I know that I can modify my Car entity to include this new field as a transient...

@Entity
@Data
public class Car {

  @Id
  private Long id;
  
  private BigDecimal askingPrice;
  
  // Value is populated from a web service
  @Transient      
  private BigDecimal kellyBlueBookPrice;

  // other fields

}

But is that a good approach? Is there a way to have Spring Data REST return some form of a DTO that includes this new property? It's possible that a few other fields may be added in the future from other data sources.

James
  • 2,876
  • 18
  • 72
  • 116

2 Answers2

4

Is there a way to have Spring Data REST return some form of a DTO that includes this new property?

No way directly you can achieve this. But you need to write some code where it can get data and convert it to DTO. Also using this approach URL will be not the same it will be changed as if you still using spring-data-rest.

There is a sample given at https://stackoverflow.com/a/45401735. But it's using the old version and is not compatible with the new spring-data-rest.

The best would be to use DTO and not directly use spring data rest implementation if you have changes in response. Some of the scenarios you can consider:

  • If you using ManyToMany, ManyToOne, and OneToOne mapping then you must skip bidirectional mapping. If you have then while converting the entity to JSON it will go in a circular loop and will never come back.
  • With using mapping it requires the hibernate session to be open until it converts the entity to JSON.
  • With Entity expose it will also expose all and other related information which is not required. Which can lead to Penetration Test to be failed for your application.
  • To partially save data you still need to pass the full object else it will reset other properties to null.

To avoid boilerplate code converting Entity to DTO and vice-versa. You can use some libraries like MapStruct, ModelMapper.

MapStruct example

@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE)
public abstract class CarMapper {

    public abstract CarDto carToCarDto(Car car);

    @InheritInverseConfiguration
    public abstract Car carDtoToCar(CarDto carDto);

}

By using DTO you can have a separate service layer where you can manage database transactions to do operations in multiple tables. You can control how much information is exposed through REST.

If you want to use the same response for operations then you can use spring-hateoas which will have _embedded/_links object.

Dhaval Gajjar
  • 2,508
  • 1
  • 2
  • 13
3

I completely disagree with the post you refer. I think you need to understand that we are talking about several layers here if we are talking about a properly designed application: presentation (REST webservices), application services - domain services, repositories, aggregates and entities - ORM repositories and entities - database code e.g. SQL plus you can add access control and logging for any of these.

https://docs.spring.io/spring-data/data-commons/docs/1.6.1.RELEASE/reference/html/repositories.html

"The goal of Spring Data repository abstraction is to significantly reduce the amount of boilerplate code required to implement data access layers for various persistence stores."

So what Spring Data repositories do is the "ORM repositories and entities - database code e.g. SQL" part of the story and what REST does is the "presentation (REST webservices)" part of the story. They are very far from each other. When you are able to simply generate the code between them, then it is a sign, that something is off with your project, because it does not do much beyond data storage and retrieval. Currently there are databases with HTTP CRUD interface which do the exact same thing. I think there is a widespread confusion that REST resources represent database entities, while they represent a bunch of callable operations on a webservice.

If you write this kind of application, then at least generate the actual code with DTOs instead of completely relying on limited frameworks and their annotations, otherwise you will hit the wall when you are trying to do something, which is a real service, not just data storage. And don't believe everything others write on stack overflow. Maybe I am wrong to...

Another thing worth to mention here, that even if we are talking about data storage, REST serves some sort of view model, which should have a structure for displaying the data instead of the structure for storing the data.

inf3rno
  • 24,976
  • 11
  • 115
  • 197
  • 1
    Thanks. In regards to "And don't believe everything others write on stack overflow" I agree with this sentiment but I gave [that post](https://stackoverflow.com/a/38876046/1706691) serious consideration bc the author 1) makes a good case for using Spring Data REST noting the representation is not simply the serialized entity 2) is the project lead of Spring Data 3) Several others seem to agree with using Spring Data REST. That doesn't mean it's right but arguments seemed sound to me. It certainly does seem to reduce boilerplate code where simple CRUD is only needed.. until it isn't. – James Oct 12 '22 at 18:34