I have this main Product
table:
@Table(name = "product") public class Product implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", unique = true, updatable = false, nullable = false) private int id; @Column(name = "user_id", length = 20) private Integer userId; @Column(name = "title", length = 75) private String title; @Column(name = "meta_title", length = 100) private String metaTitle; @Column(name = "status", length = 100) private String status; }
Additional table for storing categories that should be returned as List:
@Table(name = "product_category") public class ProductCategory implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", unique = true, updatable = false, nullable = false) private int id; @Column(name = "product_id", length = 4) private Integer productId; @Column(name = "category_id", length = 20) private Integer categoryId; }
Additional table for storing Payment Methods that should be returned as List:
@Table(name = "product_payment_methods") public class ProductPaymentMethods implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", unique = true, updatable = false, nullable = false) private int id; @Column(name = "product_id", length = 20) private Integer productId; @Column(name = "payment_methods", length = 20000) private String paymentMethods; }
I want to return a result like this:
id | title | categoryId | paymentMethods | 1 | test | 34, 43 | 345, 7, 5 | 5 | test2 | 64,5, 3 | 654, 3, 5 |
I tried this:
SELECT * FROM Product INNER JOIN product_category ON Product.id = product_category.productId INNER JOIN product_payment_methods ON Product.id = product_payment_methods.productId WHERE userId = 1
What is the proper way to populate this DTO?
public class ProductFullDTO { private int id; private Integer userId; private List<Integer> categories; private List<String> paymentMethods; }
Advertisement
Answer
If, as indicated in your comments, you need query your information with HQL a good way to proceed can be the following.
First, modify your Product
entity an include relationships for both ProductCategory
and ProductPaymentMethods
, something like:
@Table(name = "product") public class Product implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", unique = true, updatable = false, nullable = false) private int id; @Column(name = "user_id", length = 20) private Integer userId; @Column(name = "title", length = 75) private String title; @Column(name = "meta_title", length = 100) private String metaTitle; @Column(name = "status", length = 100) private String status; @OneToMany(mappedBy = "product", cascade = CascadeType.ALL, orphanRemoval = true) private List<ProductCategory> categories; @OneToMany(mappedBy = "product", cascade = CascadeType.ALL, orphanRemoval = true) private List< ProductPaymentMethods> paymentMethods; // Setters and getters, omitted for brevity }
Modify both ProductCategory
and ProductPaymentMethods
to accommodate the entities relationship:
@Table(name = "product_category") public class ProductCategory implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", unique = true, updatable = false, nullable = false) private int id; // Please, feel free to change the insertable and updatable attributes to // fit your needs @Column(name = "product_id", length = 4, insertable=false, updatable=false) private Integer productId; @ManyToOne(fetch= FetchType.LAZY) @JoinColumn(name="product_id") private Product product; @Column(name = "category_id", length = 20) private Integer categoryId; // Setters and getters, omitted for brevity } @Table(name = "product_payment_methods") public class ProductPaymentMethods implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", unique = true, updatable = false, nullable = false) private int id; // Please, feel free to change the insertable and updatable attributes to // fit your needs. By the way, why here the length is 20 and not 4? @Column(name = "product_id", length = 20, insertable=false, updatable=false) private Integer productId; @ManyToOne(fetch= FetchType.LAZY) @JoinColumn(name="product_id") private Product product; @Column(name = "payment_methods", length = 20000) private String paymentMethods; }
With this setup, as you can see in the Hibernate documentation – it is for an old Hibernate version, but it is correct today, you can use fetch joins to obtain the required information:
A “fetch” join allows associations or collections of values to be initialized along with their parent objects using a single select. This is particularly useful in the case of a collection.
For your example, consider the following HQL (assume outer join semantics, modify it as appropriate):
select product from Product as product left join fetch product.categories left join fetch product.paymentMethods where product.userId = 1
This will provide you the list of products for userId
1
, with all the associated references to categories and payment methods initialized.
The conversion between the entity and the DTO should be straightforward:
Session session = ... List<Product> products = session.createQuery( "select product " + "from Product as product " + " left join fetch product.categories " + " left join fetch product.paymentMethods " + "where product.userId = :userId", Product.class) .setParameter( "userId", 1) .getResultList(); List<ProductFullDTO> productFullDTOs = null; if (products != null) { productFullDTOs = products.stream() .map((product -> { ProductFullDTO productFullDTO = new ProductFullDTO(); productFullDTO.setId(product.getId()); productFullDTO.setUserId(product.getUserId()); List<ProductCategory> categories = product.getCategories(); if (categories != null) { List<Integer> categoriesIds = categories.stream() .map(ProductCategory::getCategoryId) .collect(Collectors.toList()) ; productFullDTO.setCategories(categoriesIds); } List<ProductPaymentMethods> paymentMethods = product.getPaymentMethods(); if (paymentMethods != null) { List<String> paymentMethodsIds = paymentMethods.stream() .map(ProductPaymentMethods::getPaymentMethods) .collect(Collectors.toList()) ; productFullDTO.setPaymentMethods(paymentMethodsIds); } return productFullDTO; })) .collect(Collectors.toList()) ; } System.out.println(productFullDTOs == null ? "No products found." : productFullDTOs.size() + " products found.");