Reputation: 2790
I have a bean which has lot of property and half of them n to n relationship. I have also added additional transient 'isEdited' boolean to make sure user has done some changes so I can reduce db fetch for all entities. My current problem sometimes I need to serialize my bean and want to compare properties with real instance. First solution I have tried is from apache commons-lang => EqualsBuilder.reflectEqual();
method which doesnt works well for me because some of inherited items doesn`t implements equals method in right way and I can not edit them. I know I can add ajax listeners to update edited property or I can update each setter method or a new equals implementation. But I am looking for little bit more practical and generic way so I can use this implementation also in the future as well.
So what I want:( Lets say I have a simple bean):
public class Foo {
private String prop1;
private String prop2;
/**
* @return the prop1
*/
public String getProp1() {
return prop1;
}
/**
* @param prop1 the prop1 to set
*/
public void setProp1(String prop1) {
this.prop1 = prop1;
}
/**
* @return the prop2
*/
public String getProp2() {
return prop2;
}
/**
* @param prop2 the prop2 to set
*/
public void setProp2(String prop2) {
this.prop2 = prop2;
}
}
Than my bean will implement an EventListener
interface and this listener will handle all setters. In case of any setter method called (after initialization) then edited property will set to true.
So my question is what would be proper way to add action/event listener to handle setters?
EDIT** I have tried PropertyChangeListener but didnt get any event output. And if I add each setters pcs.firePropertyChange();
method then it doesnt make sense that I can set isEdited straight true via those methods.
Upvotes: 1
Views: 1832
Reputation: 10055
Can't you have a Map<String,Boolean>
and update it with each setter? Take prop1
, for example:
public void setProp1(String prop1) {
if(!prop1.equals(this.prop1))
updateMap.put("prop1", true);
this.prop1 = prop1;
}
This way, while saving the entity you'll have a map with that "metadata".
EDIT: There's a flaw tough, because if an user sets a value and then goes back to the original the map will stay in true. You can avoid that by having another map (or cloning your object) with the original values to comparate both things:
public void setProp1(String prop1) {
if(!prop1.equals(this.prop1) && !prop1.equals(ORIGINAL_VALUE))
updateMap.put("prop1", true);
else
updateMap.put("prop1", false);
this.prop1 = prop1;
}
EDIT: Adam is right about aspects and indeed it's a change that can cost you some time. Also, if you want to use reflection there's another catch because you're tied to the structure of the methods. In the future if you want to change a method's name or type you'll have to update that reflection-helper class too.
I'd suggest updating temporal values before saving them so you can compare them to the originals but that would involve changing your methods if you access the fields/object directly without a getter.
In the end, a third option (there's always a third) and the one I think you might be willing to do is using valueChangeListener
's in your components. For example:
<h:inputText id="prop1Input" value="#{bean.prop1}" valueChangeListener="#{bean.changesListener}"/>
And your bean should have a method with this structure:
public void changesListener(ValueChangeEvent e) {
//Get the new value
Object o = e.getNewValue();
//get the component's id
String componentId = e.getComponent().getId();
//Cast the value and compare it, after that you should update a map. You are
//going to need one anyway to know which field changed.
boolean valueChanged = //compare against original value
if(componentId.equals("prop1Input"));
map.put("prop1", valueChanged);
}
An important note about this approach, the listener will be executed during the validation phasethe. The intention of this listener is to be able to handle both new and old value, which seems to be your case. Since you can also know the component that fired it you should be fine.
Upvotes: 1