Creating DTOs Without Mapping with JPA and Java EE

The Ping class is a JPA entity and JSON-B DTO at the same time:


@Entity
@NamedQuery(name = "all", query = "SELECT p FROM Ping p")
public class Ping {

    @Id
    @GeneratedValue
    public long id;

    public String message;
    public int age;

}

...the Ping class can be directly read from a DB:

@Stateless
public class PingStore {

    @PersistenceContext
    EntityManager em;

    public List<Ping> all() {
        return this.em.createNamedQuery("all", Ping.class).getResultList();
    }
}        
        
...and exposed via JAX-RS:

@Path("ping")
public class PingResource {

    @Inject
    PingStore store;

    @GET
    public List<Ping> all() {
        return this.store.all();
    }
}    

curl -i http://localhost:8080/flight/resources/ping/ returns:

[{"age":0,"id":1,"message":"Enjoy Java EE 8!"},{"age":0,"id":2,"message":"Enjoy Java EE 8!"}]    

A dedicated DTO:

public class PingSlice {

    public long timestamp;
    public String flightName;

    public PingSlice(String flightName) {
        this.flightName = flightName;
        this.timestamp = System.currentTimeMillis();
    }

}

can be directly mapped by the EntityManager:

public List<PingSlice> allSlices() {
        return this.em.createQuery("SELECT new com.airhacks.ping.entity.PingSlice(p.message) from Ping p", PingSlice.class).
                getResultList();
    }


exposed via JAX-RS again:

@GET
@Path("slices")
public List<PingSlice> allSlices() {
    return this.store.allSlices();
}

and returned as JSON (curl -i http://localhost:8080/flight/resources/ping/slices):

[{"flightName":"Enjoy Java EE 8!","timestamp":1542797875539},{"flightName":"Enjoy Java EE 8!","timestamp":1542797875539}]    

Also primitive types like String, can be fetched with JPA:

public List<String> allStrings() {
        return this.em.createQuery("SELECT p.message from Ping p", String.class).
                getResultList();
    }    

...exposed via JAX-RS:

@GET
@Path("strings")
public List<String> allStrings() {
    return this.store.allStrings();
}

and returned as JSON (curl -i http://localhost:8080/flight/resources/ping/strings)

["Enjoy Java EE 8!","Enjoy Java EE 8!"]

With Java EE JPA Entities can be used as DTOs first. A different "view" to the JPA entities can be implemented on-demand and without explicit mapping layers or mappers.

See you at Java EE Microservices. Is Munich's airport too far? Learn from home: javaeemicro.services.

Comments:

This works for simple cases but imagine the presentation you require alters the fetch graph. If you have sophisticated logic in the query and have multiple representations that all should be able to make use of the same query logic, this quickly runs into it's limits.
Nested structures are also not implementable with this approach. You'd need to do separate processing to be able to have collections in a DTO.
I suggest to use Blaze-Persistence Entity Views for real world cases ;)
https://github.com/Blazebit/blaze-persistence#entity-view-usage

Posted by Christian Beikov on November 21, 2018 at 01:10 PM CET #

Post a Comment:
  • HTML Syntax: NOT allowed
...the last 150 posts
...the last 10 comments
License