Jackson ObjectMapper Hibernate issue
I am working on a Spring Boot 2.0 / Java 8
shopping cart online application. I use Hibernate
as the ORM
framework.
I have two entities, Order
and OrderDetail
shown below:
@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
}
When the user heads over to his Orders, he should be able to see information related only to the order itself (no details).
For that reason, I retrieve data only from the order table. Following is my repository:
public interface OrderRepository extends CrudRepository<Order, Long> {
@Query("FROM Order o WHERE o.user.email = ?1")
List<Order> findOrdersByUser(String email);
}
In the service, what I do is simply calling the above method and converting to a the dto counterpart.
@Transactional(readOnly = true)
public List<OrdersPreviewDTO> getOrdersPreview(String email) {
List<Order> orders = orderRepository.findOrdersByUser(email);
return orderConverter.convertToOrderPreviewDTOs(orders);
}
The converter uses an Jackson ObjectMapper
object under the hood.
List<OrdersPreviewDTO> convertToOrderPreviewDTOs(List<Order> orders) {
return orders.stream()
.map(o -> objectMapper.convertValue(o, OrdersPreviewDTO.class))
.collect(toList());
}
The objectMapper
is inject by Spring
and defined in a configuration class:
@Bean
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.findAndRegisterModules();
return objectMapper;
}
The OrdersPreviewDTO
dto object contains just a subset of the Order entity, because as I already mentioned, in the Orders page I want to show only high level properties of the user's orders, and nothing related to their details.
@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
}
Everything works fine, the order entity is converted automagically by Jackson into its dto counterpart.
The problem come out when looking at the query executed by Hibernate under the hood.
Hibernate unwrap the collection of order details for each order and execute a query to retrieve data from each child collection:
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=?
and tons of others more.
Whether I modify the code in the following way, Hibernate runs only the expected query on the Order table:
This line of code:
objectMapper.convertValue(o, OrdersPreviewDTO.class)
is replaced by the following dirty fix:
new OrdersPreviewDTO(o.getOrderNumber(), o.getCreated(), o.getTotalAmount())
Query run by 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=?
My question is. Is there a way to tell Jackson to map only the Dtos field so that it doesn't trigger lazy loading fetches through Hibernate for the non required fields?
Thank you
java spring hibernate spring-boot jackson
add a comment |
I am working on a Spring Boot 2.0 / Java 8
shopping cart online application. I use Hibernate
as the ORM
framework.
I have two entities, Order
and OrderDetail
shown below:
@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
}
When the user heads over to his Orders, he should be able to see information related only to the order itself (no details).
For that reason, I retrieve data only from the order table. Following is my repository:
public interface OrderRepository extends CrudRepository<Order, Long> {
@Query("FROM Order o WHERE o.user.email = ?1")
List<Order> findOrdersByUser(String email);
}
In the service, what I do is simply calling the above method and converting to a the dto counterpart.
@Transactional(readOnly = true)
public List<OrdersPreviewDTO> getOrdersPreview(String email) {
List<Order> orders = orderRepository.findOrdersByUser(email);
return orderConverter.convertToOrderPreviewDTOs(orders);
}
The converter uses an Jackson ObjectMapper
object under the hood.
List<OrdersPreviewDTO> convertToOrderPreviewDTOs(List<Order> orders) {
return orders.stream()
.map(o -> objectMapper.convertValue(o, OrdersPreviewDTO.class))
.collect(toList());
}
The objectMapper
is inject by Spring
and defined in a configuration class:
@Bean
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.findAndRegisterModules();
return objectMapper;
}
The OrdersPreviewDTO
dto object contains just a subset of the Order entity, because as I already mentioned, in the Orders page I want to show only high level properties of the user's orders, and nothing related to their details.
@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
}
Everything works fine, the order entity is converted automagically by Jackson into its dto counterpart.
The problem come out when looking at the query executed by Hibernate under the hood.
Hibernate unwrap the collection of order details for each order and execute a query to retrieve data from each child collection:
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=?
and tons of others more.
Whether I modify the code in the following way, Hibernate runs only the expected query on the Order table:
This line of code:
objectMapper.convertValue(o, OrdersPreviewDTO.class)
is replaced by the following dirty fix:
new OrdersPreviewDTO(o.getOrderNumber(), o.getCreated(), o.getTotalAmount())
Query run by 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=?
My question is. Is there a way to tell Jackson to map only the Dtos field so that it doesn't trigger lazy loading fetches through Hibernate for the non required fields?
Thank you
java spring hibernate spring-boot jackson
add a comment |
I am working on a Spring Boot 2.0 / Java 8
shopping cart online application. I use Hibernate
as the ORM
framework.
I have two entities, Order
and OrderDetail
shown below:
@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
}
When the user heads over to his Orders, he should be able to see information related only to the order itself (no details).
For that reason, I retrieve data only from the order table. Following is my repository:
public interface OrderRepository extends CrudRepository<Order, Long> {
@Query("FROM Order o WHERE o.user.email = ?1")
List<Order> findOrdersByUser(String email);
}
In the service, what I do is simply calling the above method and converting to a the dto counterpart.
@Transactional(readOnly = true)
public List<OrdersPreviewDTO> getOrdersPreview(String email) {
List<Order> orders = orderRepository.findOrdersByUser(email);
return orderConverter.convertToOrderPreviewDTOs(orders);
}
The converter uses an Jackson ObjectMapper
object under the hood.
List<OrdersPreviewDTO> convertToOrderPreviewDTOs(List<Order> orders) {
return orders.stream()
.map(o -> objectMapper.convertValue(o, OrdersPreviewDTO.class))
.collect(toList());
}
The objectMapper
is inject by Spring
and defined in a configuration class:
@Bean
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.findAndRegisterModules();
return objectMapper;
}
The OrdersPreviewDTO
dto object contains just a subset of the Order entity, because as I already mentioned, in the Orders page I want to show only high level properties of the user's orders, and nothing related to their details.
@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
}
Everything works fine, the order entity is converted automagically by Jackson into its dto counterpart.
The problem come out when looking at the query executed by Hibernate under the hood.
Hibernate unwrap the collection of order details for each order and execute a query to retrieve data from each child collection:
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=?
and tons of others more.
Whether I modify the code in the following way, Hibernate runs only the expected query on the Order table:
This line of code:
objectMapper.convertValue(o, OrdersPreviewDTO.class)
is replaced by the following dirty fix:
new OrdersPreviewDTO(o.getOrderNumber(), o.getCreated(), o.getTotalAmount())
Query run by 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=?
My question is. Is there a way to tell Jackson to map only the Dtos field so that it doesn't trigger lazy loading fetches through Hibernate for the non required fields?
Thank you
java spring hibernate spring-boot jackson
I am working on a Spring Boot 2.0 / Java 8
shopping cart online application. I use Hibernate
as the ORM
framework.
I have two entities, Order
and OrderDetail
shown below:
@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
}
When the user heads over to his Orders, he should be able to see information related only to the order itself (no details).
For that reason, I retrieve data only from the order table. Following is my repository:
public interface OrderRepository extends CrudRepository<Order, Long> {
@Query("FROM Order o WHERE o.user.email = ?1")
List<Order> findOrdersByUser(String email);
}
In the service, what I do is simply calling the above method and converting to a the dto counterpart.
@Transactional(readOnly = true)
public List<OrdersPreviewDTO> getOrdersPreview(String email) {
List<Order> orders = orderRepository.findOrdersByUser(email);
return orderConverter.convertToOrderPreviewDTOs(orders);
}
The converter uses an Jackson ObjectMapper
object under the hood.
List<OrdersPreviewDTO> convertToOrderPreviewDTOs(List<Order> orders) {
return orders.stream()
.map(o -> objectMapper.convertValue(o, OrdersPreviewDTO.class))
.collect(toList());
}
The objectMapper
is inject by Spring
and defined in a configuration class:
@Bean
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.findAndRegisterModules();
return objectMapper;
}
The OrdersPreviewDTO
dto object contains just a subset of the Order entity, because as I already mentioned, in the Orders page I want to show only high level properties of the user's orders, and nothing related to their details.
@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
}
Everything works fine, the order entity is converted automagically by Jackson into its dto counterpart.
The problem come out when looking at the query executed by Hibernate under the hood.
Hibernate unwrap the collection of order details for each order and execute a query to retrieve data from each child collection:
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=?
and tons of others more.
Whether I modify the code in the following way, Hibernate runs only the expected query on the Order table:
This line of code:
objectMapper.convertValue(o, OrdersPreviewDTO.class)
is replaced by the following dirty fix:
new OrdersPreviewDTO(o.getOrderNumber(), o.getCreated(), o.getTotalAmount())
Query run by 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=?
My question is. Is there a way to tell Jackson to map only the Dtos field so that it doesn't trigger lazy loading fetches through Hibernate for the non required fields?
Thank you
java spring hibernate spring-boot jackson
java spring hibernate spring-boot jackson
asked Nov 13 '18 at 9:47
ShaunylShaunyl
182111
182111
add a comment |
add a comment |
3 Answers
3
active
oldest
votes
The short answer is no, don't try and be so clever, manually create your DTO to control any lazy loading, then use Jackson on the DTO outside the transaction.
The Long answer is yes, you can override MappingJackson2HttpMessageConverter and control which fields get called from the entity.
@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());
}
}
Then
@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);
}
}
}
here is a working example.
add a comment |
+1 for Essex Boy answer. I just want to add that you can directly return DTO from your JPQL Queries instead of using Jackson. It avoids a transformation from the database to your object Order
and then another transformation from Order
object to OrdersPreviewDTO
object.
For example, you need to change your query in your repository to do it. It would be something like :
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);
}
add a comment |
If OrdersPreviewDTO
is strictly a subset of your Order
class, why not simply use the @JsonView
annotation to automatically create a simple view in your controller? See https://spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring for example.
In case you need a DTO for both input and output, also consider using http://mapstruct.org/
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53278128%2fjackson-objectmapper-hibernate-issue%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
The short answer is no, don't try and be so clever, manually create your DTO to control any lazy loading, then use Jackson on the DTO outside the transaction.
The Long answer is yes, you can override MappingJackson2HttpMessageConverter and control which fields get called from the entity.
@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());
}
}
Then
@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);
}
}
}
here is a working example.
add a comment |
The short answer is no, don't try and be so clever, manually create your DTO to control any lazy loading, then use Jackson on the DTO outside the transaction.
The Long answer is yes, you can override MappingJackson2HttpMessageConverter and control which fields get called from the entity.
@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());
}
}
Then
@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);
}
}
}
here is a working example.
add a comment |
The short answer is no, don't try and be so clever, manually create your DTO to control any lazy loading, then use Jackson on the DTO outside the transaction.
The Long answer is yes, you can override MappingJackson2HttpMessageConverter and control which fields get called from the entity.
@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());
}
}
Then
@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);
}
}
}
here is a working example.
The short answer is no, don't try and be so clever, manually create your DTO to control any lazy loading, then use Jackson on the DTO outside the transaction.
The Long answer is yes, you can override MappingJackson2HttpMessageConverter and control which fields get called from the entity.
@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());
}
}
Then
@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);
}
}
}
here is a working example.
answered Nov 13 '18 at 10:31
Essex BoyEssex Boy
4,4481815
4,4481815
add a comment |
add a comment |
+1 for Essex Boy answer. I just want to add that you can directly return DTO from your JPQL Queries instead of using Jackson. It avoids a transformation from the database to your object Order
and then another transformation from Order
object to OrdersPreviewDTO
object.
For example, you need to change your query in your repository to do it. It would be something like :
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);
}
add a comment |
+1 for Essex Boy answer. I just want to add that you can directly return DTO from your JPQL Queries instead of using Jackson. It avoids a transformation from the database to your object Order
and then another transformation from Order
object to OrdersPreviewDTO
object.
For example, you need to change your query in your repository to do it. It would be something like :
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);
}
add a comment |
+1 for Essex Boy answer. I just want to add that you can directly return DTO from your JPQL Queries instead of using Jackson. It avoids a transformation from the database to your object Order
and then another transformation from Order
object to OrdersPreviewDTO
object.
For example, you need to change your query in your repository to do it. It would be something like :
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);
}
+1 for Essex Boy answer. I just want to add that you can directly return DTO from your JPQL Queries instead of using Jackson. It avoids a transformation from the database to your object Order
and then another transformation from Order
object to OrdersPreviewDTO
object.
For example, you need to change your query in your repository to do it. It would be something like :
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);
}
answered Nov 13 '18 at 11:11
victor galletvictor gallet
1,114716
1,114716
add a comment |
add a comment |
If OrdersPreviewDTO
is strictly a subset of your Order
class, why not simply use the @JsonView
annotation to automatically create a simple view in your controller? See https://spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring for example.
In case you need a DTO for both input and output, also consider using http://mapstruct.org/
add a comment |
If OrdersPreviewDTO
is strictly a subset of your Order
class, why not simply use the @JsonView
annotation to automatically create a simple view in your controller? See https://spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring for example.
In case you need a DTO for both input and output, also consider using http://mapstruct.org/
add a comment |
If OrdersPreviewDTO
is strictly a subset of your Order
class, why not simply use the @JsonView
annotation to automatically create a simple view in your controller? See https://spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring for example.
In case you need a DTO for both input and output, also consider using http://mapstruct.org/
If OrdersPreviewDTO
is strictly a subset of your Order
class, why not simply use the @JsonView
annotation to automatically create a simple view in your controller? See https://spring.io/blog/2014/12/02/latest-jackson-integration-improvements-in-spring for example.
In case you need a DTO for both input and output, also consider using http://mapstruct.org/
answered Nov 13 '18 at 12:18
mindexmindex
1,279616
1,279616
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53278128%2fjackson-objectmapper-hibernate-issue%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown