为什么javaserverfaces中的h:datatable不在表行中呈现h:inputtext

bxpogfeg  于 2021-07-07  发布在  Java
关注(0)|答案(1)|浏览(296)

我是javaee和jsf的新手,在这个问题上我花了很多天没有得出任何结论。现在我希望有人能给我一个指导。我在这个论坛上搜索了这个问题,找到了一些有用的答案,但没有什么能解决我的问题。
我有一个jsf页面,使用h:datatable标记以表格形式显示“parts”(部件名称、部件号、部件描述等)。所有行都有更新和删除链接。当我单击给定行的“update”链接时,布尔标志从不从“false”切换到“true”,因此h:inputtext将在行中呈现,在该行中我可以更新零件信息。我在日志中看到,flag是从'false'切换到'true',但是当控件从bean返回到jsf页面时,不知何故(神秘地)flag会切换回'false',因此,用于更新的关联h:inputtext不会呈现,并且我无法更新该行中的部件信息。
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>

下面是用@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;
    }

}

下面是一个单例会话bean,它最初用于使用jaxb从xml文件填充数据库中的表。

@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();
    }

}

下面是模型的实体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;
    }
}

下面是与数据库交互的会话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());
        }
    }
}

这里是jaxb编组/解编的数据 Package 器

@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);
    }
}

下面是一个简单的日志实用程序类

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;
    }
}

下面是数据库的初始数据加载

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

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>

这个应用程序还有两个版本:一个版本使用xml文件作为数据存储。另一个版本使用java集合将数据保存在内存中。它们都可以很好地工作,但是,一旦我创建了这个版本来使用数据库,更新浸没的行就会出现这个问题。
谢谢你的帮助

rdlzhqv9

rdlzhqv91#

今天,我查看了您的代码,它看起来像是一个环境设置。我可以通过点击更新链接来更新一个部件。目前,您的代码有许多缺陷,这些缺陷可能导致容器无法呈现您的应用程序。您需要确保捕获并记录singleton中的异常。它们是容器管理的,不应该抛出异常。另外,在向数据库中添加行之前,请确保该行不存在。例如,当您关闭glassfish服务器时,它不会自动关闭db服务器,因此在服务器重新启动时,上一个表仍然在数据库中运行,并且您的代码将再次尝试从xml文件加载行,这将导致重复的键异常;因此,单例让这些异常冒泡到容器级别,应用程序将永远不会启动。
此外,在测试阶段,当您从xml文件手动将数据添加到db中时,最好让应用程序为实体生成pk,以避免重复的主键错误。例如,当将netbeanside与apachederby配合使用时,每次循环使用db服务器时,pk的生成都会在应用程序启动并运行后从一个设置的初始值开始。在您的例子中,假设您从一个pk=1的xml加载了一行,那么在应用程序的jsf页面的下一次添加时,将与现有的行发生pk冲突。

相关问题