1

I have a simple JavaFX TableView which is populated by a JPA database query. I have one column set as editable and that works. However, after a change is made in a cell, it disappears as soon as I sort or even navigate through the TableView. Also, how would you persist these edits back to the database?

Here is the FXML file and its controller:

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

<?import java.lang.*?>
<?import java.net.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.control.cell.*?>
<?import javafx.scene.layout.*?>

<AnchorPane id="AnchorPane" prefHeight="600.0" prefWidth="800.0" styleClass="mainFxmlClass" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.IXS.synergyixs.ingestor.ViewUsersController">
    <stylesheets>
        <URL value="@/styles/IssueTrackingLite.css" />
    </stylesheets>
    <children>
        <TableView fx:id="loginsTable" editable="true" prefHeight="593.0" prefWidth="794.0">
            <columns>
                <TableColumn fx:id="idCol" editable="false" text="Id">
                </TableColumn>
                <TableColumn fx:id="loginCol" text="Login">
                </TableColumn>
                <TableColumn fx:id="partyCol" editable="false" text="Party ID">
                </TableColumn>
                <TableColumn fx:id="creatorCol" editable="false" text="Creator ID">
                </TableColumn>
                <TableColumn fx:id="modifierCol" editable="false" text="Modifier ID">
                </TableColumn>
            </columns>
        </TableView>
    </children>
</AnchorPane>

the controller:

package com.IXS.synergyixs.ingestor;

import com.IXS.synergyixs.ingestor.data.Usr;
import java.net.URL;
import java.util.List;
import java.util.ResourceBundle;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.Pane;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

/**
 * FXML Controller class
 *
 * @author Owner
 */
public class ViewUsersController implements Initializable {

    public EntityManagerFactory emf = Persistence.createEntityManagerFactory("com.mycompany_SynergyIXS-Ingestor_jar_1.0-SNAPSHOTPU");
    public EntityManager em = emf.createEntityManager();

    public ObservableList<Usr> tableData;
    public Usr currentUsr;
    /**
     * Initializes the controller class.
     */
    @FXML
    private Pane rootPane;
    @FXML
    private TableView<Usr> loginsTable;

    @FXML
    private TableColumn<Usr, Number> idCol;

    @FXML
    private TableColumn<Usr, String> loginCol;

    @FXML
    private TableColumn<Usr, Number> partyCol;

    @FXML
    private TableColumn<Usr, Number> creatorCol;

    @FXML
    private TableColumn<Usr, Integer> modifierCol;

    @Override
    public void initialize(URL location, ResourceBundle resources) {

        /* to verify query is returning data
        for (Usr u : userList) {
            System.out.println(u.getId() + " " + u.getLogin());
        }
         */
        idCol.setCellValueFactory(new PropertyValueFactory<>("id"));

        loginCol.setCellValueFactory(new PropertyValueFactory<>("login"));
        loginCol.setCellFactory(TextFieldTableCell.forTableColumn‌());

        partyCol.setCellValueFactory(new PropertyValueFactory<>("partyId"));

        creatorCol.setCellValueFactory(new PropertyValueFactory<>("creatorId"));

        modifierCol.setCellValueFactory(new PropertyValueFactory<>("modifierId"));
        updateUserList();
        loginsTable.setItems(tableData);
    }

    public void updateUserList() {
        List<Usr> userList = em.createNamedQuery("Usr.findAll").getResultList();
        if (tableData == null) {
            tableData = FXCollections.observableArrayList(userList);
        } else {
            tableData.clear();
            tableData.addAll(userList);
        }
    }
    /*
    public void saveUser()
    {
        em.getTransaction().begin();
        em.persist(currentUsr);
        em.getTransaction().commit();
        updateUserList();
    }
     */
}​

Usr class:

package com.IXS.synergyixs.ingestor.data;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Date;
import java.util.List;
import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;

/**
 *
 * @author Owner
 */
@Entity
@Table(name = "USR", catalog = "", schema = "ADMIN")
@XmlRootElement
@NamedQueries({
    @NamedQuery(name = "Usr.findAll", query = "SELECT u FROM Usr u")
    , @NamedQuery(name = "Usr.findById", query = "SELECT u FROM Usr u WHERE u.id = :id")
    , @NamedQuery(name = "Usr.findAllLogin", query = "SELECT u.login FROM Usr u")
    , @NamedQuery(name = "Usr.findByLogin", query = "SELECT u FROM Usr u WHERE u.login = :login")
    , @NamedQuery(name = "Usr.findByPwd", query = "SELECT u FROM Usr u WHERE u.pwd = :pwd")
    , @NamedQuery(name = "Usr.findByPartyId", query = "SELECT u FROM Usr u WHERE u.partyId = :partyId")
    , @NamedQuery(name = "Usr.findByCreatorId", query = "SELECT u FROM Usr u WHERE u.creatorId = :creatorId")
    , @NamedQuery(name = "Usr.findByCreationDttm", query = "SELECT u FROM Usr u WHERE u.creationDttm = :creationDttm")
    , @NamedQuery(name = "Usr.findByModifierId", query = "SELECT u FROM Usr u WHERE u.modifierId = :modifierId")
    , @NamedQuery(name = "Usr.findByModificationDttm", query = "SELECT u FROM Usr u WHERE u.modificationDttm = :modificationDttm")})
public class Usr implements Serializable {

    private static final long serialVersionUID = 1L;
    // @Max(value=?)  @Min(value=?)//if you know range of your decimal fields consider using these annotations to enforce field validation
    @Id
    @Basic(optional = false)
    @Column(name = "ID")
    private BigDecimal id;
    @Basic(optional = false)
    @Column(name = "LOGIN")
    private String login;
    @Basic(optional = false)
    @Column(name = "PWD")
    private String pwd;
    @Column(name = "PARTY_ID")
    private BigInteger partyId;
    @Basic(optional = false)
    @Column(name = "CREATOR_ID")
    private BigInteger creatorId;
    @Basic(optional = false)
    @Column(name = "CREATION_DTTM")
    @Temporal(TemporalType.TIMESTAMP)
    private Date creationDttm;
    @Basic(optional = false)
    @Column(name = "MODIFIER_ID")
    private BigInteger modifierId;
    @Basic(optional = false)
    @Column(name = "MODIFICATION_DTTM")
    @Temporal(TemporalType.TIMESTAMP)
    private Date modificationDttm;
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "usr")
    private List<UsrAppRole> usrAppRoleList;

    public Usr() {
    }

    public Usr(BigDecimal id) {
        this.id = id;
    }

    public Usr(BigDecimal id, String login, String pwd, BigInteger creatorId, Date creationDttm, BigInteger modifierId, Date modificationDttm) {
        this.id = id;
        this.login = login;
        this.pwd = pwd;
        this.creatorId = creatorId;
        this.creationDttm = creationDttm;
        this.modifierId = modifierId;
        this.modificationDttm = modificationDttm;
    }

    public BigDecimal getId() {
        return id;
    }

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

    public String getLogin() {
        return login;
    }

    public void setLogin(String login) {
        this.login = login;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    public BigInteger getPartyId() {
        return partyId;
    }

    public void setPartyId(BigInteger partyId) {
        this.partyId = partyId;
    }

    public BigInteger getCreatorId() {
        return creatorId;
    }

    public void setCreatorId(BigInteger creatorId) {
        this.creatorId = creatorId;
    }

    public Date getCreationDttm() {
        return creationDttm;
    }

    public void setCreationDttm(Date creationDttm) {
        this.creationDttm = creationDttm;
    }

    public BigInteger getModifierId() {
        return modifierId;
    }

    public void setModifierId(BigInteger modifierId) {
        this.modifierId = modifierId;
    }

    public Date getModificationDttm() {
        return modificationDttm;
    }

    public void setModificationDttm(Date modificationDttm) {
        this.modificationDttm = modificationDttm;
    }

    @XmlTransient
    public List<UsrAppRole> getUsrAppRoleList() {
        return usrAppRoleList;
    }

    public void setUsrAppRoleList(List<UsrAppRole> usrAppRoleList) {
        this.usrAppRoleList = usrAppRoleList;
    }

    @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 Usr)) {
            return false;
        }
        Usr other = (Usr) 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 "com.mycompany.synergyixs.ingestor.Usr[ id=" + id + " ]";
    }

}
  • Post the `Usr` class. – James_D Feb 12 '17 at 16:12
  • so you debugged as far as WHERE the "changes are lost" ? in JPA API calls? or before they get there? – Neil Stockton Feb 12 '17 at 17:06
  • Sort of an aside: the `equals(..)` and `hashCode()` methods in `Usr` might create problems. You should really compare the object state in `equals(...)`, not the `id`. Bad things might happen trying to update data if the frameworks (JPA or JavaFX) think the objects are equal but you want to consider them being different. – James_D Feb 12 '17 at 17:40

1 Answers1

2

The default onEditCommit handler assumes the table's model uses the JavaFX properties pattern (see TableView documentation, section titled "Editing"). Since your Usr class is essentially a plain JavaBean, with no property accessor methods, the default handler reduces to a no-op.

You either need to refactor your Usr class so that it uses JavaFX properties (see Using javafx.beans properties in model classes for a discussion on using JavaFX properties in JPA entities), or set an onEditCommit handler on the column:

loginCol.setOnEditCommit(e -> {
    Usr usr = e.getRowValue();
    usr.setLogin(e.getNewValue());
});

To store the value in the database, use merge(...):

loginCol.setOnEditCommit(e -> {
    Usr usr = e.getRowValue();
    usr.setLogin(e.getNewValue());
    // merge change to database:
    Usr mergedUsr = em.merge(usr);
    // ensure table has same reference as ORM cache:
    loginsTable.set(e.getTablePosition().getRow(), mergedUsr);
});
Community
  • 1
  • 1
James_D
  • 201,275
  • 16
  • 291
  • 322
  • I had to wrap the merge in a transaction begin and commit plus commented out the loginsTable.set(... as it was causing an error. Data is now persisting in the database and the tableview. Thanks for the reference to documentation, my searches were coming up empty for that information. – Eugene Fallon Feb 13 '17 at 04:29