I am designing an API using Spring boot for some social network backend. My current model looks like this:
public class User {
private long id;
private String handle;
private String name;
private List<User> followers;
private List<User> following;
// Getters setters etc
Now, I have created DTO which closely resembles above structure. My problem is that sometimes I want to return exactly what's above (which is fine) but sometimes, I don't want that.
For example when someone is only interested in finding followers of user, I don't want to include followers and following (I am simply interested in id
, handle
and name
so computing followers
and following
for all of those users would be an incredible waste of resources).
In my current implementation those fields are returned with null
values, which I don't think is a great idea. Should I create a separate DTO without those lists with just id
, handle
and name
? Or is there more elegant way to do it?
It is a controversial issue. If you don't want to create separate dto there are several ways to do it. It depends on what data access approach you are going to use:
Using Spring Data JPA it is possible to return an entity in projection. You just need to add an additional constructor to your entity:
public interface UserRepository extends JpaRepository<User, Long> {
@Query("select new User(u.id,u.name) from User u")
List<User> findAllUserItems();
}
Or same using JPA EntityManger:
public List<User> findAllUserItems() {
return entityManager.createQuery("select new User(u.id,u.name) from User u", User.class)
.getResultList();
}
If you wonder about unnecessary null fields and you are using Jackson, it is possible to configure to ignore null fields. For Spring Boot:
spring.jackson.default-property-inclusion=non_null
Or with Java config:
@Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.serializationInclusion(JsonInclude.Include.NON_NULL);
return builder;
}
Or for not Spring Boot project:
@Configuration
@EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(converter());
}
@Bean
public HttpMessageConverter converter() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
return new MappingJackson2HttpMessageConverter(objectMapper);
}
}
Also, if you going to use Hibernate Session. To map directly to dto you may use AliasToBeanResultTransformer
:
public List<UserDto> findAllUserItems() {
return session.createQuery("select u.id as id,u.name as name from User u")
.setResultTransformer(Transformers.aliasToBean(UserDto.class))
.list();
}