【问题标题】:Put together JavaFX properties and JPA Entities (NO MIXED MODE)将 JavaFX 属性和 JPA 实体放在一起(无混合模式)
【发布时间】:2015-11-03 16:20:13
【问题描述】:

基础

我有一个由 JPA (EclipseLink)(实体和控制器 + 持久性单元)管理的 mysql 数据库。 GUI 是基于 JavaFX 的。

信息

我看过这篇文章:

还有这个示例代码

问题

目前我正在使用 我的适配器(不是真正的适配器模式)将 JPAEntity 转换为 JavaFX Bean

public <T, S> Function<T, S> getRefactor() {
    return o -> {
        Object rtn = null;

        //adapt **o** it to JavaFX bean

        return (S) rtn;
    };
}

我认为这不是最好的解决方案。

问题 没有混合模式! 我相信在服务器端使用 javafx 属性是疯狂的,即使是超级懒惰的实现也是如此。

有一个灵活的解决方案来获得 JavaFX Bean 的所有优点,例如双向绑定,并保持 JPA 实体代码不变?

已编辑

即目前我有 JPAEntity + JPAController 和 FXClass,它们“代表”了 JPAEntity。

JPAEntity 是一种旧式 POJO,包含要写入 DB 的数据。

FXClass 具有 javafx 属性,包含要在 FX 环境中显示的数据。

所以...我正在使用一个中间层来将两者进行通信。

提前致谢

【问题讨论】:

  • 在 JPA 实体中使用 JavaFX 属性(仅仅是标准 JavaBean 属性的扩展)的反对意见是什么? (使用此方法的另一个例子是Adam Bien's airhacks。)任何其他解决方案都必然会在您的模型中引入另一层。如果你真的想避免它,最好的方法可能是使用JavaBeanPropertyAdapters。见stackoverflow.com/questions/23522130/…
  • 我正在使用UIFX.getTWColumn("ColumnName", 400.0, (TableColumn.CellDataFeatures&lt;ClassFX, ClassFX&gt; p) -&gt; new ReadOnlyObjectWrapper(p.getValue()), (TableColumn&lt;ClassFX, ClassFX&gt; p) -&gt; new ClassFXContent().&lt;ClassFX, ClassFX&gt;getClassTableCellFactory() )
  • 我不明白...UIFX 是什么?这与JavaBeanPropertyAdapters 有什么关系?
  • 'UIFX.getTWColumn(...)' 是创建 TableCell 内容的静态方法。抱歉,我没有使用 JavaBeanPropertyAdapter
  • +1 JavaBeanPropertyAdapter 是一种可接受的解决方案。还有其他人吗?

标签: jpa javafx


【解决方案1】:

我通常会提倡在 JPA 实体中使用 JavaFX 属性:我真的认为没有明显的理由不这样做。

但是,如果您想避免这样做,可以使用JavaBeanPropertyAdapters。这些是创建包装常规 JavaBean 属性的 JavaFX 可观察属性的适配器。所以如果你有一个 bean 类

@Entity
public class Person {

    private String firstName ;
    private String lastName ;

    @Id
    private Integer id ;

    public String getFirstName() {
        return firstName ;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName ;
    }

    public String getLastName() {
        return lastName ;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName ;
    }
}

然后你可以做类似的事情

TableColumn<Person, String> firstNameCol = new TableColumn<>("First Name");
firstNameCol.setCellValueFactory(cellData -> {
    try {
        return JavaBeanStringPropertyBuilder.create()
            .bean(cellData.getValue())
            .name("firstName")
            .build();
    } catch (NoSuchMethodException exc) {
        throw new RuntimeException(exc);
    }
});

这将创建一个 JavaFX 属性以在表中使用,并将 JavaBean 属性单向绑定到它:即,如果您更改表中的值,JavaBean 将被更新。此设置不会发生反向绑定,即更改 bean 中的值不会更新表中显示的值。

如果您想要双向绑定,您的 bean 将需要支持属性更改侦听器:

public class Person {
    private String firstName ;
    private String lastName ;

    private PropertyChangeSupport pcs ;

    public Person() {
        pcs = = new PropertyChangeSupport(this);
    }

    public void addPropertyChangeListener(PropertyChangeListener listener) {
        pcs.addPropertyChangeListener(listener);
    }

    public void removePropertyChangeListener(PropertyChangeListener listener) {
        pcs.removePropertyChangeListener(listener);
    }

    public String getFirstName() {
        return firstName;
    }
    public void setFirstName(String firstName) {
        String oldName = this.firstName ;
        this.firstName = firstName;
        pcs.firePropertyChange("firstName", oldName, this.firstName);
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        String oldName = this.lastName ;
        this.lastName = lastName;
        pcs.firePropertyChange("lastName", oldName, this.lastName);
    }

}

现在对 bean 的更改将传播到表使用的 JavaFX 属性,反之亦然。

【讨论】:

  • 当我使用 EntityManager.persist 之类的东西将 JavaFX 属性写入数据库时​​,它只会写入包装的值吗?绑定会发生什么?
  • @Mark 它只会写入包装的值。它的工作方式是,当实体被写入时,ORM 在 bean 上调用getXXX();当从数据库中读取它们时,ORM 通过调用默认构造函数创建一个新 bean,然后调用 setXXX(...) 来填充它。绑定不存储在数据库中。 (您希望将它们存储在哪个表和哪些列中???)目前尚不清楚为什么要保留和恢复绑定。如果您有真正的用例,您可以发布一个新问题。
  • @James_D 你的帖子marshall.edu/genomicjava/2014/05/09/one-bean-to-bind-them-all 不再可用.. 搬到任何地方?我记得它包含一些不错的内容,我想为当前项目重新阅读:)
  • @kleopatra 不幸的是,IT 停止支持个人博客;我需要为此找一个新家——只是现在没有时间。 Steven van Impe 有类似的材料。
【解决方案2】:

另一种可能的嵌入式、内置 AFAIK 解决方案。

来自Adapter pattern

...允许从另一个类使用现有类的接口 界面。它通常用于使现有的类与其他类一起工作 无需修改其源代码。

此示例仅供参考,并非已确认的解决方案,但需要编写结构良好且灵活应对变化的代码。 所以...

示例:

如果我们有类似的 JPAEntity

@Entity
@Table(name="EntityClass", uniqueConstraints = {
    @UniqueConstraint(columnNames = {"ID"})})
@XmlRootElement
@NamedQueries({
    @NamedQuery(name = "EntityClass.findAll", query = "SELECT a FROM EntityClass a"),
    @NamedQuery(name = "EntityClass.findById", query = "SELECT a FROM EntityClass a WHERE a.id = :id"),
    @NamedQuery(name = "EntityClass.findByYear", query = "SELECT a FROM EntityClass a WHERE a.year = :year")})
public class EntityClass implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(nullable = false)
    private Integer id;
    @Basic(optional = false)
    @Column(nullable = false, length = 4)
    private String year;
    //...and others

    private static final long serialVersionUID = 1L;

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "fKYear")
    private Collection<Some1> some1Collection;
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "fKYear")
    private Collection<Some2> some2Collection;

    public EntityClass() {
    }

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

    public EntityClass(Integer id, String year) {
        this.id = id;
        this.year = year;
    }

    public Integer getId() {
        return id;
    }

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

    public String getYear() {
        return year;
    }

    public void setYear(String year) {
        this.year = year;
    }

    @XmlTransient
    public Collection<Some1> getSome1Collection() {
        return some1Collection;
    }

    public void setSome1Collection(Collection<Some1> some1Collection) {
        this.some1Collection = some1Collection;
    }

    @XmlTransient
    public Collection<Some2> getSome2Collection() {
        return some2Collection;
    }

    public void setSome2Collection(Collection<Some2> some2Collection) {
        this.some2Collection = some2Collection;
    }

    @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 EntityClass)) {
            return false;
        }
        EntityClass other = (EntityClass) 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 this.year;
    }
}

我们创建了一个这样的接口

public interface IFXEntityClass{
    void setYear(String year);
    String getYear();
    //...
    void setSome1Collection(Collection<Some1> some1);
    Collection<Some1> getSome1Collection();
    //...
}

我们可以像这样创建我们的 FXClass

public class FXEntityClass implements IFXEntityClass{
    private final StringProperty yearProperty=new SimpleStringProperty();

    public StringProperty getYearProperty(){ return this.yearProperty;}
    public void setYear(String year){ this.year.set(year); }
    public String getYear(){ return this.year.get(); }
    //...
    void setSome1Collection(Collection<Some1> some1)( //do something)
    Collection<Some1> getSome1Collection(){ return null;}
    //...
}

现在我们拥有了所需的一切。让我们创建适配器。

public class EntityClassToFXEntityClassAdaptor implements IFXEntityClass{

    private EntityClass entity;
    private final StringProperty yearProperty=new SimpleStringProperty();

    public EntityClassToFXEntityClassAdaptor(EntityClass e){
        this.entity=e;
        //adapt it as you want
        this.yearProperty.set(e.getYear());
        //...
    }

    public StringProperty getYearProperty(){ return this.yearProperty;}
    public void setYear(String year){ this.year.set(year); }
    public String getYear(){ return this.year.get(); }
    //...
    void setSome1Collection(Collection<Some1> some1)( //do something)
    Collection<Some1> getSome1Collection(){ return null;}
    //...
}

我们终于可以使用它了

EntityClass ec=something; //get an instance of JPAEntity

EntityClassToFXEntityClassAdaptor adaptor=new EntityClassToFXEntityClassAdaptor(ec);
adaptor.getYearProperty();

还有一个更清晰的示例说明如何应用您在

中找到的这种模式
import java.awt.*;
public class CheckboxAdapter extends Checkbox{
    public CheckboxAdapter(String n){
        super(n);
    }

    public boolean isSelected(){
        return getState();
    }

    //... continue
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-07-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-13
    • 2015-05-05
    • 2021-11-06
    相关资源
    最近更新 更多