0

I tried to ask question here https://stackoverflow.com/questions/28493517/jsf-primefaces-editing-object-composed-of-2-tables but I'm afraid that it was not asked properly, that's way I am trying again, as simple as possible.

I have 2 tables:

nn:       id, name 
nn_descr: id, long_description

there is foreign key constraint between nn_descr.id and nn.id.

In general, one table contains data that can be searched and managed fairly quickly via db engine compared to nn_descr table.

And now I'd like to manage that data not in the way all CRUD creators do. I'd like to create 2 related records in my tables via one submit on JSF form consisting on text-box and text-area referenced to nn.name and descr.long_description respectively.

Without JPA I would insert record to the first table, read identity value and insert the data to the second table using identity value I already read.

With JPA I should somehow tell that engine to magically persist the data. And here is the point that I don't know how to do that. Whatever I read refers to simple CRUD which is just to be easily generated via my Netbeans.

I must admit I am getting lost with this JPA, facades and all the stuff generated but I am trying to learn this by doing simple application

I managed to have records in both tables updateable. But when I try to insert the data I get:

/nn/Create.xhtml @21,81 value="#{nnController.nnDescr.descr}": Target Unreachable, 'null' returned null

I must be doing something wrong, but I don;t know where to find the answer, tutorial, book, whatever that would go slightly deeper than simple example with one flat table and CRUD in that table.

here is my JSF (as basic as possible):

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:f="http://xmlns.jcp.org/jsf/core">

  <ui:composition template="/template.xhtml">
    <ui:define name="title">
      <h:outputText value="#{boundle.CreateNnTitle}"></h:outputText>
    </ui:define>
    <ui:define name="body">
      <h:panelGroup id="messagePanel" layout="block">
        <h:messages errorStyle="color: red" infoStyle="color: green" layout="table"/>
      </h:panelGroup>
      <h:form>
        <h:panelGrid columns="2">
          <h:outputLabel value="#{boundle.CreateNnLabel_nnName}" for="nnName" />
          <h:inputText id="nnName" value="#{nnController.selected.nnName}" title="#{boundle.CreateNnTitle_nnName}" />
          <h:outputLabel value="#{boundle.CreateNnLabel_nnDescr}" for="nnDescr" />
          <h:inputTextarea id="nnDescr" value="#{nnController.selected.nnDescr.descr}" />
        </h:panelGrid>
        <br />
        <h:commandLink action="#{nnController.create}" value="#{boundle.CreateNnSaveLink}" />
        <br />
        <br />
        <h:commandLink action="#{nnController.prepareList}" value="#{boundle.CreateNnShowAllLink}" immediate="true"/>
        <br />
        <br />
        <h:link outcome="/index" value="#{boundle.CreateNnIndexLink}"/>
      </h:form>
    </ui:define>
  </ui:composition>

and controller

package app;

import app.util.JsfUtil;
import app.util.PaginationHelper;

import java.io.Serializable;
import java.util.ResourceBundle;
import javax.ejb.EJB;
import javax.inject.Named;
import javax.enterprise.context.SessionScoped;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;
import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;
import javax.faces.model.SelectItem;

@Named("nnController")
@SessionScoped
public class NnController implements Serializable {

  private Nn current;
  private NnDescr nnDescr;

  private DataModel items = null;
  @EJB
  private app.NnFacade ejbFacade;


  private PaginationHelper pagination;
  private int selectedItemIndex;

  public NnController() {
  }

  public Nn getSelected() {
    if (current == null) {
      current = new Nn();

      selectedItemIndex = -1;
    }
    return current;
  }

  private NnFacade getFacade() {
    return ejbFacade;
  }

  public PaginationHelper getPagination() {
    if (pagination == null) {
      pagination = new PaginationHelper(10) {

        @Override
        public int getItemsCount() {
          return getFacade().count();
        }

        @Override
        public DataModel createPageDataModel() {
          return new ListDataModel(getFacade().findRange(new int[]{getPageFirstItem(), getPageFirstItem() + getPageSize()}));
        }
      };
    }
    return pagination;
  }

  public String prepareList() {
    recreateModel();
    return "List";
  }

  public String prepareView() {
    current = (Nn) getItems().getRowData();
    selectedItemIndex = pagination.getPageFirstItem() + getItems().getRowIndex();
    return "View";
  }

  public String prepareCreate() {
    current = new Nn();


    selectedItemIndex = -1;
    return "Create";
  }

  public String create() {
    try {
      getFacade().create(current);
      JsfUtil.addSuccessMessage(ResourceBundle.getBundle("/Boundle").getString("NnCreated"));
      return prepareCreate();
    } catch (Exception e) {
      JsfUtil.addErrorMessage(e, ResourceBundle.getBundle("/Boundle").getString("PersistenceErrorOccured"));
      return null;
    }
  }

  public String prepareEdit() {
    current = (Nn) getItems().getRowData();
    selectedItemIndex = pagination.getPageFirstItem() + getItems().getRowIndex();
    return "Edit";
  }

  public String update() {
    try {
      getFacade().edit(current);
      JsfUtil.addSuccessMessage(ResourceBundle.getBundle("/Boundle").getString("NnUpdated"));
      return "View";
    } catch (Exception e) {
      JsfUtil.addErrorMessage(e, ResourceBundle.getBundle("/Boundle").getString("PersistenceErrorOccured"));
      return null;
    }
  }

  public String destroy() {
    current = (Nn) getItems().getRowData();
    selectedItemIndex = pagination.getPageFirstItem() + getItems().getRowIndex();
    performDestroy();
    recreatePagination();
    recreateModel();
    return "List";
  }

  public String destroyAndView() {
    performDestroy();
    recreateModel();
    updateCurrentItem();
    if (selectedItemIndex >= 0) {
      return "View";
    } else {
      // all items were removed - go back to list
      recreateModel();
      return "List";
    }
  }

  private void performDestroy() {
    try {
      getFacade().remove(current);
      JsfUtil.addSuccessMessage(ResourceBundle.getBundle("/Boundle").getString("NnDeleted"));
    } catch (Exception e) {
      JsfUtil.addErrorMessage(e, ResourceBundle.getBundle("/Boundle").getString("PersistenceErrorOccured"));
    }
  }

  private void updateCurrentItem() {
    int count = getFacade().count();
    if (selectedItemIndex >= count) {
      // selected index cannot be bigger than number of items:
      selectedItemIndex = count - 1;
      // go to previous page if last page disappeared:
      if (pagination.getPageFirstItem() >= count) {
        pagination.previousPage();
      }
    }
    if (selectedItemIndex >= 0) {
      current = getFacade().findRange(new int[]{selectedItemIndex, selectedItemIndex + 1}).get(0);
    }
  }

  public DataModel getItems() {
    if (items == null) {
      items = getPagination().createPageDataModel();
    }
    return items;
  }

  private void recreateModel() {
    items = null;
  }

  private void recreatePagination() {
    pagination = null;
  }

  public String next() {
    getPagination().nextPage();
    recreateModel();
    return "List";
  }

  public String previous() {
    getPagination().previousPage();
    recreateModel();
    return "List";
  }

  public SelectItem[] getItemsAvailableSelectMany() {
    return JsfUtil.getSelectItems(ejbFacade.findAll(), false);
  }

  public SelectItem[] getItemsAvailableSelectOne() {
    return JsfUtil.getSelectItems(ejbFacade.findAll(), true);
  }

  public Nn getNn(java.lang.Integer id) {
    return ejbFacade.find(id);
  }

  public NnDescr getNnDescr() {
    return nnDescr;
  }

  public void setNnDescr(NnDescr nnDescr) {
    this.nnDescr = nnDescr;
  }




  @FacesConverter(forClass = Nn.class)
  public static class NnControllerConverter implements Converter {

    @Override
    public Object getAsObject(FacesContext facesContext, UIComponent component, String value) {
      if (value == null || value.length() == 0) {
        return null;
      }
      NnController controller = (NnController) facesContext.getApplication().getELResolver().
              getValue(facesContext.getELContext(), null, "nnController");
      return controller.getNn(getKey(value));
    }

    java.lang.Integer getKey(String value) {
      java.lang.Integer key;
      key = Integer.valueOf(value);
      return key;
    }

    String getStringKey(java.lang.Integer value) {
      StringBuilder sb = new StringBuilder();
      sb.append(value);
      return sb.toString();
    }

    @Override
    public String getAsString(FacesContext facesContext, UIComponent component, Object object) {
      if (object == null) {
        return null;
      }
      if (object instanceof Nn) {
        Nn o = (Nn) object;
        return getStringKey(o.getId());
      } else {
        throw new IllegalArgumentException("object " + object + " is of type " + object.getClass().getName() + "; expected type: " + Nn.class.getName());
      }
    }

  }
}

Nn.java

package app;

import java.io.Serializable;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.validation.constraints.Size;


@Entity
@Table(name = "nn")
@NamedQueries({
  @NamedQuery(name = "Nn.findAll", query = "SELECT n FROM Nn n"),
  @NamedQuery(name = "Nn.findById", query = "SELECT n FROM Nn n WHERE n.id = :id"),
  @NamedQuery(name = "Nn.findByNnName", query = "SELECT n FROM Nn n WHERE n.nnName = :nnName")})
public class Nn implements Serializable {
  private static final long serialVersionUID = 1L;
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Basic(optional = false)
  @Column(name = "id")
  private Integer id;
  @Size(max = 50)
  @Column(name = "nn_name")
  private String nnName;
  @OneToOne(cascade = CascadeType.ALL, mappedBy = "nn")
  private NnDescr nnDescr;

  public Nn() {
  }

  public Nn(Integer id) {
    this.id = id;
  }

  public Integer getId() {
    return id;
  }

  public void setId(Integer id) {
    this.id = id;
  }

  public String getNnName() {
    return nnName;
  }

  public void setNnName(String nnName) {
    this.nnName = nnName;
  }

  public NnDescr getNnDescr() {
    return nnDescr;
  }

  public void setNnDescr(NnDescr nnDescr) {
    this.nnDescr = nnDescr;
  }

  @Override
  public int hashCode() {
    int hash = 0;
    hash += (id != null ? id.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 Nn)) {
      return false;
    }
    Nn other = (Nn) object;
    if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
      return false;
    }
    return true;
  }

  @Override
  public String toString() {
    return "app.util.Nn[ id=" + id + " ]";
  }

}

As you see the data is generated. I just modified generated JSF in order to have textarea for putting the description.

I am trying to learn and spend a lot of time trying to figure out this but nothing works. It cannot be that difficult. I just don;t get something obvious.

Thank you in advance for your help.

If possible I would greatly appreciate if somebody could post somewhere complete example of how to deal with problem like that.

Community
  • 1
  • 1
norbi771
  • 814
  • 2
  • 12
  • 29
  • Your question is really long winded. It was sufficient to just post a http://stackoverflow.com/help/mcve along with the exception+stack. Exceptions represent the whole answer at their own. It's just a matter of being able to interpret it and translate into layman's terms. – BalusC Feb 20 '15 at 14:06

2 Answers2

0

value="#{nnController.nnDescr.descr}": Target Unreachable, 'null' returned null

This means that either #{nnController} or #{nnController.nnDescr} returned null and hence EL isn't able to call the setter for descr on it in order to update the model with the submitted input value.

Given the fact that other references to #{nnController} work fine, it'll be that #{nnController.nnDescr} returned null. Indeed, you're nowhere preparing it in your bean.

Fix it accordingly:

@PostConstruct
public void init() {
    nnDescr = new NnDescr();
}

Unrelated to the concrete problem, that bean should really be view scoped, not session scoped. See also How to choose the right bean scope?

That said, you'd better start off with a real book in order to learn JSF based on real instructions, explanations, workable snippets/examples, instead of clueless gazing at autogenerated code without having any fundamental knowledge/experience. You can find links at our JSF wiki page.

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • I was kind of desperate and wanted to explain in fully where the problem I have comes from, because I was afraid that I did some kind of simple mistake. And I admit I lack fundamental knowledge/experience. I have problems understanding how these beans + facades work in practice. I understand and write typical Java code, but this somehow I cannot catch. When I read the book most is logical, but when I face the problem I can't use what I read. It is hard to understand why, since I do programming (still mainly in PHP) for living. Thank you for the comments and link, I will use it. – norbi771 Feb 20 '15 at 15:09
  • Once you have read a decent book, you should be able to eliminate irrelevant code into a minimal verifiable compilable example. Or, if you can think logically, try removing step by step as much as possible small pieces of code (fields, tags, attributes, etc) and re-running the remainder as long as the problem still exhibits. Once the problem disappears, or changes into a different problem, then undo the removal and continue removing other pieces until you can't remove anything else anymore. Then just post exactly that code along with expectation and unexpectation (the error). No story needed. – BalusC Feb 20 '15 at 15:28
0

Thank you, the @PostConstruct is the right answer to my question. Another issue is how to do JPA mapping properly but it is subject of another question.

norbi771
  • 814
  • 2
  • 12
  • 29