我正在开发一个spring boot 2.0/Java 8
购物车在线应用程序。我使用Hibernate
作为ORM
框架。我有两个实体,Order
和OrderDetail
,如下所示:
@Entity
@Table(name = "orders")
public class Order extends AbstractEntityUuid {
@Column(name = "order_number", unique = true)
private String orderNumber;
@JsonBackReference
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private User user;
@Column(name = "total_amount")
private BigDecimal totalAmount = BigDecimal.ZERO;
@CreatedDate
@Column(name = "created_on", columnDefinition = "DATETIME", updatable = false)
protected LocalDateTime created;
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
@JsonManagedReference
private Set<OrderDetail> items = new HashSet<>();
@OneToOne(fetch = FetchType.LAZY, orphanRemoval = true)
@JoinColumn(nullable = false, name = "card_details_id")
private CardDetails card;
@OneToOne(fetch = FetchType.LAZY, orphanRemoval = true)
@JoinColumn(nullable = false, name = "shipping_address_id")
private Address shippingAddress;
@OneToOne(fetch = FetchType.LAZY, orphanRemoval = true)
@JoinColumn(name = "billing_address_id")
private Address billingAddress;
//getters and setters
}
@Entity
@Table(name = "order_detail")
public class OrderDetail extends AbstractPersistable<Long> {
@Column(name = "quantity")
private Integer quantity;
@Column(name = "total_amount")
private BigDecimal totalAmount = BigDecimal.ZERO;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "order_id", nullable = false)
@JsonBackReference
private Order order;
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "product_id", nullable = false)
private Product product;
//getters and setters
}
当用户转向他的订单时,他应该能够看到仅与订单本身相关的信息(没有详细信息)。因此,我只从order表中检索数据。以下是我的存储库:
public interface OrderRepository extends CrudRepository<Order, Long> {
@Query("FROM Order o WHERE o.user.email = ?1")
List<Order> findOrdersByUser(String email);
}
在服务中,我所做的只是调用上面的方法并将其转换为dto对应方。
@Transactional(readOnly = true)
public List<OrdersPreviewDTO> getOrdersPreview(String email) {
List<Order> orders = orderRepository.findOrdersByUser(email);
return orderConverter.convertToOrderPreviewDTOs(orders);
}
转换器在引擎盖下使用了一个jackson的objectmapper
对象。
List<OrdersPreviewDTO> convertToOrderPreviewDTOs(List<Order> orders) {
return orders.stream()
.map(o -> objectMapper.convertValue(o, OrdersPreviewDTO.class))
.collect(toList());
}
ObjectMapper
由spring
注入,并在配置类中定义:
@Bean
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.findAndRegisterModules();
return objectMapper;
}
OrderspReviewDTO
dto对象只包含Order实体的一个子集,因为正如我已经提到的,在Orders页面中,我只想显示用户订单的高级属性,而不是与其详细信息相关的属性。
@JsonIgnoreProperties(ignoreUnknown = true)
public class OrdersPreviewDTO {
private String orderNumber;
@JsonFormat(pattern = "dd/MM/yyyy HH:mm")
private LocalDateTime created;
private BigDecimal totalAmount;
@JsonCreator
public OrdersPreviewDTO(
@JsonProperty("orderNumber") String orderNumber,
@JsonProperty("created") LocalDateTime created,
@JsonProperty("totalAmount") BigDecimal totalAmount) {
this.orderNumber = orderNumber;
this.created = created;
this.totalAmount = totalAmount;
}
//getters and setters
}
一切工作正常,订单实体自动转换为它的dto对应的jackson。当查看Hibernate在引擎盖下执行的查询时,问题就出来了。Hibernate打开每个订单的订单详细信息集合,并执行一个查询以从每个子集合检索数据:
select carddetail0_.id as id1_2_0_, carddetail0_.brand as brand2_2_0_, carddetail0_.created as created3_2_0_, carddetail0_.exp_month as exp_mont4_2_0_, carddetail0_.exp_year as exp_year5_2_0_, carddetail0_.last4 as last6_2_0_ from card_details carddetail0_ where carddetail0_.id=?
select address0_.id as id1_1_0_, address0_.created as created2_1_0_, address0_.last_modified as last_mod3_1_0_, address0_.city as city4_1_0_, address0_.country as country5_1_0_, address0_.first_name as first_na6_1_0_, address0_.last_name as last_nam7_1_0_, address0_.postal_code as postal_c8_1_0_, address0_.state as state9_1_0_, address0_.street_address as street_10_1_0_, address0_.telephone as telepho11_1_0_ from address address0_ where address0_.id=?
select items0_.order_id as order_id4_4_0_, items0_.id as id1_4_0_, items0_.id as id1_4_1_, items0_.order_id as order_id4_4_1_, items0_.product_id as product_5_4_1_, items0_.quantity as quantity2_4_1_, items0_.total_amount as total_am3_4_1_ from order_detail items0_ where items0_.order_id=?
还有更多的人。
无论我以以下方式修改代码,Hibernate都只在Order表上运行预期的查询:
这行代码:
objectMapper.convertValue(o, OrdersPreviewDTO.class)
被以下脏修复替换:
new OrdersPreviewDTO(o.getOrderNumber(), o.getCreated(), o.getTotalAmount())
由Hibernate运行的查询:
select order0_.id as id1_5_, order0_.billing_address_id as billing_6_5_, order0_.card_details_id as card_det7_5_, order0_.created_on as created_2_5_, order0_.one_address as one_addr3_5_, order0_.order_number as order_nu4_5_, order0_.shipping_address_id as shipping8_5_, order0_.total_amount as total_am5_5_, order0_.user_id as user_id9_5_
from orders order0_ cross join user user1_
where order0_.user_id=user1_.id and user1_.user_email=?
我的问题是。有没有一种方法告诉jackson只映射Dtos字段,这样它就不会通过Hibernate触发非必需字段的惰性加载提取?
谢谢
埃塞克斯郡男孩回答+1。我只想补充一点,您可以直接从您的JPQL查询返回DTO而不是使用jackson。它避免了从数据库到对象order
的转换,然后又从order
对象到orderspreviewdto
对象的转换。
例如,您需要更改存储库中的查询来完成此操作。这可能是这样的:
public interface OrderRepository extends CrudRepository<Order, Long> {
@Query("SELECT new OrdersPreviewDTO(o.order_number, o.created_on, o.total_amount)) FROM Order o WHERE o.user.email = ?1")
List<OrdersPreviewDTO> findOrdersByUser(String email);
}
简短的答案是否定的,不要尝试和这么聪明,手动创建您的DTO来控制任何懒散加载,然后在事务外部的DTO上使用jackson。
很长的答案是肯定的,您可以覆盖MappingJackson2HttpMessageConverter并控制从实体调用哪些字段。
@Configuration
public class MixInWebConfig extends WebMvcConfigurationSupport {
@Bean
public MappingJackson2HttpMessageConverter customJackson2HttpMessageConverter2() {
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.addMixIn(DTO1.class, FooMixIn.class);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
jsonConverter.setObjectMapper(objectMapper);
return jsonConverter;
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(customJackson2HttpMessageConverter2());
}
}
然后
@Override
protected void processViews(SerializationConfig config, BeanSerializerBuilder builder) {
super.processViews(config, builder);
if (classes.contains(builder.getBeanDescription().getBeanClass())) {
List<BeanPropertyWriter> originalWriters = builder.getProperties();
List<BeanPropertyWriter> writers = new ArrayList<BeanPropertyWriter>();
for (BeanPropertyWriter writer : originalWriters) {
String propName = writer.getName();
if (!fieldsToIgnore.contains(propName)) {
writers.add(writer);
}
}
builder.setProperties(writers);
}
}
}
这里有一个工作示例。
问题内容: 包括: all Spring libs, Apache Tomcat 7.0 library 在构建路径中 但它仍然给出错误: 在“ org.sprintframework.web-3.1.0.M1.jar”中,我可以看到“ org.springframework.web.context.ContextLoaderListener”。 Google上的某个人说应该包含spring.ja
问题内容: 我使用非常简单的代码返回XML 但是,出现以下错误 请帮忙。谢谢 问题答案: 运行时出现NoSuchMethodError表示你使用的库版本与生成代码所针对的版本不同。 在你的情况下,Spring是元凶。在运行时检查类路径上的内容,并确保以下各项: 版本与编译时间罐相同 如果存在多个版本,请删除不需要的版本
问题内容: 我不明白注释和之间的实际区别是什么? 扩展名还是它们具有完全不同的含义?什么时候应该使用它们?在服务层中使用Spring ,在DAO 中使用javax? 谢谢回答。 问题答案: 几年前,Spring定义了自己的Transactional注释以使Spring bean方法具有事务性。 Java EE 7终于做了同样的事情,现在除了EJB方法外,还允许CDI bean方法是事务性的。因此,
我在CentOS虚拟机中安装了RabbitMQ,该虚拟机的网络适配器被定义为Bridge。我正在尝试配置RabbitMQ管理,以便通过机器的IP地址访问WebApp。配置如下:
这个FAQ的最新版本总是可以从Apache主站点得到,位于<http://httpd.apache.org/docs/2.2/faq/> 如果你的问题在这里没有找到答案,你也可以看看Apache 1.3 FAQ ,看你的问题是否在那里有了答案。 主题 背景 关于 Apache HTTP Server 的背景知识。 支持 我遇到问题该怎么办? 错误信息 这些错误信息是什么意思? 背景 什么是Apac
发布问题 更新问题 设置问题悬赏 获取问题列表 获取一个问题详情 删除一个问题 获取用户发布的问题列表 发布问题 POST /questions 输入 字段 类型 描述 subject 字符串 必须,问题主题或者说标题,不能超过 255 字节 ,必须以 ? 结尾。(不区分全角或者半角) topics 数组 必须,绑定的话题,数组子节点必须符合 { "id": 1 } 的格式。 body 字符串
问题内容: 我简直不敢相信我网站上正在发生的事情。当我添加此行时: 一切正常。如果我不这样做,CSS就会“混乱”,一切都会变得不同,布局也会变得“丑陋”。 这条线如何解决所有问题? 问题答案: 您正在将HTML与XHTML混合使用。 通常,声明用于区分HTMLish语言的版本(在这种情况下为HTML或XHTML)。 不同的标记语言将表现不同。我最喜欢的例子是。在浏览器中查看以下内容: XHTML
我试图在fabric rocket chat上联系,但没有得到太多帮助,因此在SO上发布了它。我有以下疑问: 我们是否可以在链码内访问块高度(我知道这在客户端是可行的,但在链码内是否可能) 可以从链码中的正在进行的事务调用新事务吗? 想知道hyperledger Fabric中存储的数据的历史记录在哪里 我们可以根据链码中的transactionid进行查询吗? 在fabric链码中编写调度程序是