0

I use PostgreSQL and I have these tables, product and product_media with relation OneToMany on product with product_media. I want to retrieve a list with product which each of them contains a list of product_media. And I have two options in my mind in order to retrieve them from DB.

First solution is initially retrieve the list of product and then iterate the retrieved list and execute query in order to retrieve the list of product_media.

Query1:

select * from product as p where p.status=1;

Retrieve List and then iterate this list and execute this query:

select * from product_media as pm where pm.product_id=?

Second is to implement join in query and retrieve all data from my DB. Query:

select * from product as p Join product_media as pm on (p.id=pm.product_id)

Retrieve a complex list with all data. The problem of second option is to do not know an elegant way to map this list into an object which has the format below. Do you know how can map automatically the results into this format?

product:[
    {
       id:1,
       name:'Pro1',
       medias:[
         {
            id:1,
            uuid:'asdfi-asdf-rg-fgsdf-do'
         },
         {
            id:2,
            uuid:'asdfi-asdf-rg-fgsdf-do'
         }    
       ]
    },
    {
       id:2,
       name:'Pro2',
       medias:[
         {
            id:5,
            uuid:'asdfi-asdf-rg-fgsdf-do'
         },
         {
             id:7,
             uuid:'asdfi-asdf-rg-fgsdf-do'
         }    
       ]
    }
]
pik4
  • 1,123
  • 1
  • 18
  • 46
  • You're using JPA, so you should simply have a OneToMany association, and use a **JPQL** (not SQL) query, with a join fetch.http://docs.jboss.org/hibernate/orm/5.1/userguide/html_single/Hibernate_User_Guide.html#fetching-strategies-dynamic-fetching – JB Nizet Jun 11 '16 at 20:38
  • If you're going to use JPA, then you should ... well ... you should use JPA. And where in your problem did you say you wanted JSON in there somewhere? – scottb Jun 12 '16 at 06:56
  • From the response posted I could see you've many to many relationship – Saravana Jun 12 '16 at 08:55

2 Answers2

2

I think the second variant is the better option. After fetching the object tree from the database you can do something like the following to achieve what you are posted above:

  1. Assuming your entities are defined as follows:

    Product.java

    public class Product {
    
        private long id;
        private String name;
        private List<ProductMedia> mediaList;
    
    
        public Product() {
            mediaList = new ArrayList<ProductMedia>();
        }
    
        public Product(long id, String name) {
            this.id = id;
            this.name = name;
            mediaList = new ArrayList<ProductMedia>();
        }
        // getters + setters
    }
    

    ProductMedia.java

    public class ProductMedia {
        private long id;
        private String uuid;
    
        public ProductMedia() { }
    
        public ProductMedia(long id, String uuid) {
            this.uuid = uuid;
        }
        // getters + setters
    }
    
  2. Using the Jackson library you can generate output as follows:

    public class JsonTest {
    
        public static void main(String[] args) throws IOException {
    
            ObjectMapper mapper = new ObjectMapper();
    
            mapper.enable(SerializationFeature.INDENT_OUTPUT);
    
            Product prod = new Product(1, "p1");
    
            ProductMedia pm = new ProductMedia(1, "uuid1");
            ProductMedia pm2 = new ProductMedia(2, "uuid2");
    
            prod.getMediaList().add(pm);
            prod.getMediaList().add(pm2);
    
    
            Product prod1 = new Product(2, "p2");
    
            ProductMedia pm3 = new ProductMedia(3, "uuid3");
            ProductMedia pm4 = new ProductMedia(4, "uuid4");
    
            prod1.getMediaList().add(pm3);
            prod1.getMediaList().add(pm4);
    
            Product[] pList = {prod, prod1};
    
            mapper.writeValue(System.out, pList);
        }
    }
    

    In this example, I am writing the output onto the console. But you are not restricted to it; you can write to a file passing in a FileOutputStream.

To be able to run this example you need to add the dependency; if you use Maven you can add the following into your POM:

    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.7.4</version>
    </dependency>

Otherwise add the jar of the dependency into your project build path.

ujulu
  • 3,154
  • 2
  • 9
  • 14
  • I think that you are eight but how can iterate retrieved data? using for and if statements? – pik4 Jun 12 '16 at 09:36
  • Why do you want to iterate? If the objects are mapped correctly you don't need to do iterations. The framework will do it for you. As you see in the example I did the mapping myself; but if the mapping is done correctly the persistence provider will return a product properly nested with the children. – ujulu Jun 12 '16 at 10:06
  • I have this list which each object has attributes of each tuple and then I should iterate in order to get the Json Object with all data. List productTests = productRepository.getProductTest(); – pik4 Jun 12 '16 at 10:08
  • The SQL query return tuples with these attributes: product_id, product_name, media_id, media_uuid – pik4 Jun 12 '16 at 10:10
  • Your query returns a list of `product` objects and each element holds a list of `media` objects, right? – ujulu Jun 12 '16 at 10:12
  • In PostgreSQL you can retrieve tuples. for example you have these data: product_id, product_name, media_id, media_uuid \n 1 , 'prod 1', 1, 'asdf-asdf-asf',\n 1 , 'prod 1', 2, 'asdf-asdf-asf',\n 2 , 'prod 2', 1, 'asdf-asdf-asf',\n 3 , 'prod 1', 1, 'asdf-asdf-asf',\n 3 , 'prod 1', 2, 'asdf-asdf-asf' – pik4 Jun 12 '16 at 10:14
  • Are you using JPA or you chose a wrong tag? If you are using JPA you don't need to do that. Just put some annotations on your entities and the provider will do all the work for you. If you need help leave me a comment and I will edit the entities. – ujulu Jun 12 '16 at 10:19
  • Ok. I am using JPA but in some cases JPA execute more queries than are needed and I am trying to find more elegant solution. Then I want to retrieve all relevant tuples from DB and map them in specific object. – pik4 Jun 12 '16 at 10:23
  • That means you want to use JDBC directly. If that is what you want you have to do the mapping manually in a loop over the resultset and do the mapping as I did in my example above yourself. But I don't think that you want to do it. But I myself would not do it if that is not absolutely necessary. – ujulu Jun 12 '16 at 10:32
  • I think that is faster and more elegant to do it myself. – pik4 Jun 12 '16 at 10:37
  • You can check this question. http://stackoverflow.com/questions/37876807/sql-results-mapping-to-json-spring?noredirect=1#comment63222081_37876807 – pik4 Jun 18 '16 at 07:52
  • Faster probably, but elegant I don't believe. Rather ugly code; otherwise frameworks like Hibernate, and generally, ORM frameworks would have been needles. It is up to you what you want to use. Hey, you are not obliged to use my solution; I just wanted to help you because I thought you are experiencing a problem. – ujulu Jun 18 '16 at 08:20
2

If your response is not in json format you can try below

There is a many-to-many relationship between Product and Media.

Product_Media is a helper table to maintain many-to-many relationship between Product and Media entities.

Product entity:

@Entity(name = "product")
public class Product {

    @Id
    @GeneratedValue
    private Long product_id;

    @Column
    private String name;

    @ManyToMany(cascade = { CascadeType.MERGE }, fetch = FetchType.EAGER)
    @JoinTable(name = "product_media", joinColumns = {
            @JoinColumn(name = "product_id", table = "product") }, inverseJoinColumns = {
                    @JoinColumn(name = "media_id", table = "media") })
    List<Media> medias;

}

Media entity

@Entity(name = "media")
public class Media {

    @Id
    @GeneratedValue
    private Long media_id;

    @Column
    private String name;

}

SQL generated by Hibernate

select
    product0_.product_id as product_1_1_0_,
    product0_.name as name2_1_0_,
    medias1_.product_id as product_1_1_1_,
    media2_.media_id as media_id2_2_1_,
    media2_.media_id as media_id1_0_2_,
    media2_.name as name2_0_2_ 
from
    product product0_ 
left outer join
    product_media medias1_ 
        on product0_.product_id=medias1_.product_id 
left outer join
    media media2_ 
        on medias1_.media_id=media2_.media_id 
where
    product0_.product_id=?

If the relationship is one-to-many, change entities like below

Media Entity

@Entity(name = "media")
public class Media {

    @Id
    @GeneratedValue
    private Long id;

    @Column
    private String name;

    @ManyToOne
    @JoinColumn(name = "product_id", referencedColumnName = "id", nullable = false, updatable = false)
    private Product product;

    public Media() {

    }
}

Product Entity

@Entity(name = "product")
public class Product {

    @Id
    @GeneratedValue
    private Long id;

    @Column
    private String name;

    @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "product")
    List<Media> medias;
}

Hibernate generated SQL

select
    product0_.id as id1_2_0_,
    product0_.name as name2_2_0_,
    medias1_.product_id as product_3_2_1_,
    medias1_.id as id1_0_1_,
    medias1_.id as id1_0_2_,
    medias1_.name as name2_0_2_,
    medias1_.product_id as product_3_0_2_ 
from
    product product0_ 
left outer join
    media medias1_ 
        on product0_.id=medias1_.product_id 
where
    product0_.id=?
Saravana
  • 11,085
  • 2
  • 29
  • 43
  • I have these entities now. But for each product should execute a query to retrieve all media which are connected with Product. – pik4 Jun 12 '16 at 08:47
  • use 2nd option, SQL with joins, if you use the above code, it will create a SQL with joins and execute – Saravana Jun 12 '16 at 09:45
  • Yes buy my query is: select p.id as id, p.name_el as name_el, pm.id as media_id, pm.multimedia_path as multimedia_path from product as p join product_multimedia as pm on (p.id=pm.product_id) and I want to map the results into Product.class – pik4 Jun 12 '16 at 11:59
  • Pro1 and Pro2 have the same media, are you sure the relationship is one-to-many, I believe it's many-to-many – Saravana Jun 12 '16 at 12:20
  • I am sure that is oneToMany relation. It's an example – pik4 Jun 12 '16 at 12:27
  • You can check this question. http://stackoverflow.com/questions/37876807/sql-results-mapping-to-json-spring?noredirect=1#comment63222081_37876807 – pik4 Jun 18 '16 at 07:52