Say I have a student entity:
@Entity
public class Student {
@Getter
@JsonProperty
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
@Getter
@JsonProperty
@ManyToOne
private Clazz clazz;
@Getter
@JsonProperty
private String
firstName,
lastName;
}
And I want to persist it by POSTing to a JAX-RS class:
@Path("/student")
@Produces(MediaType.APPLICATION_JSON)
public class StudentResource {
private StudentDAO studentDao;
public StudentResource(StudentDAO studentDao) {
this.studentDao = studentDao;
}
@POST
@UnitOfWork
@Consumes(MediaType.APPLICATION_JSON)
public void persist(Student student) {
this.studentDao.save(student);
}
}
And my request body looks like:
{
"firstName": "Joe",
"lastName": "Bloggs",
"clazz": 1
}
Then Jackson's going to blow up because:
DEBUG [2015-05-16 11:08:59,144] io.dropwizard.jersey.jackson.JsonProcessingExceptionMapper: Unable to process JSON
! com.fasterxml.jackson.databind.JsonMappingException: Can not instantiate value of type [simple type, class Clazz] from Integral number (1); no single-int-arg constructor/factory method
I understand why I'm getting this error; it can't deserialize the Clazz object from the key. I can't think of a few hacky ways to fix this (e.g. add a Clazz(int id) constructor and make the clazzDao statically available to look up an instance from the database and populate the fields from within that constructor). But can someone offer me a cleaner solution?
Thanks in advance.
--
You received this message because you are subscribed to the Google Groups "dropwizard-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dropwizard-us...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
I'd start by not having the Jackson object and the JPA entity use the same class. You're directly tying a dependency from your REST API to your database schema, which means you're in trouble down the road when you want to change one without changing the other.
Then StudentResource can take the class ID from the request and look up the Clazz entity, construct the Student entity with the Clazz instance, and persist it to the database. It's a little bit of extra code, but decoupling the API from the schema will probably be worth it.
And my request body looks like:
{ "firstName": "Joe", "lastName": "Bloggs", "clazz": 1 }
{
"firstName": "Joe",
"lastName": "Bloggs",
"clazz": { id: 1 }
saveStudent(Student student) {// Look up the Clazz reference.Clazz clazz = clazzDAOfindById(student.getClazz().getId());student.set(clazz);studentDao.update(student);}
--
Rupert:in this approach, how would you handle the following two situations? These are honest questions. I'm not trying to be argumentative. I actually think these pattern discussions are really useful for everyone to hear and participate in.1) I want to share the API representation classes with other projects (imagine a servicename-api module), so those projects don't need to write and maintain their own Jackson bindings. If I pass around JPA entities, now I'm forcing another service to take JPA as a runtime dependency. Obviously if I pass around Jackson annotated objects I'm forcing Jackson as a dependency, but I find developers are happier taking on a particular [de]serialization implementations than infrastructure (database classes). I'm sure there's several camps in conversations around whether system components ought to be sharing this code or not, so we can have that conversation as well if folks are interested.
2) I want to mix-in details from another service/source into the API response. For instance, students come from the database but maybe their presence information (online, offline, away, etc) comes from another service (another dropwizard service, for instance). You want the presence information in the API response but not in the database. Now you have to create and populate a bunch of @Transient properties on the entity objects before sending them back to the client.
What I see all the time is people assuming they need a transactional style API as the default position, diving in and creating one, along with reams of mapping logic, and end up with an inconvenient and less maintainable API, that in many cases they don't even need because their application doesn't call for it.