简体   繁体   English

为什么 Java Server Faces 中的 h:dataTable 不会在表格行中呈现 h:inputText

[英]Why h:dataTable in Java Server Faces doesn't render h:inputText in the table rows

I am a newbie to Java EE and JSF and I spent days on this issue without reaching any conclusion.我是 Java EE 和 JSF 的新手,我在这个问题上花了几天时间没有得出任何结论。 Now I am hoping someone can give me a guidance.I have searched this forum on this problem, found some useful answers but nothing that could solve my problem.现在我希望有人能给我指导。我已经在这个论坛上搜索了这个问题,找到了一些有用的答案,但没有什么可以解决我的问题。

I have a JSF page showing "parts" (part name, part number, part description, etc.) in a tabulated from using the h:dataTable tag.我有一个 JSF 页面,在使用 h:dataTable 标签的表格中显示“部件”(部件名称、部件编号、部件描述等)。 The rows all have update and delete links.这些行都有更新和删除链接。 When I click on the link for "update" for a given row, the boolean flag never toggles from 'false' to 'true' so for the h:inputText to render in the row, where I can update the part information.当我单击给定行的“更新”链接时,boolean 标志永远不会从“假”切换到“真”,因此 h:inputText 在行中呈现,我可以在其中更新零件信息。 I see through the logs that flag is toggle from 'false' to 'true' but when the control returns from the bean to the JSF page, somehow (mysteriously) the flag toggles back to 'false', therefore, the associated h:inputText for update does not render and I cannot update the part information in that row.我通过日志看到标志从'false'切换到'true'但是当控件从bean返回到JSF页面时,不知何故(神秘地)标志切换回'false',因此,关联的h:inputText for update 不会呈现,我无法更新该行中的零件信息。
The JSF page JSF页面

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE html>
<html lang="en"
      xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:f="http://xmlns.jcp.org/jsf/core"
      xmlns:ui="http://xmlns.jcp.org/jsf/facelets">

    <h:head>
        <title>This is a part number list service</title>
        <h:outputStylesheet library="css" name="cssLayout.css"  />
    </h:head>
    <h:body>
        <h:form>
            <h2> You are authorized to use the Part number List Service</h2>
            <p style="text-align: center">
                <h:dataTable value ="#{partList.parts}" var="p"
                             styleClass="part-table"
                             headerClass="part-table-header"
                             rowClasses="part-table-odd-row,part-table-even-row"
                             border="10"
                             >
                    <f:facet name="caption">
                        <h3> <h:outputText value="#{bundle.caption}" /> </h3>
                    </f:facet>
                    <p></p>    
                    <h:column>
                        <f:facet name="header">Part Name</f:facet>
                            #{p.name}
                        <h:inputText value = "#{p.name}"
                                     size = "5" rendered = "#{p.canUpdate}" />
                        <h:outputText value = "#{p.name}"
                                      rendered = "#{not p.canUpdate}" />
                    </h:column>
                    <h:column>
                        <f:facet name="header">Part Manufacture</f:facet>
                        <h:inputText value = "#{p.manufacture}"
                                     size = "10" rendered = "#{p.canUpdate}" />
                        <h:outputText value = "#{p.manufacture}"
                                      rendered = "#{not p.canUpdate}" />
                    </h:column>
                    <h:column>
                        <f:facet name="header">Part Number</f:facet>
                        <h:inputText value = "#{p.number}"
                                     size = "15" rendered = "#{p.canUpdate}" />
                        <h:outputText value = "#{p.number}"
                                      rendered = "#{not p.canUpdate}" />
                    </h:column>
                    <h:column>
                        <f:facet name="header">Part Description</f:facet>
                        <h:inputText value = "#{p.description}"
                                     size = "10" rendered = "#{p.canUpdate}" />
                        <h:outputText value = "#{p.description}"
                                      rendered = "#{not p.canUpdate}" />
                    </h:column>
                    <h:column>
                        <f:facet name="header">Price</f:facet>
                        <h:inputText value = "#{p.price}"
                                     size = "5" rendered = "#{p.canUpdate}" />
                        <h:outputText value = "#{p.price}"
                                      rendered = "#{not p.canUpdate}" />
                    </h:column>
                    <h:column>
                        <f:facet name = "header">Update</f:facet>
                        <h:commandLink value = "Update" 
                                       action = "#{partList.updateLinkAction(p)}" 
                                       rendered = "#{not p.canUpdate}">
                        </h:commandLink>
                    </h:column>
                    <h:column>
                        <f:facet name = "header">Delete</f:facet>
                        <h:commandLink value = "Delete" 
                                       action = "#{partList.deleteAction(p)}" 
                                       rendered = "#{not p.canUpdate}">
                        </h:commandLink>
                    </h:column>
                </h:dataTable>
            </p>
                <p></p>
                <h:commandButton id="back"
                                 value="Logout"
                                 action="auth" />
                <h:commandButton id="update"
                                 value="Save updates"
                                 action="#{partList.saveUpdate}" />
                

            <h2> Add new part</h2>
            <table>
                <tr>
                    <td>Part Name</td>
                    <td><h:inputText id="addpartname" size="10" value="#{partList.partName}" /></td>
                </tr>
                <tr>
                    <td>Part Manufacture</td>
                    <td><h:inputText id="addpartmanufacture" size="10" value="#{partList.partManufacture}" /></td>
                </tr>
                <tr>
                    <td>Part Number</td>
                    <td><h:inputText id="addpartnumbere" size="10" value="#{partList.partNumber}" /></td>
                </tr>
                <tr>
                    <td>Part Description</td>
                    <td><h:inputText id="addpartdescription" size="10" value="#{partList.partDescription}" /></td>
                </tr>
                <tr>
                    <td>$Price</td>
                    <td><h:inputText id="addpartprice" size="10" value="#{partList.partPrice}" /></td>
                </tr>
            </table>
            <p></p>
            <h:commandButton id="addparttolist" 
                             value="Add part"
                             action="#{partList.addAction}" />
        </h:form>

    </h:body>
</html>

Here is the managed bean that is annotated with @SessionScope这是使用 @SessionScope 注释的托管 bean

@FacesConfig
@Named
@SessionScoped

public class PartList implements Serializable {

    @EJB
    RequestSessionBean request;

    private List<Part> parts;
    private String partName;
    private String partNumber;
    private String partManufacture;
    private String partDescription;
    private float price;
    private static final Logger logger = LoggerUtil.getLogger(PartList.class.getName(),
            "C:\\Logs\\log.txt");

    private static final long serialVersionUID = 1000L;

    private void update(Part p) {
        logger.entering(PartList.class.getName(), "update");

        if (p.getCanUpdate()) {
            try {
                logger.log(Level.INFO, "NB: Updating part. Part number:{0} Part name: {1} can update: {2}",
                        new Object[]{p.getNumber(), p.getName(), p.getCanUpdate()});
                request.removePart(p.getId());
                request.addPart(p);
                parts = request.getAllParts();
            } catch (Exception e) {
                logger.log(Level.SEVERE, "NB: something went wrong", e);
                throw (new EJBException(e));
            }
            logger.exiting(PartList.class.getName(), "NB: leaving update()");
        }
    }

    public PartList() {
        logger.log(Level.INFO, "NB PartList default constructor called");
    }

    public void setPartName(String n) {
        partName = n;
    }

    public String getPartName() {
        return partName;
    }

    public void setPartNumber(String n) {
        partNumber = n;
    }

    public String getPartNumber() {
        return partNumber;
    }

    public String getPartManufacture() {
        return partManufacture;
    }

    public void setPartManufacture(String n) {
        partManufacture = n;
    }

    public void setPartDescription(String p) {
        partDescription = p;
    }

    public String getPartDescription() {
        return partDescription;
    }

    public void setPartPrice(float p) {
        price = p;
    }

    public float getPartPrice() {
        return price;
    }

    public List getParts() {
        try {
            parts = request.getAllParts();
        } catch (Exception e) {
        }
        return parts;
    }

    public String deleteAction(Part p) {
        request.removePart(p.getId());
        parts = request.getAllParts();
        return null;
    }

    public String addAction() {
        Part p = new Part(partName, partManufacture, partNumber, partDescription, price);
        request.addPart(p);
        parts = request.getAllParts();
        clearDataField(); //clear the form data after add operation
        return null;
    }

    private void clearDataField() {
        this.partDescription = null;
        this.partManufacture = null;
        this.partName = null;
        this.partNumber = null;
        this.price = 0;
    }

    public String updateLinkAction(Part p) { //update link clicked
        logger.entering(PartList.class.getName(), "updateLinkAction");
        logger.log(Level.INFO, "NB: Updating part. Part number:{0} Part name: {1} can update: {2}",
                new Object[]{p.getNumber(), p.getName(), p.getCanUpdate()});
        p.setCanUpdate(true);
        logger.log(Level.INFO, "Can update is now :{0}",
                p.getCanUpdate());
        logger.exiting(PartList.class.getName(), "updateLinkAction");
        return null; //This allows the page flow remain on the same page
    }

    public String saveUpdate() {
        parts.stream()
                .forEach(e -> e.setCanUpdate(false));
        parts.stream()
                .filter(e -> e.getCanUpdate())
                .forEach(e -> update(e));

        return null;
    }

}

Here is a singleton session bean that is used to initially populate the table in the database from an XML file using JAXB. Here is a singleton session bean that is used to initially populate the table in the database from an XML file using JAXB.

@Singleton
@Startup
//This bean will be managed by the container automatically since it is a singleton. The container calls 
//the method that has a @PostConstruct annotation. This is ideal to perform initial data load into the
// the data store
public class DataLoaderSessionBean {
    @EJB
    private RequestSessionBean request;
    private final static String logPath="C:\\Logs\\log.txt";
    private final static String dataPath="c:\\Logs\\Parts.xml";
    private final static Logger logger=LoggerUtil.getLogger(DataLoaderSessionBean.class.getName(), logPath);
    private void saveData(){
        logger.entering(DataLoaderSessionBean.class.getName(),"saveData()");
         try {
            List<Part> parts = request.getAllParts();
            JAXBContext context = JAXBContext
                    .newInstance(PartWrapper.class);
            Marshaller m = context.createMarshaller();
            m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

            // Wrapping our person data.
            PartWrapper wrapper = new PartWrapper();
            wrapper.setParts(parts);

            // Marshalling and saving XML to the file.
            m.marshal(wrapper, new File(dataPath));
            m.marshal(parts, System.out);

        } catch (JAXBException e) {
            e.printStackTrace();
        }
        logger.exiting(DataLoaderSessionBean.class.getName(),"saveData()");
    }
     private List<Part> loadData(){
        logger.entering(DataLoaderSessionBean.class.getName(),"loadData()");
         List<Part> parts=null;
         try {
            JAXBContext context = JAXBContext
                    .newInstance(PartWrapper.class);
            Unmarshaller um = context.createUnmarshaller();
            PartWrapper wrapper = (PartWrapper) um.unmarshal(new File(dataPath));
            parts = wrapper.getParts();

        } catch (JAXBException e) {
            e.printStackTrace();
        }
        logger.exiting(DataLoaderSessionBean.class.getName(),"loadData()");
        return parts;
    }

    @PostConstruct
    public void createData(){
        request.addParts(loadData());
    }
    
     @PreDestroy
    public void deleteData() {
        saveData();
    }
    
}

Here is the Entity bean for the model这是 model 的实体 bean

@Entity
@Table(name = "PERSISTENCE_PART")
@NamedQuery(
        name = "findAllParts",
        query = "SELECT p FROM Part p "
        + "ORDER BY p.number"
)
@SessionScoped

public class Part implements Serializable {

    private static final long serialVersionUID = 1001L;
    private static final String logPath="C:\\Logs\\log.txt";
    private static final Logger logger = LoggerUtil.getLogger(Part.class.getName()
             ,logPath);
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    @NotNull
    private String name;
    @NotNull
    private String manufacture;
    @NotNull
    private String number;
    @NotNull
    private String description;
    @NotNull
    private float price;
    //@Transient
    // @XmlTransient
    private boolean canUpdate;

    public Part() {

    }

    public Part(String n, String m, String nu, String d, float p) {
        name = n;
        manufacture = m;
        number = nu;
        description = d;
        price = p;
    }

    public void setId(Long l) {
        id = l;
    }

    public Long getId() {
        return id;
    }

    public void setName(String n) {
        name = n;
    }

    public String getName() {
        return name;
    }

    public void setNumber(String n) {
        number = n;
    }

    //  @Id
    //  @Column(nullable = false)
    public String getNumber() {
        return number;
    }

    public void setManufacture(String n) {
        manufacture = n;
    }

    public String getManufacture() {
        return manufacture;
    }

    public void setDescription(String d) {
        description = d;
    }

    public String getDescription() {
        return description;
    }

    public void setPrice(float p) {
        price = p;
    }

    public float getPrice() {
        return price;
    }

    public void setCanUpdate(boolean b) {
        logger.log(Level.INFO, "NB: Part.setCanUpdate() for part name {0} with id {1} and canUpdate {2}",
                 new Object[]{this.name, this.id, b});
        canUpdate = b;
    }

    public boolean getCanUpdate() {
        logger.log(Level.INFO, "NB: Part.getCanUpdate() for Part name {0} with id {1} and CanUpate {2}",
                 new Object[]{this.name, this.id, this.canUpdate});
        return canUpdate;
    }
}

Here is the session bean that interacts with the database.这是与数据库交互的 session bean。

@Stateful
public class RequestSessionBean {

    // Add business logic below. (Right-click in editor and choose
    // "Insert Code > Add Business Method")
    @PersistenceContext
    private EntityManager em;
    
    public void createPart(String name,
            String manufacture,
            String partNumber,
            String description,
            float price) {
        try {
            Part part = new Part(name,
                    manufacture,
                    partNumber,
                    description,
                    price);
            em.persist(part);
        } catch (Exception ex) {
            throw new EJBException(ex.getMessage());
        }
    }
    public void addParts(List<Part> parts){
        parts.stream()
                .forEach(e->addPart(e));
    }
    public void addPart(Part p){
        try {
            em.persist(p);
        } catch (Exception ex) {
            throw new EJBException(ex.getMessage());
        }
    }
    public List<Part> getAllParts() {
        List<Part> parts = (List<Part>) em.createNamedQuery("findAllParts").getResultList();
        return parts;
    }
    public Part getPart(String partNumber){
        Part p=null;
        try {
            p  = em.find(Part.class, partNumber);
        } catch (Exception e) {
            throw new EJBException(e.getMessage());
        }
        return p;
    }
    public void removePart(Long id) {
        try {
            Part p = em.find(Part.class, id);
            em.remove(p);
        } catch (Exception e) {
            throw new EJBException(e.getMessage());
        }
    }
}

Here is a data wrapper for the JAXB marshalling/unmarshalling这是 JAXB 编组/解组的数据包装器

@XmlRootElement(name="PartWrapper")
public class PartWrapper {
    List<Part> parts;
    public List<Part> getParts(){
        return parts;
    }
    @XmlElement(name="part")
    public void setParts(List<Part> p){
        parts=p;
    }
    public void add(Part p){
        if(parts==null){
            parts = new ArrayList();
        }
        parts.add(p);
    }
}

Here is a simple utility class for logging这是一个用于记录的简单实用程序 class

public class LoggerUtil {

    public static Logger getLogger(String className, String pathToLogFile) {
        Logger logger = Logger.getLogger(className);
        for (Handler h : logger.getHandlers()) {
            logger.removeHandler(h);
        }
            logger.addHandler(getFileHandler(pathToLogFile));
            logger.addHandler(getConsoleHandler());
            logger.setLevel(Level.FINER);

        return logger;
    }
    private static Handler getConsoleHandler(){
         ConsoleHandler handle = new ConsoleHandler();
        handle.setLevel(Level.FINER);
        handle.setFormatter(new SimpleFormatter());
        return handle;
    }
    private static Handler getFileHandler(String pathToLogFile){
        Handler handle=null;
        try {
            handle = new FileHandler(pathToLogFile,10240,1, true);
            handle.setLevel(Level.FINER);
            handle.setFormatter(new SimpleFormatter());

        } catch (IOException | SecurityException e) {
        }
        return handle;
    }
}

Here is the initial data load for the database这是数据库的初始数据加载

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<PartWrapper>
    <part>
        <canUpdate>false</canUpdate>
        <description>EGR Valve</description>
        <id>7</id>
        <manufacture>Emission Systems</manufacture>
        <name>EGR Valve</name>
        <number>EV4301766ES</number>
        <price>29.95</price>
    </part>
    <part>
        <canUpdate>false</canUpdate>
        <description>Carborator float</description>
        <id>6</id>
        <manufacture>Duke Carboration</manufacture>
        <name>Floater Element</name>
        <number>FE3511029DC</number>
        <price>3.99</price>
    </part>
</PartWrapper>

The web.xml web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="4.0" 
         xmlns="http://xmlns.jcp.org/xml/ns/javaee" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd">
    <context-param>
        <param-name>javax.faces.PROJECT_STAGE</param-name>
        <param-value>Development</param-value>
    </context-param>
    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>*.xhtml</url-pattern>
    </servlet-mapping>
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
    <welcome-file-list>
        <welcome-file>showpartnumer.xhtml</welcome-file>
    </welcome-file-list>
</web-app>

I have two other versions of this application: One version uses an XML file as the datastore.我有这个应用程序的其他两个版本:一个版本使用 XML 文件作为数据存储。 Another version uses a Java Collection to keep the data in memory.另一个版本使用 Java 集合将数据保留在 memory 中。 They both work fine, however, once I created this version to use the database this problem with updating rows immerged.它们都可以正常工作,但是,一旦我创建了这个版本来使用数据库,这个问题就会出现更新浸入的行。
Thanks for any help谢谢你的帮助

Today, I looked into your code and it seems like an environment setup.今天,我查看了您的代码,它似乎是一个环境设置。 I was able to update a part by clicking on the update link.我可以通过单击更新链接来更新零件。 Currently, your code has many pitfalls that might lead to errors preventing the container from rendering your application.目前,您的代码存在许多可能导致错误阻止容器呈现您的应用程序的缺陷。 You need to make sure to catch and log exceptions in your singleton.您需要确保在 singleton 中捕获并记录异常。 They are container-managed and they should not throw exceptions.它们是容器管理的,它们不应该抛出异常。 In addition, before adding rows to the database, ensure the row does not exist.此外,在向数据库添加行之前,请确保该行不存在。 For instance, when you shut down the Glassfish server, it does not automatically shut down the DB servers, so upon server re-start, your tables from the previous run still in the database, and your code will try to load the rows once again from an XML file, which will lead to duplicate key exceptions;例如,当您关闭 Glassfish 服务器时,它不会自动关闭数据库服务器,因此在服务器重新启动时,您之前运行的表仍在数据库中,您的代码将尝试再次加载行来自 XML 文件,这将导致重复密钥异常; hence the Singleton let these exceptions to bubble up to the container level and your application will never start.因此 Singleton 让这些异常冒泡到容器级别,您的应用程序将永远不会启动。

In addition, during the testing phase, when you manually adding data into DB from an XML file, it would be better to have the application to generate the PK for the entities to eschew duplicate primary key errors.此外,在测试阶段,当您从 XML 文件手动将数据添加到 DB 时,最好让应用程序为实体生成 PK 以避免重复的主键错误。 For instance, when using Netbeans IDE in concert with Apache Derby, every time the DB server is recycled the pk generation starts from a set initial value after the application is up and running.例如,当 Netbeans IDE 与 Apache Derby 一起使用时,每次回收 DB 服务器时,都会从应用程序后设置的初始值开始生成并运行 pk。 In your case, say you loaded a row from an XML with PK=1, then on your next add from the application's JSF page, there will be a PK conflict with this existing row.在您的情况下,假设您从 PK=1 的 XML 加载了一行,然后在下一次从应用程序的 JSF 页面添加时,将与该现有行发生 PK 冲突。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM