简体   繁体   中英

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

I wan't to be able to store computed values from Java in my database.

For example, I might have a Person class that with a firstName and a lastName . I may want a getter that returns the the total length of the Person s name, without it being an actual property.

@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;
    }
}

So if I create a new Person like so:

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

Then we should end up with this in the table:

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

Does anyone know if this is possible?

Please for the sake of Old Gods and the New Gods don't do this

Doing something like this would totally mess up your database. You will run into problems sooner or later. Looking at your profile you obtained a CS degree so you definitely had your databases course. Remember the Second normal form and the Third normal form and how you will break this if you have attributes which depend on other attributes.

What you should do is to have either a transient field (marked with @Transient ) or you can use the getter and provide the information from there. Every time you need to access the name_length you will call this getter but you won't store the information in the database.

Even if you want to calculate the length outside of your application, you can still use some database function for this - like length .


Edit based on the requirement mentioned by the OP:

In JPA there are two ways how you can declare the columns - either on fields or on methods (getters/setters). It would go like this:

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

However you mentioned Ebean in your question and Ebean is not regarded as the reference JPA implementation. There is a good chance this is not yet supported but you can try it in your specific case.

There is another way which is proven to work. You define your model like this:

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

The important part is the updateComplexCalculation method. When the constructor is called and on every setter-call you invoke this method to update the complex property. Of course you should invoke it only on setter-calls which are needed for the computation.

The following code:

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

yields then:

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

What's wrong about property in the model? Just make it private add the public getter but without setter, finally override save and update methods.

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

If you still want to avoid property in the model, you will need to use custom SQL query (probably also within the overridden save/update methods), like showed in other answer or Ebean's docs , note that will perform at least 2 SQL queries per each save or update operation.

Thanks to Anton and biesior for giving me some ideas.

Rather than override the save or update methods, we opted for a private variable that is recalculated when the variables it's dependent on are updated.

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;
    }

}

This has several benefits over the suggested methods of updating the value in the save or update methods.

Recalculating in the save or update method means we need to call one of those methods every time we set a field on the object. If we don't, the nameLength will get out of sync. I couldn't, for example, change the Persons firstName , and then use the nameLength to do something else without first persisting the object to the database.

Furthermore, using the save / update methods couples the objects state to the Ebean. The ORM is for persisting object state, not for setting object state.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM