Spring MVC | Angularjs | Hibernate
I have a very simple web app that list all books from database.
Page looks something like this....
-------------------------------------------
Problem
1. My update method returns not only book object but also other information such as error message, error code...etc. This causes a problem in my view when I update.
-------------------------------------------
Angularjs $resource
Once the data is returned from the server the existing reference is populated with the actual data. This is a useful trick since usually the resource is assigned to a model which is then rendered by the view. Having an empty object results in no rendering, once the data arrives from the server then the object is populated with the data and the view automatically re-renders itself showing the new data. This means that in most cases one never has to write a callback function for the action methods.
Now this is actually very convenient as you don't have to re-render new data to the view. But for my case, I really want to return not only the object its self but also other necessary data if when an error occurs.
This is what my update method looks like in my controller
#RequestMapping(path="/booklist/{id}", method = RequestMethod.PUT, produces="application/json")
#ResponseBody
public ResponseBuilder<Book> save(#PathVariable("id") Integer id, #RequestBody #Valid Book book, BindingResult result){
if(result.hasErrors()){
/*...
* Some error process
*/
ResponseBuilder<Book> rrBuilder = new ResponseBuilder.Builder<Book>(book)
.httpStatus(HttpStatus.BAD_REQUEST)
.map(errorMap)
.message(result.getAllErrors().toString())
.build();
return rrBuilder;
}else{
bookDao.saveOrUpdate(book);
ResponseBuilder<Book> rrBuilder = new ResponseBuilder.Builder<Book>(
book)
.httpStatus(HttpStatus.ACCEPTED)
.build();
return rrBuilder;
}
};
Questions
What is a proper way of doing this? I could use ResponseEnitity but this doesn't allow me to pass error codes when needed.
Should I include Error Object with in my Book class?
Or should all Object implements an Error interface?
Look something like this..
#Entity
#Table(name="books")
#Component
public class Book implements Serializable{
#Id
#GeneratedValue
private int id;
#NotBlank
private String name;
/**
* other fields
*/
#Transient
private ErrorHandler errorHandler;
How do you usually handle errors?
thank you...
Related
I'm trying to update an object using REST controller in Spring 3,Jackson 4.0, using the PUT method.
We have a Container class with 4 properties one to many relationships to Container.
#JsonIdentityInfo(generator=ObjectIdGenerators.UUIDGenerator.class, property="#Id",scope = Container.class)
public class Container implements Comparable, Serializable {
private int containerId;
.
.
.
private Set<Container> containers;
private Collection<ImagePerContainer> imageControls;
private Collection<TextControl> textControls;
private Collection<PromoControl> promoControls;
private Collection<WebSource> webSourceControls;
}
Each one of this Objects (childs) have a reference to Container (father), like this:
#JsonIdentityInfo(generator=ObjectIdGenerators.UUIDGenerator.class, property="#Id",scope = TextControl.class)
public class TextControl implements Serializable {
.
.
.
private int textControlId;
private String textControlName;
private Container container;
.
.
.
}
Im trying to update the object when my angularjs app modifes the model using $resources PUT request. The first time it saves succesfully, but the second time I get a 400 response from the server, which logs this error:
HandlerMethod details:
Controller [com.bamboo.catW3.web.json.ContainerController]
Method [public org.springframework.http.ResponseEntity<com.bamboo.catW3.domain.Container> com.bamboo.catW3.web.json.ContainerController.updateContainer(int,com.bamboo.catW3.domain.Container)]
org.springframework.http.converter.HttpMessageNotReadableException: Could not read JSON: Already had POJO for id (java.util.UUID) [com.fasterxml.jackson.annotation.ObjectIdGenerator$IdKey#8adc3c85] (through reference chain: com.bamboo.catW3.domain.Container["#Id"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Already had POJO for id (java.util.UUID) [com.fasterxml.jackson.annotation.ObjectIdGenerator$IdKey#8adc3c85] (through reference chain: com.bamboo.catW3.domain.Container["#Id"])
at org.springframework.http.converter.json.MappingJackson2HttpMessageConverter.readJavaType(MappingJackson2HttpMessageConverter.java:171)
at org.springframework.http.converter.json.MappingJackson2HttpMessageConverter.read(MappingJackson2HttpMessageConverter.java:163)
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(AbstractMessageConverterMethodArgumentResolver.java:135)
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.readWithMessageConverters(RequestResponseBodyMethodProcessor.java:180)
at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:95)
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:77)
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:162)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:123)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:745)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:686)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856)
If I update another Container it works also the first time, but if I modified again and try to update it gives me the same 400 error. It doesn't even enter the controller. If I restart the server, then it lets me update the object one time, and it stops working again.
Why does it work the first time only ? How can I fix this ?
I faced the same problem, and just found this link :
https://github.com/FasterXML/jackson-databind/issues/499
It said that it was a bug in jackson 2.4.0 that was fixed in 2.4.1 and later.
Just update your jackson library to the latest stable release.
I am attempting to validate the input to a REST method using one or more specific validation groups, but so far I have been unable to figure out how to properly do it. I am using RESTeasy 3.X with Hibernate Validator 5.X.
I know that prior to the Bean Validation 1.1 spec, a RESTeasy method could be annotated with something like:
#ValidateRequest(groups=MyGroup.class)
However, this functionality no longer exists in Hibernate Validator 5.X.
Suppose I have an entity like:
#Entity
#Table(name = "example_table")
#XmlRootElement
public class ExampleEntity implements Mappable, Serializable
{
// ...
#Column(name = "title")
#NotEmpty(groups = Default.class, ExampleEntityGroup.class)
#Size(max = 255)
private String title;
#Column(name = "description")
#NotEmpty(groups = Default.class)
#Size(max = 255)
private String description;
// ...
}
Now I'd like to define a REST PUT method to update this entity, and I'd like the "description" field to allow an empty value. For this to happen I'd like to validate the entity using the ExampleEntityGroup group that I've defined (whereas the Default group would be used to validate POST requests that create new objects).
Right now my update method interface looks something like:
#Path("{id}")
#PUT
#Consumes(...)
#Produces(...)
ExampleEntity update(#PathParam("id"), ExampleEntity exampleEntity);
However, with this as written, it will always validate exampleEntity using the Default validation group. What would I do to force the update() method to use the other validation group?
The only documentation I could find that seems close to this is an example from the Bean Validation spec that uses an interceptor. Is there a better way?
RestEasy 3 with Bean Validation 1.1 will automatically validate constrained resource methods, i.e. it suffices to place a constraint annotation to a resource method parameter or return value and it will be validated. There is no way to validate a specific group, it will always be the default group. You could try and open a feature request for the RestEasy project for this.
I am new to ASP.Net MVC . Any help is greatly appreciated in resolving my problem.
I am using a LINQToSQL db in my MVC application. For one of the auto generated partial class (Example MyClass assume for table MyClass) , I created another Partial class as MyClass and added DataAnnotations Like following...
namespcae NP
{
[MetadaType(typeof(myData))]
[Serializable()]
public partial class MyClass
{
}
public myData
{
[Required]
public string ID { get ; set ;}
// Other properties are listed here
}
}
In my controller class example MyHomeController
I have a code as follows:
List<MyClass> list = new List<MyClass>();
list = dbContext.StoredProcedure(null).ToList<MyClass>()
session["data"] = list.
above code works fine if I use inProc session state. But if I use SQLServer mode then I get error as
"Unable to serialize the session state. In 'StateServer' and
'SQLServer' mode, ASP.NET will serialize the session state objects,
and as a result non-serializable objects or MarshalByRef objects are
not permitted. The same restriction applies if similar serialization
is done by the custom session state store in 'Custom' mode. "
Can anyone tell me what I am doing wrong here..?. I can see the data is getting populated in ASPState database tables. By application throws error as follows.
Just mark as Serializable all classes whose instances you want to store in Session.
Finally I was able to resolve the issue.
Solution:
Add the below statement before querying the database. In my case I was calling LinqToSQl context( dbContext).
dbContext.ObjectTrackingEnabled = false;
Sample Code:
List empList = new List();
dbContext.ObjectTrackingEnabled = false;
empList = dbContext.SomeStoredProcedure().ToList()
Session["employee"] = empList.
I'm using app engine datastore so I have entity like this.
#PersistenceCapable
public class Author {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
#JsonProperty("id")
#JsonSerialize(using = JsonKeySerializer.class)
#JsonDeserialize(using = JsonKeyDeserializer.class)
private Key key;
....
}
When the model is sent to view, it will serialize the Key object as an Id value. Then, if I send data back from view I want to deserialize the Id back to Key object by using JsonKeyDeserializer class.
public class JsonKeyDeserializer extends JsonDeserializer<Key> {
#Override
public Key deserialize(JsonParser jsonParser, DeserializationContext deserializeContext)
throws IOException, JsonProcessingException {
String id = jsonParser.getText();
if (id.isEmpty()) {
return null;
}
// Here is the problem because I have several entities and I can't fix the Author class in this deserializer like this.
// I want to know what class is being deserialized at runtime.
// return KeyFactory.createKey(Author.class.getSimpleName(), Integer.parseInt(id))
}
}
I tried to debug the value in deserialize's parameters but I can't find the way to get the target deserialized class. How can I solve this?
You may have misunderstood the role of KeySerializer/KeyDeserializer: they are used for Java Map keys, and not as generic identifiers in database sense of term "key".
So you probably would need to use regular JsonSerializer/JsonDeserializer instead.
As to type: it is assumed that handlers are constructed for specific types, and no extra type information is passed during serialization or deserialization process: expected type (if handlers are used for different types) must be passed during construction.
When registering general serializers or deserializers, you can do this when implementing Module, as one of the arguments is type for which (de)serializer is requested.
When defining handlers directly for properties (like when using annotations), this information is available on createContextual() callback of interface ContextualSerializer (and -Deserializer), if your handler implements it: BeanProperty is passed to specify property (in this case field with annotation), and you can access its type. This information needs to be stored to be used during (de)serialization.
EDIT: as author pointed out, I actually misread the question: KeySerializer is the class name, not annotation.
i have a partial view with a gridview in it. so when user clicks add button request will redirect to Add method of unitsController. After add it to database I should refetch all data from database. Is there a way to prevent controller from get all database records?
Below is my current controller
public class UnitsController : Controller
{
TList<Units> model=null;
public ActionResult UnitsPartial()
{
if(model==null)
model = database.GetAll();
return PartialView(model);
}
[HttpPost]
public ActionResult Add(Units unit)
{
if (ModelState.IsValid)
{
database.Save(unit);
model.Add(unit);
}
return PartialView("UnitsPartial", model);
}
In the last line I want to use return PartialView("UnitsPartial", model) instead of return database.GetAll() to prevent a database query. But model is null in Add method.
Is my approach correct or not? And why is model is null in add() method?
--UPDATED
first of all control redirect to UnitsPartial() and will fill model object correctly. after press add button, control will redirect to Add(...) method but this time model is equal to null !!!
what is the problem with it? i tried to pass model.Clone() to partial view
return PartialView("UnitsPartial", model.Clone());
but the result is the same
You can't cache values in the controller class like this using member variables. HTTP is stateless and MVC 3 follows that approach. Each individual call to an action method is going to have a brand new instance of Controller class with the model set to null.
So start by calling database.GetAll() in both action methods and then ask the question "How do I make this more efficient".
I don't know if it is the right approach or not, since I do not fully understand what you try to do, but your model is null because you initialize it as null when the controller is created. you should do a model = new TList() before adding something to it....