[英]Spring REST Hibernate Application Design
環境 :
Spring 4 REST
Spring MVC
過冬
問題 :
我們正在開發一個低於堆棧的應用程序。
Spring REST Web服務將為客戶端公開API,它將在UI(ASP .NET)上顯示它。 響應以JSON格式發送。
考慮以下情況:
客戶端調用REST api以獲取具有ID的用戶。 dao層提取用戶實體,並將被提交給客戶端。
以下場景的問題/觀察:
由於用戶可以使用與Hibernate映射相關的其他實體(例如userRoles使用oneToMany),因此還需要獲取這些實體,否則拋出LazyInitialization異常,因為UI嘗試通過User對象訪問這些集合。
並非響應中需要User對象中的所有屬性(例如:某些請求不需要用戶擁有的角色)。
考慮到上面的圖片,通過Spring REST向客戶端發送User對象(或響應)的最佳設計方法是什么?
創建一個模擬實體對象的中間對象層(如DTO)。 根據要求在服務層中填充此DTO。 由於服務層在交易中運行,因此將解決第1號問題。 但這需要在實體和DTO之間進行額外的復制
在Hibernate實體/查詢級別處理問題編號1/2(連接獲取查詢或修改映射)並通過注釋排除響應中不需要的屬性,如:@JsonIgnore。 但是這種方法不靈活,需要非常仔細地設計實體類
任何人都可以對此發表評論嗎? 還有更好的選擇嗎?
我強烈建議使用DTO級別,原因如下:
在某些時候,您的REST表示將不會完全匹配DAO實體。 以下是一些例子:
使用某些第三方庫(EhCache,Hazelcast等)或簡單的Map類結構緩存數據 - Hibernate實體的自定義序列化可能會成為具有復雜關系的實體的巨大痛苦。
使用DTO級別,您可以將服務接口/ DTO作為接口/客戶端庫,以便與其他組件集成。 您仍然可以安全地修改/完全重新設計DAO層實現,甚至可以切換到No SQL解決方案。
作為結論 - 在REST API中使用Hibernate實體可以很好地處理簡單的“Hello World”應用程序,並且不適用於大多數現實生活中的解決方案。
選項1是最好的方法
創建一個模擬實體對象的中間對象層(如DTO)
創建DTO對象將使您的設計更加靈活。您所要做的就是處理其余Controller中的DTO對象而不是服務層,這樣您就可以使用相同的服務層來生成許多DTO。
在實體和DTO之間復制,這是一項額外的工作,但您可以使用Mapper
來處理Dozer
考慮這個例子:
@Service
public class MyService {
@Transactional
public User getUserBId(Long id){
User user = ....
return user;
}
}
休息控制器:
@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);
}
}
在這樣的scenariou你理想情況下你應該使用Hibernate4Module(參考Hibernate4Module github鏈接 )
使用此選項將確保在spring rest層上將實體序列化為JSON,並且不會嘗試訪問它們(或者在您的情況下將它們序列化為JSON)。
我將在下面列出可能的解決方案代碼。
如果您使用maven,這些將是您的依賴項:
<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>
創建一個類HibernateAwareObjectMapper並在其中注冊Hibernate4Module。
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());
}
}
如果您使用的是基於spring bean xml的配置,則可以使用以下內容:
<?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>
否則如果你在Spring啟動時使用基於純Java的配置,你可以這樣做:
@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);
}
}
然后您的控制器看起來像這樣:
@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
}
此外,如果您仍希望傳遞實體的延遲加載屬性,則在您的服務/ DAO層或由@Transactional注釋的層中
你可以這樣做:
Hibernate.initialize(myEntity.getLazyLoadedAttribute());
我希望這有幫助 :)
如果我的回答可以幫助你,請做upvote並將其標記為答案:)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.