【问题标题】:Ebean: How to create a column for a getter without a propertyEbean:如何为没有属性的getter创建列
【发布时间】:2016-03-24 19:42:28
【问题描述】:

我不能在我的数据库中存储来自 Java 的计算值。

例如,我可能有一个带有 firstNamelastName 的 Person 类。我可能想要一个返回 Persons 名称总长度的 getter,而不是实际属性。

@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();

那么我们应该在表格中结束:

|  id  |  first_name  |  last_name  |  name_length |
====================================================
|  1   |    "Bob"     |    "Roy"    |      6       |

有人知道这是否可行吗?

【问题讨论】:

    标签: java playframework ebean playframework-2.4


    【解决方案1】:

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

    这样做会完全弄乱您的数据库。你迟早会遇到问题。查看您的个人资料,您获得了 CS 学位,因此您肯定参加过数据库课程。请记住 Second normal formThird normal form 以及如果您的属性依赖于其他属性,您将如何打破它。

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

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


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

    在 JPA 中,您可以通过两种方式声明列 - 在字段上或在方法(getter/setter)上。它会是这样的:

    @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
    

    【讨论】:

    • 我记得很清楚我的正常形式,我同意你的看法。但有时会出现要求。客户想要数据库中的字段,但他们不希望它只是数据库的计算值。这是出于性能原因。这不是实际的用例,因此长度不起作用。计算这些字段的规则要复杂得多,并且存在并在我们的 Java 代码中使用。在使用 SQL 生成报告时,我们需要这些字段作为字段,最好不要在一堆 SQL 视图中重复业务逻辑。
    • 我明白你的意思。虽然我仍然不喜欢这个想法,但我已经用一种方法更新了我的答案。我的假设是您必须在使用 Ebean 时定义一个属性。它应该在没有 Hibernate 属性的情况下工作。
    • 接受您的评论。他获取名称长度的事实是手动计算值让我失望。
    【解决方案2】:

    模型中的属性有什么问题?只需使其 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 answerEbean's docs,请注意至少会执行每个保存或更新操作 2 个 SQL 查询。

    【讨论】:

      【解决方案3】:

      感谢 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 用于持久化对象状态,而不是用于设置对象状态。

      【讨论】:

      • 鉴于您决定采用的解决方案实际上是我提出的解决方案,您可以接受我的回答 :)
      • 没问题,很高兴您能够解决您的问题!玩得开心:)
      猜你喜欢
      • 2016-02-17
      • 1970-01-01
      • 2019-04-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多