Ebean:如何为没有 属性 的 getter 创建列

Ebean: How to create a column for a getter without a property

我不想将 Java 的计算值存储在我的数据库中。

例如,我可能有一个人 class,有一个 firstName 和一个 lastName。我可能想要 getter returns Person 名字的总长度,而不是实际的 属性.

@Entity
public class Person extends Model {
    @Id
    public Long id;

    public String firstName;

    public String lastName;

    public Int getNameLength() {
        return firstName.length() + lastName.length();
    }

    public Person (String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
}

所以如果我像这样创建一个新的 Person

Person bob = new Person("Bob", "Roy");
bob.save();

那么我们应该在 table:

中结束这个
|  id  |  first_name  |  last_name  |  name_length |
====================================================
|  1   |    "Bob"     |    "Roy"    |      6       |

有人知道这是否可行吗?

为了Old Gods and the New Gods请不要这样做

做这样的事情会把你的数据库搞得一团糟。您 运行 迟早会遇到问题。查看您的个人资料,您获得了 CS 学位,因此您肯定有数据库课程。请记住 Second normal form and the Third normal form 以及如果您的属性依赖于其他属性,您将如何打破它。

您应该做的是拥有一个临时字段(标记为 @Transient)或者您可以使用 getter 并从那里提供信息。每次您需要访问 name_length 时,您将调用此 getter 但您不会将信息存储在数据库中。

即使您想在应用程序之外计算长度,您仍然可以为此使用一些数据库函数 - like length


根据OP提到的要求进行编辑:

在 JPA 中,有两种方法可以声明列 - 在字段上或在方法上 (getters/setters)。它会像这样:

@Column(name = "complex_calculation") // Due to some bad requirement
public Integer getNameLength() {
    return fisrtName.length() + lastName.length();
}

但是您在问题中提到了 Ebean,并且 Ebean 不被视为参考 JPA 实现。很有可能这还不受支持,但您可以在您的特定情况下尝试。

还有另一种方法被证明有效。你这样定义你的模型:

@Entity
public class Person extends Model {
    @Id
    private Long id;

    private String firstName;

    private String lastName;

    private Integer nameLength;

    public Long getId() {
        return id;
    }

    // getter for first name and last name with @Column annotation

    @Column(name = "complex_calculation")
    public Integer getNameLength() {
        return firstName.length() + lastName.length();
    }

    public Person (String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
        updateComplexCalculation();
    }

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

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

    private void updateComplexCalculation() {
        this.nameLength = firstName.length() + lastName.length();
    }
}

重要的部分是updateComplexCalculation方法。当调用构造函数时,在每次 setter 调用时调用此方法来更新 complex 属性。当然,您应该仅在计算所需的 setter 调用时调用它。

以下代码:

Person p = new Person("foo", "bar");
p.save();

Logger.debug("Complex calculation: " + p.getNameLength());

p.setFirstName("somethingElse");
p.save();

Logger.debug("Complex calculation: " + p.getNameLength());

然后产量:

[debug] application - Complex calculation: 6
[debug] application - Complex calculation: 16

模型中 属性 有什么问题?只需 private 添加 public getter 但不添加 setter,最后覆盖 saveupdate 方法。

private Integer nameLength;

// BTW shouldn't you also count the space between first and last name?
public Integer getNameLength() {
    return firstName.length() + lastName.length();
}

@Override
public void save() {
    nameLength = firstName.length() + lastName.length();
    super.save();
}

@Override
public void update() {
    nameLength = firstName.length() + lastName.length();
    super.update();
}

如果您仍想在模型中避免使用 属性,您将需要使用自定义 SQL 查询(可能也在覆盖的 save/update 方法中),如显示 in other answer or Ebean's docs,请注意,每次保存或更新操作将至少执行 2 SQL 次查询。

感谢 Anton 和 biesior 给我一些想法。

我们没有覆盖 saveupdate 方法,而是选择了一个私有变量,当它所依赖的变量更新时,该变量会重新计算。

public class Person {
    @Id
    public Long id;
    private String firstName;
    private String lastName;
    private Integer nameLength;

    public String getFirstName() { 
        return firstName; 
    }

    public String getLastName() { 
        return lastName; 
    }

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

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

    private void calculateNameLength() {
        nameLength = getFirstName().length + getLastName().length;
    }

}

saveupdate 方法中建议的更新值的方法相比,这有几个好处。

saveupdate 方法中重新计算意味着我们每次在对象上设置字段时都需要调用其中一个方法。如果我们不这样做,nameLength 将不同步。例如,我不能更改 Persons firstName,然后在不先将对象持久保存到数据库的情况下使用 nameLength 做其他事情。

此外,使用 save/update 方法将对象状态耦合到 Ebean。 ORM 用于持久化对象状态,而不是用于设置对象状态。