Reputation: 65
I am trying to avoid boilerplate code in my client's requests handling through JAX-RS
and RESTeasy
. I have several classes, example
class User{
private String name;
private String username;
private String address;
private long id;
//getters and setters
}
class Company{
private String name;
private String address;
private String location;
//getters and setters
}
I want to have create()
, update()
, delete()
, and getAll()
responses. For example, For the User I would have the following class with a create method and subsequent update, delete and getAll:
@Path ("/user")
public class UserApi {
@PersistenceUnit
private EntityManagerFactory emf;
@POST
@Path("/create")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public String Create(@Form User user){
EntityManager em = emf.createEntityManager();
try{
em.getTransaction().begin();
em.merge(user);
em.getTransaction().commit();
}
catch (Exception e){
e.printStackTrace();
em.getTransaction().rollback();
return "{\"success\":false, \"msg\":\"Error occured, please try later\"}";
}
return "{\"success\":true, \"msg\": \"Saved successfully\"}";
}
I would have to repeat the same code for each of the classes I have. Someone had commented on use of a factory class to avoid this, perhaps using generics or an Interface. I have had a hard time figuring it out. Please suggest a good design to solve this.
Upvotes: 0
Views: 494
Reputation: 5166
You must ask yourself what the differences between all implementations will be.
In this case, not much. You can copy/paste this piece of code, replace @Path("/user") with @Path("/company"), type User
with Company
and Bob's your uncle. Em.merge() takes any object, so we don't really care.
So because the @Path annotation is different for all, it would be acceptible to create a new Api like this;
@Path("/users")
@Stateless
public class UserApi extends AbstractCrudApi<User> {}
All elements that are unique to this REST service are defined. We have our @Path and User which is passed as a Generic Type.
Now we create this AbstractCrudApi abstract class. abstract
, because we want to implement the common logic for all future services. Interfaces,as you already know, cannot have implemented methods. (Let's just forget about java 8 default
methods)
@Produces(MediaType.APPLICATION_JSON)
public abstract class AbstractCrudApi<T> {
@PersistenceContext
private EntityManager em;
@POST
@Path("/create")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public String create(@Form T entity) {
try {
em.getTransaction().begin();
em.merge(entity);
em.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
em.getTransaction().rollback();
return "{\"success\":false, \"msg\":\"Error occured, please try later\"}";
}
return "{\"success\":true, \"msg\": \"Saved successfully\"}";
}
}
And that is all there is to it. I don't quite see how a factory can be of any advantage here.
Another tip; try using the javax.ws.rs.core.Response
return type instead of String. This will allow you to set a http response status as well as your response body. You can also try using Jackson to marshall your response objects. Check resteasy-jackson2-provider
.
Upvotes: 2