汇总JavaFX属性和JPA实体(无混合模式)

时间:2015-08-11 12:28:27

标签: jpa javafx

我有一个由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实体代码?

EDITED

即。目前我有JPAEntity + JPAController和FXClass,&#34;代表&#34; JPAEntity。

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

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

所以......我正在使用中间层来传达两者。

提前致谢

2 个答案:

答案 0 :(得分:7)

我通常会主张在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属性,反之亦然。

答案 1 :(得分: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
}