Skip to content
Advertisement

Populate DTO using query with JOIN

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.");
7 People found this is helpful
Advertisement