0

how to return more values in GET response?

My tables in database look like this:

user(id, login, password)
question(id, user_id, value, created_date, status, first_answer, second_answer)
answer(id, question_id, user_id, value)

For every question there can be only two possible answers. I use POST to answer questions (resource /answers).

Now when I do GET /questions/1, it returns this answer

{
    "id" : 1,
    "value" : "Example",
    "createdDate" : 1495825431000,
    "firstAnswer" : "A",
    "secondAnswer" : "B",
    "status" : "accepted"
}

(without user, because I use @JsonIgnore on this)

Now I'd like to make GET request and get in response more values (for example the login of the user who created the question and two counters that show how many times the first answer and the second answer were selected.

{
    "id" : 1,
    "value" : "Example",
    "createdDate" : 1495825431000,
    "firstAnswer" : "A",
    "secondAnswer" : "B",
    "status" : "accepted",
    "createdBy" : "John",
    "firstCount" : 120,
    "secondCount" : 80
}

How to do it? I'd love to do it with one query in Springboot. For example like this:

SELECT q.id, q.value, q.first_answer, q.second_answer, q.created_date, q.status,
(SELECT COUNT(answer) FROM answer WHERE answer = 1 AND question_id = q.id) AS 'first_count', 
(SELECT COUNT(answer) FROM answer WHERE answer = 2 AND question_id = q.id) AS 'second_count',
(SELECT u.login FROM user u WHERE u.id = q.user_id) AS 'createdBy'
FROM question q

My class with model looks like this:

@Entity
@Table(name = "question")
public class Question {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column
    private Integer id;

    @JsonIgnore
    @ManyToOne
    @JoinColumn(nullable = false)
    private User user;

    @Column(nullable = false)
    private String value;

    @Column(nullable = false)
    private Timestamp createdDate;

    @Column(nullable = false)
    private String firstAnswer;

    @Column(nullable = false)
    private String secondAnswer;

    @Column(nullable = false)
    private String status;

    public Question() {
    }

    //getters, setters, etc. 

}

And my repository class looks like this:

public interface QuestionsRepository extends CrudRepository<Question, Integer> {
}

I don't want to create new rows in database.

I know I can fill @Transient variables in Controller's class. But I don't want to make many database query - I'd like to do it in one query, it's faster.

foxbuur
  • 169
  • 1
  • 9

1 Answers1

0

It is bad practice to return object from database to the client. You should remove annotation @JsonIgnore and you should use DTO, check this link

In DTO you can have as many properties as you want. Also, you can have properties from more than one object.

Lemmy
  • 2,437
  • 1
  • 22
  • 30
  • Ok, I created DTO classes. Could you tell me (of course if it's possible) how can I create objects by using one query (I'd like to use only one SELECT). Now in my Repository class I have methods with native queries, but when I want to return whole collection, it takes 2-3 seconds to create objects and make response. – foxbuur May 29 '17 at 20:31
  • 1
    You can create query which will return DTO you want. For example: @Query("SELECT new ExampleDTO(q.id, q.value, q.first_answer, q.second_answer, q.created_date, q.status," + "(SELECT COUNT(answer) FROM answer WHERE answer = 1 AND question_id = q.id) AS 'first_count'," + "(SELECT COUNT(answer) FROM answer WHERE answer = 2 AND question_id = q.id) AS 'second_count'," + "(SELECT u.login FROM user u WHERE u.id = q.user_id) AS 'createdBy')" + "FROM question q") List findAdditionalInfo(); Where ExampleDTO is DTO that you created – Lemmy May 29 '17 at 20:50
  • This will return all data in one request – Lemmy May 29 '17 at 20:51
  • It doesn't work. What could I do? "Caused by: java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: expecting IDENT, found ''first_count'' near line 1, column 171 [SELECT new QuestionDTO(...)" – foxbuur May 31 '17 at 18:24
  • I removed "AS 'first_count" and "AS 'second_count'" from query and now I see this error: "Caused by: org.hibernate.QueryException: could not resolve property: first_answer of: pri.model.Question" – foxbuur May 31 '17 at 19:49
  • 1
    instead first_answer you should put firstAnswer, for second_answer secondAnswer and for created_date createdDate. All columns must match property values – Lemmy May 31 '17 at 20:12
  • thank you! Can I have another question? When I do **GET /questions?status='accepted'** I want to see only accepted questions (and without this param I'd like to see whole collection). If I use more params, the query should contains more extensive WHERE clause. Is there any good and fast way to build query dynamically? – foxbuur May 31 '17 at 21:20
  • You can use Spring Data criteria api using Specification or Query DSL for dynamically queries. Check these links: http://www.baeldung.com/rest-api-search-language-spring-data-querydsl, https://stackoverflow.com/questions/30574885/criteria-in-spring-data, https://spring.io/blog/2011/04/26/advanced-spring-data-jpa-specifications-and-querydsl/https://github.com/pkainulainen/spring-data-jpa-examples/tree/master/criteria-api – Lemmy May 31 '17 at 21:25
  • Is it possible to create nested objects with one SELECT query? I have Tag table `tag(id, name)` and every question can have many tags. I've tried to add this to query `"(SELECT new pri.dto.TagDTO(t.id, t.name) FROM Tag t JOIN t.questions q2 WHERE q2.id = q.id)"` but it doesn't work. – foxbuur Jun 01 '17 at 21:02