users@jersey.java.net

[Jersey] Re: Implementing a Query by Example

From: Antonio Goncalves <antonio.mailing_at_gmail.com>
Date: Tue, 22 Oct 2013 15:28:45 +0200

Hi all,

I'm using a very manual approach to solve this problem and I would like to
share it with you in case you have a better idea. Basically my resource
doesn't do much, it just uses a @QueryParam to get the XML and JPA to do a
QBE :

    @GET
    @Path("*/query*")
    @Produces("application/xml")
    public Response findByQBE(*_at_QueryParam("example")* *Book* example) {

        CriteriaBuilder builder = em.getCriteriaBuilder();
        CriteriaQuery<Book> criteria = builder.createQuery(Book.class);
        // ...
        // query by example
        return Response.ok(books).build();
    }


The manual work that I do is in the Book entity. Basically I give it a
constructor with a String (needed by JAX-RS) to be able to unmarshall the
XML into the bean itself, and I create a toXML() method that marshalls the
bean into XML :

@Entity
*_at_XmlRootElement*
public class Book implements Serializable {

    @Id @GeneratedValue
    private String isbn;
    private String title;
    private String description;
    // ... other attributes

    public Book() {
    }

    public *Book(String xml)* throws JAXBException {
        // Uses JAXB to unmarshall the XML string into the bean itself
        JAXBContext ctx = JAXBContext.newInstance(Book.class);
        Unmarshaller m = ctx.createUnmarshaller();
        Book book = (Book) m.*unmarshal*(new StringReader(xml));
        this.isbn = book.getIsbn();
        this.title = book.getTitle();
        this.description = book.getDescription();
        this.nbOfPages = book.getNbOfPages();
        this.publisher = book.getPublisher();
    }

    public String *toXML()* throws JAXBException {
        // Uses JAXB to marshall the bean into an XML string
        StringWriter writer = new StringWriter();
        JAXBContext ctx = JAXBContext.newInstance(Book.class);
        Marshaller m = ctx.createMarshaller();
        m.*marshal*(this, writer);
        return writer.toString();
    }

    // ... getters & setters
}

Then, to invoke my resource with the Client API, I just call the toXML()
method as follow :

        Book example = new Book();
        example.setTitle("Java");
        Response response = client.target(URL + "/books").path("/query")
                .queryParam("example", *example.toXML()*)
                .request(MediaType.APPLICATION_XML)
                .get();

This way I invoke the resource with a request that looks like what I want :

/rest/books/*query/example=<book><title>Java</title></book>*


So it does work except there is many manual code that, in my mind, could be
automatised. Am I wrong ? Do you have a better idea ? In this case I
suppose a provider would make sense but I already use JAXB and XML so it
feels weird to do the job a second time.

Thanks for you thoughts

Antonio


2013/10/22 Antonio Goncalves <antonio.mailing_at_gmail.com>

> Hi all,
>
> I need to implement a query by example service and be able to consume it
> with the new client API.
>
> The idea is that I have a Book entity with a JAXB annotation :
>
> @Entity
> @XmlRootElement
> public class Book implements Serializable {
>
> @Id @GeneratedValue
> private Long id = null;
>
> private String isbn;
> private String title;
> private String description;
> }
>
>
> I now need a service that would be able to receive an 'example' of the
> Book in XML format. So if the service receives :
>
> <book><isbn>1234</isbn></book>
>
> It will return all the books with an ISBN like '1234. If the service
> receives :
>
> <book><isbn>1234</isbn><title>Java</title><description>Best
> book</description></book>
>
> Il will return all the books with an ISBN like '1234' AND a title like
> 'Java' AND a description like 'Best Book'. As you can see, the query string
> depends on which attributes of the entity are set. So I was thinking of
> having a URI that would look like :
>
> /rest/books/*query*=<book><isbn>1234</isbn></book>
> /rest/books/*query*=<book><isbn>1234</isbn><title>Java</title><description>Best
> book</description></book>
>
> So I assume that the resource would then look like :
>
> @GET
> public Response findByQBE(@QueryParam("*query*") Book example) {
> ...
> }
>
> And the client API
>
> Book *example* = new Book();
> example.setTitle("Java");
> client.target(URL + "/books").path("/qbe").queryParam("*query*", *example*
> ).request(MediaType.APPLICATION_XML).get();
>
> But that doesn't work (I get a 404).
>
> I feel I don't need to write a provider to marshall the POJO into an XML
> String. So I think I'm doing something wrong here.
>
> Any idea
>
> Thanks
>
> --
> Antonio Goncalves
> Software architect and Java Champion
>
> Web site <http://www.antoniogoncalves.org/> | Twitter<http://twitter.com/agoncal>
> | LinkedIn <http://www.linkedin.com/in/agoncal> | Paris JUG<http://www.parisjug.org/>
> | Devoxx France <http://www.devoxx.fr/>
>



-- 
Antonio Goncalves
Software architect and Java Champion
Web site <http://www.antoniogoncalves.org/> |
Twitter<http://twitter.com/agoncal>
 | LinkedIn <http://www.linkedin.com/in/agoncal> | Paris
JUG<http://www.parisjug.org/>
 | Devoxx France <http://www.devoxx.fr/>