Spring REST Hibernate Application Design

Wednesday:

Spring 4 REST

Spring mvc

Sleep mode

Problem:

We are developing an application with a stack below.

enter image description here

The Spring REST web service will open an API for a client that displays it in a user interface (ASP.NET). The response is sent in JSON.

Consider the scenario below:

The client calls the REST api to get the user id with the id. The dao level retrieves the user object and will be passed to the client.

And below are the problems / observations for the above scenario:

  • Since the user may have other hghernate mapping objects associated with it (for example, userRoles using oneToMany), these objects must also be retrieved, otherwise the LazyInitialization exception is thrown as the user interface tries to access these collections through the User object.

  • Not all properties in the user object will be required in response (for example: some requests do not need the roles that the user has).

Given the above picture, what is the best design for sending a User object (or response) to a client through Spring REST ??

  • Create an intermediate layer of objects (for example, DTO) that simulate entity objects. Have these DTOs filled in at the Service Level as per requirement. Since the service level is performed within the transaction, number 1 will be resolved. But this requires additional copying between the entity and the DTO

  • Process release number 1/2 at the entity / query level of Hibernate (join query queries or update mapping) and exclude properties not required in response through annotations such as: @JsonIgnore. But this approach is not flexible and requires very careful design of entity classes.

Can anyone comment on this? Is there a better alternative?

+6
source share
3 answers

I highly recommend using the DTO level, here are a few reasons:

  • At some point, your REST view will not be fully mapped to the DAO Entity. Here are some examples:

    • You need to return a complete list of user information (only username and last name of the user) for the mobile version of your application.
    • You want to provide user information downloaded from DAO +, payment account information received from a separate service.
    • You want to combine information from two separate DAO objects into one service call
    • and etc.
  • Caching data using some third-party library (EhCache, Hazelcast, etc.) or a simple structure like a map. Custom serialization of Hibernate objects can be a big pain for objects with complex relationships.

  • With the DTO level, you have the service / DTO interfaces as an interface / client library for integration with other components. And you can still modify / completely reverse engineer your DAO implementation, even switching to a solution without SQL.

As a conclusion, using the Hibernate Entities in the REST API is great for simple Hello World applications, such as applications, and does not work for most real-world solutions.

+3
source

Option 1 - The Best Approach

Create an intermediate layer of objects (for example, DTO) that simulate an object. Objects

Creating a DTO object will make your design more flexible. All you have to do is process the DTO objects in the rest of the controller and not at the service level, so you can use the same service level to create many DTOs.

Copying between the entity and the DTO is extra work, but you can use the Mapper to handle it like Dozer

Consider the following example:

 @Service public class MyService { @Transactional public User getUserBId(Long id){ User user = .... return user; } } 

Rest controller:

 @RestController public UserRestController { @Resource private Myservice service; @Resource private Mapper mapper; // here you can use a dto @RequestMapping(...) public UserDto getUser(@RequestParam()Long userId){ User user = service.getUserBId(userId); return mapper.map(user,UserDto.class); } } 
+2
source

In such a scenario, you should ideally use the Hibernate4Module ( Hibernate4Module github Link )

Using this ensures that serialization of entities in JSON on the spring dormant layer is related to lazy loaded attributes and will not try to access them (or serialize them in JSON in your case).

I will give a list of possible solutions below.

If you use maven, these will be your dependencies:

 <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.3.0</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-hibernate4</artifactId> <version>2.3.0</version> </dependency> 

Create the HibernateAwareObjectMapper class and register the Hibernate4Module in it.

 package com.mypackage.web; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.hibernate4.Hibernate4Module; public class HibernateAwareObjectMapper extends ObjectMapper { private static final long serialVersionUID = 1L; public HibernateAwareObjectMapper() { registerModule(new Hibernate4Module()); } } 

If you are using spring beans xml configuration, you can use something like:

 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="..."> <mvc:annotation-driven> <mvc:message-converters> <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="objectMapper"> <bean class="com.mypackage.web.HibernateAwareObjectMapper"/> </property> </bean> </mvc:message-converters> </mvc:annotation-driven> </beans> 

else, if you are using a pure java based configuration, as when loading spring, you can do it like this:

 @Configuration @EnableWebMvc @EnableAsync @ComponentScan(basePackages = { "com.mypackage.controller" }) // package referring to controllers @PropertySource("classpath:imagesConfig.properties") public class WebConfiguration extends WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter { @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters){ List<MediaType> supportedMediaTypes=new ArrayList<>(); supportedMediaTypes.add(MediaType.APPLICATION_JSON); supportedMediaTypes.add(MediaType.TEXT_PLAIN); MappingJackson2HttpMessageConverter converter=new MappingJackson2HttpMessageConverter(); converter.setObjectMapper(new HibernateAwareObjectMapper()); converter.setPrettyPrint(true); converter.setSupportedMediaTypes(supportedMediaTypes); converters.add(converter); super.configureMessageConverters(converters); } } 

Then your controllers might look something like this:

 @RequestMapping(value="/doSomething", method=RequestMethod.POST, produces="application/json;charset=UTF-8") public @ResponseBody MyCustomWebResponseObject<MyEntity> create(@Valid @RequestBody MyEntity myEntity) throws Exception { // do whatever } 

Also now, if you want to still go through the lazy loaded attribute of the object, then in your service / DAO layer or layer annotated by @Transactional

You can do it:

 Hibernate.initialize(myEntity.getLazyLoadedAttribute()); 

Hope this helps :)

do upvote and mark this as an answer if my answer helps you :)

+1
source

All Articles