Reputation: 11
I'm displaying a list of Football Players from a database, which the user can select, and add to an ArrayList. Displaying all players works fine, but not for the selected ones. When I ask for the plain object or its hashcode, its returns its, but as soon as I reference a property pertaining to the Player entity, it gives an error. Anyone?
Backing bean
@Named
@SessionScoped
public class PlayerBean implements Serializable {
private static final long serializableUID = 1L;
@Inject
PlayerFacade facade;
private List<Player> allPlayers;
private List<Player> selectedPlayers;
public PlayerBean() {
}
public List<Player> getAllPlayers() {
return facade.findAll();
}
public List<Player> getSelectedPlayers() {
return selectedPlayers;
}
public void setSelectedPlayers(List<Player> selectedPlayers) {
this.selectedPlayers= selectedPlayers;
}
}
Entity Class
@Entity
@Table(name = "player")
@XmlRootElement
public class Player implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(name = "PlayerID")
private Integer playerID;
@Size(max = 45)
@Column(name = "Name")
private String name;
@Size(max = 45)
@Column(name = "Position")
private String position;
// @Max(value=?) @Min(value=?)//if you know range of your decimal fields consider using these annotations to enforce field validation
@Column(name = "Weight")
private BigDecimal weight;
public Player() {
}
public Player(Integer playerID) {
this.playerID = playerID;
}
public Integer getPlayerID() {
return playerID;
}
public void setPlayerID(Integer playerID) {
this.playerID = playerID;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPosition() {
return position;
}
public void setPosition(String position) {
this.position = position;
}
public BigDecimal getWeight() {
return weight;
}
public void setWeight(BigDecimal weight) {
this.weight = weight;
}
@Override
public int hashCode() {
int hash = 0;
hash += (playerID != null ? playerID.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof Player)) {
return false;
}
Player other = (Player) object;
if ((this.playerID == null && other.playerID != null) || (this.playerID != null && !this.playerID.equals(other.playerID))) {
return false;
}
return true;
}
@Override
public String toString() {
return "playerID=" + playerID + " ]";
}
}
JSF Page
<h:form id="allPlayers" style="text-align: center;" >
<h:selectManyCheckbox value="#{playerBean.selectedPlayers}">
<f:selectItems value="#{playerBean.allPlayers}" var="p" itemLabel="#{p.name} #{p.position} #{p.weight}" />
</h:selectManyCheckbox>
<h:commandButton value="Add to Team" action="team"/>
</h:form>
</p>
<p>
<h:form id="selectedPlayers" style="text-align: center;" >
<h:dataTable value="#{playerBean.selectedPlayers}" var="players">
<h:column>
#{players}
</h:column>
<h:column>
#{players.name}
</h:column>
</h:dataTable>
<h:commandButton value="Set Team" action="team"/>
</h:form>
Error Message
javax.el.ELException: /team.xhtml: The class 'java.lang.String' does not have the property 'name'.
at com.sun.faces.facelets.compiler.TextInstruction.write(TextInstruction.java:88)
at com.sun.faces.facelets.compiler.UIInstructions.encodeBegin(UIInstructions.java:82)
at com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.encodeRecursive(HtmlBasicRenderer.java:302)
at com.sun.faces.renderkit.html_basic.TableRenderer.renderRow(TableRenderer.java:385)
at com.sun.faces.renderkit.html_basic.TableRenderer.encodeChildren(TableRenderer.java:162)
at javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:894)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1856)
at javax.faces.render.Renderer.encodeChildren(Renderer.java:176)
at javax.faces.component.UIComponentBase.encodeChildren(UIComponentBase.java:894)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1856)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1859)
at javax.faces.component.UIComponent.encodeAll(UIComponent.java:1859)
at com.sun.faces.application.view.FaceletViewHandlingStrategy.renderView(FaceletViewHandlingStrategy.java:443)
at com.sun.faces.application.view.MultiViewHandler.renderView(MultiViewHandler.java:131)
at javax.faces.application.ViewHandlerWrapper.renderView(ViewHandlerWrapper.java:337)
at com.sun.faces.lifecycle.RenderResponsePhase.execute(RenderResponsePhase.java:120)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:219)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:647)
at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1682)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:318)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:160)
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:734)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:673)
at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:99)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:174)
at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:357)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:260)
at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:188)
at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:191)
at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:168)
at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:189)
at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:119)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:288)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:206)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:136)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:114)
at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:838)
at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:113)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:115)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:55)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:135)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:564)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:544)
at java.lang.Thread.run(Thread.java:724)
Upvotes: 1
Views: 6335
Reputation: 11742
You will understand your problem if you take into account the following concepts:
List<Player>
. As its values will be set via EL that's run-time and reflection-based. By the moment EL does its job type information of your collection will have already been erased and it'll be plain List
.itemValue
attribute of <f:selectItems>
tag so I think that it's written after form submit to its default value, most probably p,toString()
in your case.If you debug your code and see what's been submitted you'll see that your list was in fact populated by strings. Now you should understand the cause of the problem and the exception thrown, as there really is no name property in String
class.
As to the solution there are basically two things to be done in order to keep the things working:
itemValue
of <f:selectItems>
tag explicitly as itemValue="#{p}"
to tell JSF to expect your player instance;Converter
explicitly by using, for example converter
attribute of <h:selectManyCheckbox>
tag. This can be done by providing your own @FacesConverter
class, or, if you don't mind using the excellent OmniFaces JSF utility library, just use its SelectItemsConverter
as converter="omnifaces.SelectItemsConverter"
. This will enable JSF to fill in the specified class instances into your backed list.Upvotes: 1