What are rules behind setting model's child attributes to null? - java

In my Spring MVC / Thymeleaf application I apply an UUID when instantiating a brand new instance of any model entity (I do it in an AbstractModel that each entity extends). I use it then for custom equals method implementation that spreads along all entities
#Column(name = "UUID", nullable = false, unique = true)
private String uuid = IdGenerator.createId();
I some other place I create a form bean (simple wrapper) containing a graph of such newly created entities.
an example wrapper would look like this:
WrapperObj obj = new Obj(new FooEntity(), new BarEntity())
both FooEntity and BarEntity have some fileds like ints, Strings etc. that are initially not set. The only fileld that is set isthe UUID
Then I pass the wrapper to the form where I eventually fill up the empty fields.
When I submit the form and the entities are not modified, they still exist in the wrapper. When I attach another object the the wrapper WrapperObj obj = new Obj(new FooEntity(), new BarEntity(), new TestObj()) that has no UUID initially set and submit the form without changing it's fields, the TestObj is reset to null.
This is my desired behaviour for the other UUID-enabled entities.
How can I instruct the spring mvc framework to ignore the UUID field at the binding time so that the entities also become null when not modified?
Thanks for your help!
CORRECTION
the TestObj object was not bound to any input field in the form.
UPDATE
I have just discovered that it is not the uuid that causes the problem. I my wrapper I have another child entity, let's say BoolFoo that has only boolean fields (apart of the id(int) and uuid(string) set by the AbstractEntity class).
Those fields are of type checkbox within the form.
When I submit the form without modifying any of BoolFoo's fileds, BoolFoo as a whole is set to null (although the id and uuid are present when passing the object to the form).
However, if there is at least one text field(did not checked against other types) provided for any child entity, the child object (or any actually) will never be null when comming back from the form.
I've tried already the binder.setDisallowedFields("uuid") approach as well as binder.registerCustomEditor(String.class, new StringTrimmerEditor(true)); but no matter what I do, the unmodified objects will not be set to null.
Looks like there are different rules when it comes to different form field types in spring mvc.
Has anyone come across similar problem?

The DataBinder has two properties named allowedFields and disallowedFields that you could define what to bind or not. Just use that with annotation #InitBinder before executing controller method:
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.setDisallowedFields("uuid");
}

Related

How to update only a subset of fields and update the repository?

I'm making a spring boot application, and I'm looking to update an existing entry in the DB through my service and controller. In my service layer I have the below method. So I'm retrieving the fields associated with a caseID, creating a model mapper which maps my entity object class to my VO, and then mapping the retrieved data to my DTO. Then I save my repository. The purpose is to add only the fields which I have specified in my req message ie if I only want to update 1 field out of 20, it updates this field and leaves the rest untouched. The below runs successfully, but the field I specify in my request message in postman does not update in the DB. Why is this? I have tried mapping different objects and saving different variables to the repository but nothing seems to update the DB.
public StoredOutboundErrorCaseVO updateCase(OutboundErrorCaseVO outboundErrorCaseVO, Long caseNumber) {
OutboundErrorCaseData existingCaseData = ErrorCaseDataRepository.findById(caseNumber).get();
ModelMapper mm = new ModelMapper();
mm.getConfiguration().setAmbiguityIgnored(true);
OutboundErrorCaseData uiOutboundErrorCaseData = mm.map(outboundErrorCaseVO,
OutboundErrorCaseData.class);
mm.map(existingCaseData, uiOutboundErrorCaseData);
ErrorCaseDataRepository.save(uiOutboundErrorCaseData);
return mm.map(uiOutboundErrorCaseData, StoredOutboundErrorCaseVO.class);
}
Controller - code omitted for brevity, POST method (I usually use PUT for updates but I believe I can still use POST)
StoredOutboundErrorCaseVO updatedCase = outboundErrorService.updateCase(outboundErrorCaseVO,
caseNumber);
Repo
#Repository
public interface OutboundErrorCaseDataRepository extends JpaRepository<OutboundErrorCaseData, Long> {
You are getting data and passing it into existingCaseData and save uiOutboundErrorCaseData. So my guess is Hibernate is adding a new object into the database with new Id and with you updated value. It of course depends on your model definition. Especially id.
I also think Hibernate won't let you save uiOutboundErrorCaseData with the same Id if you already have an object in Hibernate Session associated with that id. So, why don't you update existingCaseData with the new value and save it back.
I created a working solution, although I realise it can be improved, it certainly works. The only drawback is that I need to specify all the fields which can be updated, ideally I want a solution which takes in n number of fields and updates the record.
OutboundErrorCaseData existingCaseDta = ErrorCaseDataRepository.findById(caseNumber).get();
if (outboundErrorCaseVO.getChannel() != null) {
existingCaseDta.setChannel(outboundErrorCaseVO.getChannel());
}
ErrorCaseDataRepository.save(existingCaseDta);
ModelMapper mm = new ModelMapper();
return mm.map(existingCaseDta, StoredOutboundErrorCaseVO.class);

How to effectively update a record using hibernate & jparepository

I'm trying to implement a method for updating a database record. So far, I created this one:
public Optional<User> update(final Integer id,final UpdateUserDto dto) {
userRepository.findById(id).ifPresent((user -> {
user.setShop((dto.getShopId() == null) ? null : shopRepository.findById(dto.getShopId())
.orElseThrow(ShopNotFoundException::new));
user.setUsername(dto.getUsername());
user.setPassword(passwordEncoder.encode(dto.getPassword()));
user.setRoles(Arrays.asList(
roleRepository.findByRoleName(dto.getRole()).orElseThrow(RoleNotFoundException::new)
));
}));
return userRepository.findById(id);
}
But now I added two more fields to my user entity (activated, productAllowed) and I must enhance my update method to make them updatable. I can do that, but I have other entities also and if I change them it will be a lot of maybe boilerplate code.
Is there any kind of best practice to do this in a better way, or I just need to keep setting all the fields manually?
I was also thinking about reflection, but in that case I have a few fields that cannot be copied exactly from the DTO to the entity (e.g. the shop field, which is queried from database, or role field).
And I also don't think that another query for returning the new object is effective, but although I set the properties in a service layer, the original findById()'s returned user is wrapped inside an Optional, so I don't think it will be updated.
Thank you in advance.

Spring data jpa update (save) wont' update entity [duplicate]

I have an entity which looks something like this: (I'm coding to the web page so I apologize for any mistakes)
#Entity
public class Entity {
#Id
private Long id;
private String field;
// Insert getters and setters here...
}
I try to manipulate it using reflection:
Long id = 1;
Entity entity = myDao.getEntity(id);
entity.setField("set directly");
Field[] fields = entity.getClass().getDeclaredFields();
for (Field f : fields) {
if (f.getName().equals("field")) {
f.setAccessible(true);
f.set(entity, "set using reflection");
f.setAccessible(false);
}
}
System.out.println(entity.getField());
This program prints "set using reflection". However, in the database the value set using reflection does not get updated:
SELECT * FROM ENTITY WHERE ID = 1
ID FIELD
1 set directly
This is strange. I could swear that this used to work - but now it isn't. Is it really so that you cannot manipulate entities using reflection?
I'm using EclipseLink 1.1.1 if that matters.
Changing values of an entity class by reflection is going to fraught with issues. This is because you're dealing with a class which is persistent and thus the persistence API needs to know about changes to the fields.
If you make changes via reflection, chances are the persistence API will not know about those changes.
A better solution would be to call the setters via reflection.
I'm pretty sure the Entity you are given by your persistence framework is actually wrapped in another class (possibly the same with stuff tacked on through reflection). Changing the field directly through reflection seems unlikely to work. You might want to check if there's a (generated) setter that you can use. Although if you're going that route one might ask why you don't allow callers to call the setter directly?
Your class might be instrumented and the setters responsible for recording changes. I'm not familiar with EclipseLink to check if the class returned by myDao.getEntity(id); is your actual class of a sub-class generated by EclipseLink.

Remove attribute from object

I got a list that return from below db call.
List<employee> list = empolyeeRepository.findByEmployeeId(id);
List contains employee pojo class object. I want to remove one attribute let's say "employee bank account no" when returning from rest call.
#RequestMapping(value = "/employeeInformation/{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
#Timed
public List<Employee> getEmployeeInformation(#PathVariable("id") String id) throws Exception {
return empolyeeRepository.findByEmployeeId(id);
}
Is there any annotation or good practice to do that?
As it mentioned in comments above, you cant remove fields of compiled class at runtime. Assuming you have to exclude some field from generated json, there I see two options:
Create a class with fields you want to be present in resulting json, copy required values from original object to a new created. This approach is called view model and allows you to decorate some object's data, hiding sensitive data from being exposed.
Depending on implementation of your serializer there may be annotations to exclude fields. #JsonIgnore may be placed on getter method, if you are using Jackson (default in spring boot). Second aproach requires significant less code, but the first one is more flexible.
Try #JsonIgnore to ignore properties from serialization and de-serialization.
Here is the link to the docs

(Spring / Hibernate) Initialize children objects strategy

I have a form that should be bind to a complex object that wrap a lot of children, every time before loading this form I have to initialize all children object in a method that only have a lot of new statements and calling a setter method, I have to repeat this scenario for a lot of forms and other complex objects
Is there a better strategy than the initializeEmployee method?
For example:
#Entity
public class Employee {
Integer Id;
Contract contract;
Name name;
List<Certificate> list;
// getter and setters
}
#Entity
public class Contract {
String telephoneNum;
String email;
Address address;
// getter and setters
}
#Entity
public class Address {
String streetName;
String streetNum;
String city;
}
public class Name {
String fName;
String mName;
String lName;
// getter and setters
}
// And another class for certificates
public initializeEmployee() {
Employee emplyee = new Employee();
Name name = new Name();
employee.setName(name);
Contract contract = new Contract();
Address address = new Address();
contract.setAddress(address);
employee.setContract(contract);
// set all other employee inner objects,
}
EDIT:
According to below answers, it seems that there is no optimal answer. However, I could use the Entity constructor or a Factory Design Pattern.
But both solutions don't solve my other problem in initializing all fields strategy with Required and Optional fields.
For example:
If I have Name as required (i.e. the Employee entity will not persisted if Name object attributes are empty, on the other side the Contract entity is an optional. and I cannot persist an empty Contract object to the database, so I have to make it null first before persistence, then reinitialize it after persistence like the following
// Set Contract to null if its attributes are empty
Contract contract = employee.getContract()
if(contract.getTelephoneNum().isEmpty && contract.getEmail().isEmpty() && contract.getAddress().isEmpty()){
empolyee.setContract(null);
}
employeeDAO.persist(employee);
// reinitialize the object so it could binded if the the user edit the fields.
employee.setContract(new Contract());
You can add constructors (it is their role after all) to your entities to instanciate these fields if having a null value has no meaning for your case.
Another way, if you don't like adding contructors, is to add a static factory method to instanciate your bean which will look like initializeEmployee() but with potential parameters and returning an Employee object. http://en.wikipedia.org/wiki/Factory_method_pattern
Similarly, you can instanciate your collections too, as there is probably no meaning for a null collection (but there is one for an empty collection).
You can add behaviour to your entities, don't be locked in Anemic Domain Model which is considered an anti-pattern by Martin Fowler http://www.martinfowler.com/bliki/AnemicDomainModel.html
EDIT
I see you are using dao.persist(entity): you are probably using JPA. If so, maybe it is best to not modify your object graph (on the front side) and add an EntityListener (in the persistence layer) for Employee: here is a link for Hibernate EntityListener (it is a JPA feature, so if you are using another framework don't worry) http://docs.jboss.org/hibernate/entitymanager/3.5/reference/en/html/listeners.html
With an EntityListener, you can add small "aop like" actions before persistence and after. This will allow you to not deal with null values on the domain and front layers and will ensure that every entity fits in any case (better validation).
In PrePersist: you'all add your code to check null values (possibly with custom methods "isEmpty()" on the domain classes) and nullify fields if needed. In PostPersist you add your new object.
I couldn't get what you really need, but I think you could try this way:
#Entity
public class Employee {
Integer Id;
Contract contract = new Contract();
Name name = new Name();
List<Certificate> list;
// getter and setters
}
#Entity
public class Contract {
String telephoneNum;
String email;
Address address = new Address();
// getter and setters
}
I'm not sure it reduces the verbosity at all but since this is a UI issue, you could initialize the editable objects in your flow.xml and then put it all together in an Employee instance prior to saving to the DB.
<on-start>
<evaluate expression="new foo.bar.Name()" result="flowScope.employeeName" />
<evaluate expression="new foo.bar.Contract()" result="flowScope.contract" />
<evaluate expression="new foo.bar.Address()" result="flowScope.address" />
</on-start>
Actually I would advise against using Hibernate Entities directly in GUI. In many cases (I assume in yours too, but I'm missing some details on your use-case) it is useful to use a Data Transfer Object pattern instead. You can create such DTO that is GUI specific, has only those fields that you need, and the structure is only as complex as needed.
After specific user action (like save e.g.) use those DTOs (on event handling) to create your Entities that will be persisted.
Unless your case is that just entering the GUI screen causes Entities creation, then I would recommend Factory pattern.
Also note that in many cases initialization of component objects that are making up the main object (Employee in your example) are better to be initialized in constructor of main object, eg. if you expect that Contract cannot be null - initialize it in constructor. The same for the list of Certificates and others.

Categories

Resources