0

In my Entity class I have a HashMap. Now I'm trying to create a Select of this Map to be able to select on of the objects. So I created following classes:

HorseConverter:

@Named
public class HorseConverter implements Converter{

    @EJB
    private HorseBean bean;

    @Override
    public Object getAsObject(FacesContext context, UIComponent component, String value) {
        return bean.getHorse(Long.valueOf(value));
    }

    @Override
    public String getAsString(FacesContext context, UIComponent component, Object value) {
        if(!(value instanceof Horse)){
            throw new ConverterException(new FacesMessage("Object is not a Horse"));
        } else {
            Horse h = (Horse) value;
            return Long.toString(h.getId());
        }
    }

}

Race Entity:

public Map<Horse, Integer> getHorses() {
        return horses;
    }

    public void setHorses(HashMap<Horse, Integer> horses) {
        this.horses = horses;
    }

And my view:

Horse:
<h:selectOneMenu value="#{betController.horse}" converter="#{horseConverter}">
    <f:selectItems value="#{raceController.selectedRace.horses}" var="h" itemLabel="#{h.nickName}" itemValue="#{h}"/>
</h:selectOneMenu>

Seems like the value I'm getting isn't an instance of Horse. I checked the following link: https://stackoverflow.com/tags/selectonemenu/info So it seems that the key is automaticly used as value. But even writing h.key doesn't make a difference.

EDIT: Here is my hash and equals code from the Horse Entity:

@Override
    public int hashCode() {
        int hash = 7;
        hash = 97 * hash + (int) (this.id ^ (this.id >>> 32));
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Horse other = (Horse) obj;
        if (this.id != other.id) {
            return false;
        }
        return true;
    }
Community
  • 1
  • 1
VercauterenP
  • 71
  • 2
  • 8

2 Answers2

0

Have you overriden hashcode() and equals() in your Horse() object?

Your Converter needs equals() overriden in order to work. If you don't do this, only two references to the same instance of Horse() will be equal, rather than two seperate instances that have exactly the same state. Collections create an implicit copy to compare, so you won't have a single instance on the heap in this case.

Don't forget that the argument in the equals() object is Object(), NOT Horse().

If you don't override hashcode(), the hashcode will be different for every instance of Horse. This means that you will struggle to find the right Horse for comparison, even if your Horses are logically equivalent, because again, you'll have more than one instance that you will be comparing in order to find your Horse in your HashMap.

For further information, see this chapter of Effective Java by Joshua Bloch.

8bitjunkie
  • 12,793
  • 9
  • 57
  • 70
  • Not overriding them would result in `Validation Error: value is not valid` upon submit. The OP doesn't seem to be facing that problem. The OP is already facing a problem when just the page is presented. Note that converter's `getAsString()` is invoked, not `getAsObject()`. – BalusC Jan 17 '13 at 15:50
  • 1
    indeed as BalusC stated an equals and hash were already implemented. Edited the message. Thanks though – VercauterenP Jan 17 '13 at 16:55
0

You can't use var on a Map value. This specific <f:selectItems> construct works only if you use List<Horse> instead of Map<Horse, Integer>.

public List<Horse> getHorses() {
    return horses;
}

If you really want to use a Map, then you should be returning a Map<String, Horse>, where String is the nickname of the Horse.

public Map<String, Horse> getHorses() {
    return horses;
}

In case of using a Map value, don't forget to remove the var:

<f:selectItems value="#{raceController.selectedRace.horses}" />

The map's key becomes the option label and the map's value becomes the option value.


Unrelated to the concrete problem, a HashMap is by nature unordered. If you want to show the dropdown items in insertion order, rather use LinkedHashMap.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Well I'm not sure if my way of writing is correct but the Map I'm returning is from my RaceController which contains the Horse object and the amount people have been betting on that horse. I suppose what you're telling with the String value (nickname) being the key (which is not unique) is that I cannot use an object as key? – VercauterenP Jan 17 '13 at 16:33
  • Point is, you cannot use `` on a map value. If you want to use `var`, then you need e.g. `List`. Or, if you want to use `Map`, then remove the `var` (and `itemLabel` and `itemValue`) and fix the map structure so that it holds exactly the labels and values you need. – BalusC Jan 17 '13 at 16:43