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.
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 #