在模型类中使用 javafx.beans 属性

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/23187989/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 21:15:04  来源:igfitidea点击:

Using javafx.beans properties in model classes

javahibernatejavafxmodelpersistence

提问by Johnny

Is it a correct practice to use JavaFX beans properties in the model classes?

在模型类中使用 JavaFX bean 属性是正确的做法吗?

I wonder if it is a good practice to use properties in model classes to be able to bind them easier with the view components. I'm not worried about the availability of those libraries in future because my program will be run on JRE8 or later but the nature of using the JavaFX libraries in the model classes make me skeptical and I'm worried about current and future incompabilities specially because I want to use Hibernate to persist those attributes.

我想知道在模型类中使用属​​性以便能够更轻松地将它们与视图组件绑定是否是一个好习惯。我不担心将来这些库的可用性,因为我的程序将在 JRE8 或更高版本上运行,但是在模型类中使用 JavaFX 库的性质让我持怀疑态度,我特别担心当前和未来的不兼容,因为我想使用 Hibernate 来保留这些属性。

Note: I use a pure JavaFX environment and I will never need Swing compability in my application.

注意:我使用纯 JavaFX 环境,我的应用程序中永远不需要 Swing 兼容性。

采纳答案by James_D

I'm going to offer something of a dissenting opinion here.

我将在这里提出一些不同意见。

JavaFX Properties and JPA

JavaFX 属性和 JPA

As I commented on jewelsea's answer, using a JavaFX property-based bean with JPA is possible as long as you use "property access" rather than "field access". The blog postI linked there goes into more detail on this, but the basic idea is that any annotations should be on the get...()methods and not on the fields. As far as I can see, this does prevent use of any of the read-only JavaFX property patterns in conjunction with JPA, but I've never really felt JPA played well with read only properties (i.e. get methods and no set method) anyway.

正如我对 Jewelsea 的回答所评论的那样,只要您使用“属性访问”而不是“字段访问”,就可以将基于 JavaFX 属性的 bean 与 JPA 结合使用。我在那里链接的博客文章对此进行了更详细的介绍,但基本思想是任何注释都应该在get...()方法上,而不是在字段上。据我所知,这确实阻止了将任何只读 JavaFX 属性模式与 JPA 结合使用,但我从来没有真正觉得 JPA 与只读属性(即 get 方法和无 set 方法)配合得很好.

Serialization

序列化

Contrary to my comment on jewelsea's answer, and with the benefit of a few weeks to work with this (and having been placed in a position where I was facing replicating several entity classes on a JavaFX client side using JavaFX properties), I think the lack of serialization of JavaFX properties can be worked around. The key observation is that you really only need to serialize the wrapped state of the property (not, for example, any listeners). You can do this by implementing java.io.Externalizable. Externalizableis a sub-interface of Serializablethat requires you to fill in the readExternal(...)and writeExternal(...)methods. These methods can be implemented to externalize just the state wrapped by the property, rather than the property itself. This means that if your entity is serialized and then deserialized, you will end up with a new property instance, and any listeners will not be preserved (i.e. the listeners effectively become transient), but as far as I can see this would be what was wanted in any reasonable use case.

与我对jewelsea的回答的评论相反,并且有几周的时间来处理这个问题(并且已经被安置在我面临使用JavaFX属性在JavaFX客户端复制几个实体类的位置),我认为缺乏可以解决 JavaFX 属性的序列化问题。关键的观察是你真的只需要序列化属性的包装状态(而不是,例如,任何侦听器)。您可以通过实施java.io.Externalizable. Externalizable是一个子界面Serializable,需要你填写readExternal(...)writeExternal(...)方法。可以实现这些方法来外部化由属性包装的状态,而不是属性本身。这意味着,如果您的实体被序列化然后反序列化,您最终将获得一个新的属性实例,并且任何侦听器都不会被保留(即侦听器实际上变成了transient),但据我所知,这将是我们想要的在任何合理的用例中。

I experimented with beans defined this way and it all seems to work nicely. Additionally, I ran a small experiment in transferring them between the client and a restful web service, using the Hymanson mapper to convert to and from a JSON representation. Since the mapper just relies on using the get and set methods, this works just fine.

我用这种方式定义的 bean 进行了试验,看起来一切都很好。此外,我还进行了一个小实验,在客户端和 Restful Web 服务之间传输它们,使用 Hymanson 映射器在 JSON 表示之间进行转换。由于映射器仅依赖于使用 get 和 set 方法,所以这工作得很好。

Some caveats

一些注意事项

A couple of points need to be observed. As with any Serialization, it's important to have a no-argument constructor. And of course, all values wrapped by the JavaFX properties must themselves be serializable - again this is the same rule as for any serializable bean.

需要注意几点。与任何序列化一样,拥有无参数构造函数很重要。当然,JavaFX 属性包装的所有值本身必须是可序列化的——这与任何可序列化 bean 的规则相同。

The point about JavaFX properties working via side-effects is well taken, and care needs to be exercised when moving these properties (which are, to some extent, designed with a single-threaded model in mind) to a potentially multi-threaded server. A good rule of thumb is probably that if you use this strategy, listeners should only be registered on the client side (and remember, those listeners are transient with respect to transfer back to the server, whether by serialization or by JSON representation). Of course, that suggests that using these on the server side then might be a bad design; it becomes a trade-off between the convenience of having a single entity that is "all things to all people" (observable properties for the JavaFX client, serializable for persistence and/or remote access, and with persistent mappings for JPA) versus exposing functionality (e.g. observability) where it might not be fully appropriate (on the server).

关于 JavaFX 属性通过副作用工作的观点得到了很好的理解,在将这些属性(在某种程度上,考虑到单线程模型设计)移动到潜在的多线程服务器时需要小心。一个好的经验法则可能是,如果你使用这个策略,监听器应该只在客户端注册(记住,这些监听器在传输回服务器方面是暂时的,无论是通过序列化还是通过 JSON 表示)。当然,这表明在服务器端使用这些可能是一个糟糕的设计;它变成了拥有一个“所有人的所有事物”的单一实体的便利性(JavaFX 客户端的可观察属性,可序列化以实现持久性和/或远程访问,

Finally, if you do use the JPA annotations, those have runtime retention which implies (I think) that your JavaFX client will need the a javax.persistence specification on the classpath).

最后,如果您确实使用了 JPA 注释,则这些注释具有运行时保留,这意味着(我认为)您的 JavaFX 客户端将需要类路径上的 javax.persistence 规范)。

Here's an example of such a "man for all seasons" entity:

以下是此类“四季皆宜”实体的示例:

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.time.MonthDay;

import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

/**
 * Entity implementation class for Entity: Person
 *
 */
@Entity

public class Person implements Externalizable {


    private static final long serialVersionUID = 1L;

    public Person() {

    }

    public Person(String name, MonthDay birthday) {
        setName(name);
        setBirthday(birthday);
    }

    private final IntegerProperty id = new SimpleIntegerProperty(this, "id");

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    public int getId() {
        return id.get();
    }

    public void setId(int id) {
        this.id.set(id);
    }

    public IntegerProperty idProperty() {
        return id ;
    }

    private final StringProperty name = new SimpleStringProperty(this, "name");

    // redundant, but here to indicate that annotations must be on the property accessors:
    @Column(name="name")
    public final String getName() {
        return name.get();
    }

    public final void setName(String name) {
        this.name.set(name);
    }

    public StringProperty nameProperty() {
        return name ;
    }

    private final ObjectProperty<MonthDay> birthday = new SimpleObjectProperty<>();

    public final MonthDay getBirthday() {
        return birthday.get();
    }

    public final void setBirthday(MonthDay birthday) {
        this.birthday.set(birthday);
    }

    public ObjectProperty<MonthDay> birthdayProperty() {
        return birthday ;
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeInt(getId());
        out.writeObject(getName());
        out.writeObject(getBirthday());
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException,
            ClassNotFoundException {
        setId(in.readInt());
        setName((String) in.readObject());
        setBirthday((MonthDay) in.readObject());
    }

}

回答by jewelsea

JavaFX Property Design

JavaFX 属性设计

JavaFX properties are designed such that you don't need to be running a JavaFX program to use them. The sections of the Oracle Using JavaFX Properties and Binding Tutorial, demonstrates such usage (e.g. a Bill class to model the properties of a bill). The sample from the tutorial just runs a standard Java program with a mainand not a JavaFX Application. So you can generically use properties and binding without having an additional requirement on the JavaFX runtime. This means that you could for instance, make use of JavaFX properties and bindings in a server side application.

JavaFX 属性的设计使您无需运行 JavaFX 程序即可使用它们。在各节的Oracle使用JavaFX属性和具有约束力的教程,演示了这种用法(如比尔类法案的属性模型)。本教程中的示例只运行一个标准的 Java 程序,main而不是JavaFX 应用程序。因此,您可以通用地使用属性和绑定,而无需对 JavaFX 运行时提出额外要求。这意味着您可以例如在服务器端应用程序中使用 JavaFX 属性和绑定。

"Correct" Practices

“正确”做法

OK, so you can do it, but is it "correct" practice?

好的,所以你可以做到,但这是“正确”的做法吗?

I don't think many people use JavaFX properties in this way. One reason for this is simply because JavaFX properties are quite new. I don't think it's "wrong" to use JavaFX properties in model objects.

我认为没有多少人以这种方式使用 JavaFX 属性。原因之一仅仅是因为 JavaFX 属性非常新。我不认为在模型对象中使用 JavaFX 属性是“错误的”。

Caveats

注意事项

JavaFX properties do not support Java serialization (by this I mean direct support for the Serializableinterface). Numerous server side Java technologies might require model serialization and they would be unable to serialize any object which made use of JavaFX properties.

JavaFX 属性不支持 Java 序列化(我的意思是直接支持Serializable接口)。许多服务器端 Java 技术可能需要模型序列化,并且它们无法序列化任何使用 JavaFX 属性的对象。

JavaFX properties themselves are not container aware and work via side-effects (e.g changing a property may trigger an update to another bound value), so be aware of this and make sure that this kind of processing is an acceptable approach in your environment. In particular, be careful you do not generate unwanted race conditions in a multi-threaded server environment (JavaFX client applications generally require less care with respect to this as JavaFX in general runs mostly as a single threaded environment).

JavaFX 属性本身不是容器感知的并且通过副作用工作(例如,更改属性可能会触发对另一个绑定值的更新),因此请注意这一点并确保此类处理在您的环境中是可接受的方法。特别是,请注意不要在多线程服务器环境中生成不需要的竞争条件(JavaFX 客户端应用程序通常需要较少注意这一点,因为 JavaFX 通常主要作为单线程环境运行)。

JavaFX Properties and Hibernate/JPA

JavaFX 属性和 Hibernate/JPA

I don't think mixing JavaFX properties into Hibernate (or JPA) entity classes is a good idea. I have not seen anybody do that. Hibernate itself is not aware of JavaFX properties and in general is designed to work against Java primitives like Strings and ints, so I don't know how it could possibly automatically map a JavaFX property to a database field.

我不认为将 JavaFX 属性混合到 Hibernate(或 JPA)实体类中是一个好主意。我还没有看到有人这样做。Hibernate 本身不知道 JavaFX 属性,并且通常设计为针对字符串和整数等 Java 原语工作,所以我不知道它如何自动将 JavaFX 属性映射到数据库字段。

You would likely need a setup which defined your entity class hierarchy and a parallel hierarchy for your JavaFX property based model classes and finally a mapper layer which mapped between the two. This kind of architectural setup is essentially an MVVM model. The model (M) is your Hibernate entity classes, and the view model (VM) is your JavaFX property based model.

您可能需要一个设置来定义实体类层次结构和基于 JavaFX 属性的模型类的并行层次结构,最后是在两者之间映射的映射器层。这种架构设置本质上是一个MVVM 模型。模型 (M) 是您的 Hibernate 实体类,视图模型 (VM) 是您的基于 JavaFX 属性的模型。

回答by user2768310

for persisting jpa collections just use this signature for getter method..

对于持久化 jpa 集合,只需将此签名用于 getter 方法..

@OneToMany
List<Person> getAll()
{
return observableList;
}