value="#{nnController.nnDescr.descr}":目标无法到达,'null' 返回 null

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

我试着在这里提问 但我怕问的不对,我再试一次,越简单越好

我有 2 个 table:

nn:       id, name 
nn_descr: id, long_description

nn_descr.id 和 nn.id 之间存在外键约束。

一般来说,与 nn_descr table.

相比,table 包含可以通过数据库引擎相当快速地搜索和管理的数据

现在我想以不同于所有 CRUD 创建者的方式管理这些数据。我想在我的 table 中创建 2 条相关记录,方法是在 JSF 表单上提交一次,该表单包含分别引用 nn.name 和 descr.long_description 的文本框和文本区域。

如果没有 JPA,我会在第一个 table 中插入记录,读取标识值并使用我已经读取的标识值将数据插入第二个 table。

对于 JPA,我应该以某种方式告诉该引擎神奇地保留数据。 这就是我不知道该怎么做的要点。无论我读到什么,都指的是简单的 CRUD,它可以通过我的 Netbeans 轻松生成。

我必须承认我迷失了这个 JPA、外观和所有生成的东西,但我试图通过做简单的应用程序来学习它

我设法让两个 table 中的记录都可更新。但是当我尝试插入数据时,我得到:

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

我一定是做错了什么,但我不知道在哪里可以找到答案、教程、书籍,任何比简单示例更深入的东西,其中有一个平面 table 和 CRUD table.

这是我的 JSF(尽可能基本):

<?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>

和控制器

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 + " ]";
  }

}

如您所见,数据已生成。我刚刚修改了生成的 JSF,以便有用于放置描述的文本区域。

我正在努力学习并花了很多时间试图弄清楚这一点,但没有任何效果。它不可能那么困难。我只是不明白一些明显的事情。

预先感谢您的帮助。

如果可能的话,如果有人可以 post 某个完整的例子来说明如何处理这样的问题,我将不胜感激。

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

这意味着 #{nnController}#{nnController.nnDescr} 返回了 null,因此 EL 无法调用 setter for descr以便使用提交的输入值更新模型。

鉴于对 #{nnController} 的其他引用工作正常,#{nnController.nnDescr} 将返回 null。实际上,您无处可在您的 bean 中准备它。

相应地修复它:

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

与具体问题无关,那个 bean 确实应该是视图范围的,而不是会话范围的。另见 How to choose the right bean scope?

也就是说,您最好从一本真正的书开始,以便根据真正的说明、解释和可行的方法学习 JSF snippets/examples,而不是在没有任何基础知识的情况下盲目地盯着自动生成的代码 knowledge/experience。您可以在 our JSF wiki page.

找到链接

谢谢,@PostConstruct 是我问题的正确答案。 另一个问题是如何正确地进行 JPA 映射,但这是另一个问题的主题。