Update multiple fields of JPA Entity - spring

I have JPA entity Customer having say 50 fields and I would like to update it from the end user using html form.
I am passing one instance of entity to html page form (using thymeleaf), this form is having only 20 fields out of 50 (including ID field). Now once the form is submitted, I would like to update 20 fields from data received using form to the database. I am not getting solution for above issue. One solution is to update individual field but I don't think it is good solution.
#Entity
public class Customer
{
...
50 fields
}
My get method:
#GetMapping(value = "customer")
public String customer(Model model) {
Customer customer = null;
Optional<Customer> optional = customer Repo.findById(customerId);
if (optional.isPresent()) {
customer = optional.get();
}
model.addAttribute("customer", Customer);
return "customer";
}
Html form:
<form action="updateCustomer">
----20 fields which I would like to get update from user are here
</form>
#PostMapping(value = "updateCustomer")
public String updateCustomer(Model model, #ModelAttribute Customer customer) {
if(customer==null) {
System.out.println("Customer object is null");
}
else
{
customerRepo.save(customer);
}
return "savedCustomer";
}
In the post method when I get customer object it is having only 20 fields data not 50(Customer entity is having total fields) because html form is having only 20 fields for update. How to update the old customer object having 50 fields using the new customer object having updated 20 fields.?

There are three ways in the past that I solved this problem
1) have the page GET the Customer object in question, use the object to pre-populate the form, and then POST the changed customer object. The benefit is that the user changing the Customer sees all info related to the Customer, and you have a easy merge on the backend. The drawback is an additional REST call.
2) Create a DTO, and transfer non-null fields from the DTO to the entity. The benefit is you don't have to update all the fields in the form, and no extra network call. the drawback is that it's a pure pain in the rear end.
3) Create a DTO, and make it an entity to save. The benefit is that it's a easy merge back to the database, nothing prevents you from mapping the same table and fields to multiple entities. The drawback is that you have to worry about concurrency issues, which may just not work in your workflow, and the DTO is basically specific per form.

To make partial updates to entity, you either need to use Criteria API or JPQL query ... this is called projection in JPA, here is a quick example ,
Note : You might not be able to use this feature if you are using an old version of JPA query parser (no JPQL updates) + old version of JPA (when no CriteriaUpdate lib was there) you will then need to fetch the object from DB with the id passed from the client, update the 20 properties from the client and save the changes back

Below solution worked for me:
Helper Class:
public class BeanCopy {
private static final Set<Class<?>> primitiveTypes = new HashSet<Class<?>>(
Arrays.asList(Boolean.class, Character.class, Byte.class, Short.class, Short.class, Integer.class, Long.class, Float.class, Double.class, Void.class, String.class, Date.class));
public static void nullAwareBeanCopy(Object dest, Object source) throws IllegalAccessException, InvocationTargetException
{
new BeanUtilsBean() {
#Override
public void copyProperty(Object dest, String name, Object value)
throws IllegalAccessException, InvocationTargetException {
if(value != null && (primitiveTypes.contains(value.getClass()) )) {
super.copyProperty(dest, name, value);
}
}
}.copyProperties(dest, source);
}
}
This is how I copied and forced changes to database:
try {
BeanCopy.nullAwareBeanCopy(DBcustomer,customer);
customerRepo.save(DBcustomer);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
Please let me know any better solution is available for the above problem.

Related

From request object to the database

I have an app with an AngularJS front-end and a Spring MVC back-end. I'm having some trouble with converting/mapping request objects to domain/dto objects.
On one page you can add a new order to the system, the POST payload would look something like this:
{
memo: "This is some extra info for order",
orderLines: [{productId:3, quantity:4}, {productId:2, quantity:5}, {productId:1, quantity:4}],
shippingDate: "2014-10-08T19:16:19.947Z",
warehouseId: 2
}
The Spring MVC controller method looks like this:
#RequestMapping(value = "/order", method = RequestMethod.POST)
public ResponseEntity<Void> addOrder(#RequestBody #Valid OrderRequest orderRequest, UriComponentsBuilder b) throws Exception {
// the magic
}
Where OrderRequest is filled with the values of the POST request, the OrderRequest and OrderLineRequest look like this:
public class OrderRequest {
private Long id;
private Date shippingDate;
private String memo;
private List<OrderLineRequest> orderLines;
private Long warehouseId;
public OrderRequest() {
}
// getters and setters ommitted
}
public class OrderLineRequest {
private Long id;
private String productCode;
private int quantity;
public OrderLineRequest() {
}
}
My question now is, in order to save an Order object with orderService.add(order) I need to construct the Order object based on the values that were sent in the request. Where/how do I do this?
OPTION 1
The OrderRequest class could have a makeOrder() method with just returns an Order object like so:
public Order makeOrder() {
Order order = new Order();
order.setMemo(this.memo);
order.setShippingDate(this.shippingDate);
...
}
Then I'd have to map the OrderLineRequest which could have their own makeOrderLine method:
public OrderLine makeOrderLine() {
OrderLine orderLine = new OrderLine();
orderLine.setQuantity = this.quantity;
...what to do with only the productId?
}
As you can see I can set the quantity but in the request I only received the productId, but in the database I save the productCode, productName as well, so I need that info from the database, but I don't want to make a database call from the Request object...I also don't want to half of the mapping in the request object and the rest of the mapping in the controller where I do have access to the services.
OPTION 2
I can use Dozer to do the mapping for me, but that would mean injecting the services into the Dozer custom converters which seem equally unclean to me...
OPTION 3
I pass the OrderRequest object to the service layer and let the service layer handle it, but my question would remain, how exactly would the service layer convert it, say you have the method addOrder like this:
public void addOrder(OrderRequest orderRequest) {
}
Would you call another service to convert from one to the other as I don't really want this conversion in a business logic method?
Any help would be appreciated
use the #RequestBody to map your jsonObject that is send with the request , to a DTO .
please refer to the following tutorial .
hope that helps .
and please ask if there is something not clear .

Custom Validation in Spring MVC 3.0 - Pass #Model to Validator

I am trying to write a custom validator in a Spring MVC application. I would like to know if there a way to pass the #Model object to a custom spring validator?
Say, I have a Person object and Account object. I have to write a custom validator to validate Person, but the validation is dependent of the Account object and other objects in session.
For example, Person cannot have more than 3 accounts, account types have to be of specific category and not old than 3 years (this value, ie the number years is dynamic based on the profile logged in and is in session).
How can I pass both objects, especially #Model to the validator.
public class ServiceValidator implements Validator {
#Autowired
private ServiceDAO servicesDao;
#Override
public boolean supports(Class<?> clasz) {
return clasz.isAssignableFrom(Person.class);
}
#Override
public void validate(Object obj, Errors errors) {
Person subscriber = (Person) obj;
// How can I access #Model object here ???
}
Doubt if you can but have two workarounds:
a. If it is persisted data that you are looking for, probably it is just better to retrieve it once more in the validator and validate using that data, so for eg, in your case if you are validating person and persons account details are retrievable from DB, then get it from DB and validate in your validator using the retrieved data.
b. Probably this is a better approach if the number of places where you need to use the validator is fairly confined:
public class ServiceValidator {
#Autowired
private ServiceDAO servicesDao;
public void validate(Person subscriber, List<Account> accounts, ..., Errors errors) {
}
Just call the above validator directly from your requestmapped methods..
In your controller..
List<Account> accounts = //retrieve from session
serviceValidator.validate(subscriber, accounts, ...errors);
if (errors.hasErrors())..
else..

"An object with the same key already exists in the ObjectStateManager..." exception is thrown when setting an entity state to modified

I followed some examples(including such books as "Pro ASP.NET MVC 3" and "Professional ASP.NET MVC 3") to create simple ASP.NET MVC 3 apps using EF 4.1 (since I'm new to these technologies).
I'm using the following repository(single instance of it is used by all action methods of the controller) to access the DB:
public class ProductRepository : IProductRepository
{
private readonly EFDbContext _context = new EFDbContext();
#region Implementation of IProductRepository
....
public void SaveProduct(Product product)
{
if (product.ProductId == 0)
{
_context.Products.Add(product);
}
else
{
_context.Entry(product).State = EntityState.Modified;
}
_context.SaveChanges();
}
....
}
This repository performs updating as it was shown in the examples I used.
Product class:
public class Product
{
public int ProductId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public string Category { get; set; }
}
In case of updating the product, I'm getting the exception "An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key"
I know that the similar questions have been already discussed here but my question is a bit different:
Why this code which was taken from examples is not working (though it looks pretty simple and straightforward)? What wrong might I have done or missed something.
After searching for hours for a solution, I have found one that seems suitable after doing enough reading.
The fix is here:
An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key
Basically, fetch the record from the Context and call:
var currentProduct = _context.Products.Find(product.ProductId);
_context.Entry(currentProduct).CurrentValues.SetValues(product);
This seems like a bad idea and something I've always hated about EF in my previous workings, but cccording to Ladislav Mrnka (who apparnently answers every EF related question on Stackoverflow) in this post:
Entity Framework and Connection Pooling
EF will store a request for an entity internally, so ideally, it will already be there and it won't be making an additional call back to the database.
The root cause of the problem seems to be that once a product is fetched from the Context, the context is keeping track of it and that's what is causing all the trouble. So merging your changes back in is the only way.
Hope that helps.
It looks like you're not updating product.ProductId when the item is saved for the first time. This means that when you come back to save the item again it's adding it to the context again, hence the error.
As the Id will be added by database (I'm assuming it's the autogenerated Id) then you'll need to read your product data back onto the client.
From a Generics standpoint, here's how I have resolve the same problem very recently:
public TEntity Update(TEntity model, bool persist)
{
if (model == null)
{
throw new ArgumentException("Cannot update a null entity.");
}
var updateModel = Get(model.Id);
if (updateModel == null)
{
return model;
}
this.context.Entry<TEntity>(updateModel).CurrentValues.SetValues(model);
this.Save(persist);
return model;
}

Spring MVC 3.0: How do I bind to a persistent object

I'm working with Spring MVC and I'd like it to bind a a persistent object from the database, but I cannot figure out how I can set my code to make a call to the DB before binding. For example, I'm trying to update a "BenefitType" object to the database, however, I want it to get the object fromthe database, not create a new one so I do not have to update all the fields.
#RequestMapping("/save")
public String save(#ModelAttribute("item") BenefitType benefitType, BindingResult result)
{
...check for errors
...save, etc.
}
There are several options:
In the simpliest case when your object has only simple properties you can bind all its properties to the form fields (hidden if necessary), and get a fully bound object after submit. Complex properties also can be bound to the form fields using PropertyEditors.
You may also use session to store your object between GET and POST requests. Spring 3 faciliates this approach with #SessionAttributes annotation (from the Petclinic sample):
#Controller
#RequestMapping("/owners/*/pets/{petId}/edit")
#SessionAttributes("pet") // Specify attributes to be stored in the session
public class EditPetForm {
...
#InitBinder
public void setAllowedFields(WebDataBinder dataBinder) {
// Disallow binding of sensitive fields - user can't override
// values from the session
dataBinder.setDisallowedFields("id");
}
#RequestMapping(method = RequestMethod.GET)
public String setupForm(#PathVariable("petId") int petId, Model model) {
Pet pet = this.clinic.loadPet(petId);
model.addAttribute("pet", pet); // Put attribute into session
return "pets/form";
}
#RequestMapping(method = { RequestMethod.PUT, RequestMethod.POST })
public String processSubmit(#ModelAttribute("pet") Pet pet,
BindingResult result, SessionStatus status) {
new PetValidator().validate(pet, result);
if (result.hasErrors()) {
return "pets/form";
} else {
this.clinic.storePet(pet);
// Clean the session attribute after successful submit
status.setComplete();
return "redirect:/owners/" + pet.getOwner().getId();
}
}
}
However this approach may cause problems if several instances of the form are open simultaneously in the same session.
So, the most reliable approach for the complex cases is to create a separate object for storing form fields and merge changes from that object into persistent object manually.
So I ended up resolving this by annotating a method with a #ModelAttribute of the same name in the class. Spring builds the model first before executing the request mapping:
#ModelAttribute("item")
BenefitType getBenefitType(#RequestParam("id") String id) {
// return benefit type
}
While it is possible that your domain model is so simple that you can bind UI objects directly to data model objects, it is more likely that this is not so, in which case I would highly recommend you design a class specifically for form binding, then translate between it and domain objects in your controller.
I'm a little confused. I think you're actually talking about an update workflow?
You need two #RequestMappings, one for GET and one for POST:
#RequestMapping(value="/update/{id}", method=RequestMethod.GET)
public String getSave(ModelMap model, #PathVariable Long id)
{
model.putAttribute("item", benefitDao.findById(id));
return "view";
}
then on the POST actually update the field.
In you example above, your #ModelAttribute should already be populated with a method like the above method, and the properties be bound using something like JSTL or Spring tabglibs in conjunction with the form backing object.
You may also want to look at InitBinder depending on your use case.

change foreign key for a linq entity

I have a CRUD repository as fallowing:
public class CrudRepository<T> : ICrudRepository<T>
where T : class, IUnique
{
static DataContext db = new DataContext();
protected DataContext DataContext { get { return db; } }
public virtual IQueryable<T> GetAll()
{
return db.GetTable<T>();
}
public virtual void Add(T item)
{
db.GetTable<T>().InsertOnSubmit(item);
}
public virtual void Save()
{
db.SubmitChanges();
}
public virtual T Get(int id)
{
return GetAll().FirstOrDefault(t => t.Id.Equals(id));
}
}
I use static data context for all instance off repository.
I want to change foreign key entity so i try fallowing solution:
CrudRepository<Employee> employeeRepository = new CrudRepository<Employee >();
Employee employee = employeeRepository.Get(employeeId)
employee.OfficeId = officeId;
employeeRepository.Save();
But it throw fallowing exception :
ForeignKeyReferenceAlreadyHasValueException
So i try fallowing second solution:
CrudRepository<Employee> employeeRepository = new CrudRepository<Employee >();
Employee employee = employeeRepository.Get(employeeId)
employee.Office = new CrudRepository<Office>().Get(officeId);
employeeRepository.Save();
But it throw exception with fallowing message:
An attempt has been made to Attach or
Add an entity that is not new, perhaps
having been loaded from another
DataContext
what can i do?
what is the problem?
Three things jump out at me.
employee.OfficeId = officeId;
If the Employee class has an OfficeId property and an Office property, you must use the Office property to make changes. The Office property is auto-generated from the relationship in the linq designer.
If you want to use Id based manipulations instead, delete the relationship between employee and office in the designer (note: this does not change the database, it just changes the mappings used by the code generator).
new CrudRepository<Employee >();
new CrudRepository<Office>().Get(officeId);
Each Crud Repository has its own datacontext. Objects loaded from different datacontexts are not allowed to co-mingle. Suppose they were allowed to co-mingle - when you call SubmitChanges, which DataContext is responsible for saving?
Ultimately, this means your CrudRepository implementation is going to be something you want to move away from if you want to continue using LinqToSql. Supporting Crud operations on a single class just isn't that useful. At least it's only passthrough calls, and will be easy to replace with direct DataContext method calls.
static DataContext db = new DataContext();
This is damnable. DataContext is not threadsafe.

Resources