-->

Play Framework 2.3 How to add unique constraint to

2019-08-11 09:49发布

问题:

Given the Play Framework 2.3 Computer Database sample application, I would like to practice adding a unique constraint on an attribute. Let's say I want the name attribute of the Computer class to be unique. I've tried to do this by adding a validate() function (and a getter) to Computer.java:

public List<ValidationError> validate() {

    List<ValidationError> errors = new ArrayList<ValidationError>();

    if(Computer.find.where().eq("name", getName()).findRowCount() != 0){
        errors.add(new ValidationError("name", "Name must be unique. That value is already taken."));
    }

    return errors;
}

public String getName() {
    return name;
}

This check works when creating new records in the database, however, this now causes a validation error when you update a Computer object but don't change the name. Is there a way to add a uniqueness constraint, similar to Rails? How can I validate uniqueness in Play?

Thanks!

UPDATE: see the answer by davide.

I ended up using the @Column(unique = true) constraint from the javax.persistence API. This doesn't generate an error in Play forms; instead, it throws a PersistenceException. Therefore I had to add change my controller to achieve the behavior I wanted. Both the create() and update() actions need a try/catch like this:

 try {
     computerForm.get().save();
 } catch (PersistenceException pe) {
     flash("error", "Please correct errors below.");
     formData.reject("name", "Name conflict. Please choose a different name.");
     return badRequest(createForm.render(computerForm));
 }

UPDATE 2: each of the answers below is a possible solution

回答1:

You need to exclude current entity from unique checking, i.e. like that:

if(Computer.find.where().eq("name", getName()).ne("id", getId()).findRowCount() != 0){
     errors.add(new ValidationError("name", "Name must be unique."));
}

It will give you SQL query during update:

select count(*) from computer t0 where t0.name = 'Foo' and t0.id <> 123 

And this during create:

select count(*) from computer t0 where t0.name = 'Foo' and t0.id is not null 

P.S. ne() expression stands for Not Equal To and of course this approach assumes that your name field is Required

Edit: I sent you pull request with working solution, all you need is to add hidden field in your editForm like:

<input name="id" type="hidden" value='@computerForm("id").value'/>

Other thing is that you can simplify your model, i.e. don't need for getters for public fields.



回答2:

I not sure if this answer your question, because I'm not familiar with Ruby syntax.

To "create a uniqueness constraint in the database" you can use the javax persistence API. Ebean will also recognize this.

To have a plain uniqueness constraint which involves a single field, you can use the @Column annotation:

  @Entity
  public class Computer extends Model {
    ...

    @Column(unique = true)
    public String name;

    ...
  }

If you need some combination of fields to be unique, instead use the @Table annotation

  @Table(
        uniqueConstraints=
            @UniqueConstraint(columnNames={"name", "brand"})
    )
  @Entity
  public class Computer extends Model {
    ...

    public String name;
    public String brand;

    ...
  }

I hope it helps!