[英]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.