Uniqueness Validator

81 views
Skip to first unread message

Uzzal Devkota

unread,
Nov 19, 2013, 2:24:39 AM11/19/13
to activejd...@googlegroups.com
Hey guys,

I added a uniqueness validator as following:

public class UniquenessValidator extends ValidatorAdapter {

    private final String attribute;

    public UniquenessValidator(String attribute) {
        this.attribute = attribute;
        setMessage("should be unique");
    }

    @Override
    public void validate(Model m) {
        if(!Model.where(attribute + " = ?", m.get(attribute)).isEmpty()) {
            m.addValidator(this, attribute);
        }
    }

}

// field: username
public class User extends Model {
    static {
        validateWith(new UniquenessValidator("username"));
    }
}


It works!. But, I was wondering if it is the optimal way to validate uniqueness. Especially the following line:

if(!Model.where(attribute + " = ?", m.get(attribute)).isEmpty())

Thanks.

Uzzal Devkota

unread,
Nov 19, 2013, 5:07:27 AM11/19/13
to activejd...@googlegroups.com
Sorry, didn't check for update method. Doesn't validate while updating.

The validate method should be like following (Now works in all cases):

    @Override
    public void validate(Model m) {
        if (!Model.where(attribute + " = ? AND id IS NOT ?", m.get(attribute), m.getId()).isEmpty()) {
            m.addValidator(this, attribute);
        }
    }

m.getId() returns null for new record.

Still, the question remains. Is the query optimal?

Model.where(attribute + " = ? AND id IS NOT ?", m.get(attribute), m.getId()).isEmpty()

Thanks.

Igor Polevoy

unread,
Nov 20, 2013, 12:18:12 AM11/20/13
to activejd...@googlegroups.com
No, it is not optimal, because: 

1. It will potentially load many model instances while you only need one attribute. 
2. It is not thread safe - while you are checking, another DB connection might update the values. 

Base.count(): 

You can do this: 

If(Base.count("your_table", "query to filter", ... optional params) > 0){
// not empty
}else{
//empty
}

tx

Uzzal Devkota

unread,
Nov 20, 2013, 9:06:31 AM11/20/13
to activejd...@googlegroups.com
And how would I get the table name from an instance of "Model" ? Could not find in the docs. :(

Uzzal Devkota

unread,
Nov 20, 2013, 9:17:57 AM11/20/13
to activejd...@googlegroups.com
Oh there is this method that returns the table name of the model:

Model.getTableName();

So my method would look like:

    @Override
    public void validate(Model m) {
        if(Base.count(Model.getTableName(), attribute + " = ? AND id IS NOT ?", m.get(attribute), m.getId()) > 0) {
            m.addValidator(this, attribute);
        }
    }

I guess it is final.

Thanks Igor. Hope to get help in future as well.

Uzzal Devkota

unread,
Nov 20, 2013, 9:19:57 AM11/20/13
to activejd...@googlegroups.com
Still confused how "Model.getTableName()" returns the table name. o.O

Igor Polevoy

unread,
Nov 20, 2013, 11:41:45 AM11/20/13
to activejd...@googlegroups.com
This should work: 

this.getMetaModelLocal().getTableName();

Uzzal Devkota

unread,
Nov 20, 2013, 12:00:10 PM11/20/13
to activejd...@googlegroups.com
I'm using activejdbc version 1.4.8. And there is getMetaModel() class method instead of getMetaModelLocal() instance method.

What's the point in calling: this.getMetaModelLocal().getTableName();

when you can get the table name by: Model.getTableName();

Any differences between these two approach?

Ayways thanks igor for reply. :)

Igor Polevoy

unread,
Nov 20, 2013, 4:07:34 PM11/20/13
to activejd...@googlegroups.com
Because Model.getTableName() operates on class Model, not your model. Chances are it will not provide you with a table name 

Uzzal Devkota

unread,
Nov 20, 2013, 7:48:51 PM11/20/13
to activejd...@googlegroups.com
Yes, that was my concern.

But, there is no "this.getMetaModelLocale()" method in activejdbc 1.4.8 :(

Igor Polevoy

unread,
Nov 20, 2013, 8:46:28 PM11/20/13
to activejd...@googlegroups.com
sorry, typo: getMetaModelLocal()

Uzzal Devkota

unread,
Nov 20, 2013, 9:55:36 PM11/20/13
to activejd...@googlegroups.com
Thanks.

Se

unread,
Feb 22, 2014, 11:07:43 AM2/22/14
to activejd...@googlegroups.com
would this be correct then?

@Override
    public void validate(Model m) {
        if(Base.count(this.getMetaModelLocal().getTableName(), attribute + " = ? AND id IS NOT ?", m.get(attribute), m.getId()) > 0) {
            m.addValidator(this, attribute);
        }
    }

The if-statement doesn't compile though:
error: cannot find symbol
        if(Base.count(this.getMetaModelLocal().getTableName(), attribute + " = ? AND id IS NOT ?", m.get(attribute), m.getId()) > 0) {
  symbol: method getMetaModelLocal()

Igor Polevoy

unread,
Feb 26, 2014, 1:27:55 AM2/26/14
to activejd...@googlegroups.com
Ag, sorry about that. This will work:

String tableName = Registry.instance().getTableName(m.getClass())

where "m" is an instance of your model 

tx

Se

unread,
Feb 26, 2014, 12:23:05 PM2/26/14
to activejd...@googlegroups.com
Still doesn't compile throwing 'error: getTableName(Class<? extends Model>) has protected access in Registry'

This is the method itself, for the reference:
   public void validate(Model m) {
if(Base.count(Registry.instance().getTableName(m.getClass()), attribute + " = ? AND id IS NOT ?", m.get(attribute), m.getId()) > 0) {

Igor Polevoy

unread,
Feb 26, 2014, 2:22:28 PM2/26/14
to activejd...@googlegroups.com
sorry about that, was trying in the  same project/package, did not see that.
This will do:

System.out.println(Registry.instance().getMetaModel(m.getClass()).getTableName());

tx

Se

unread,
Feb 26, 2014, 8:07:41 PM2/26/14
to activejd...@googlegroups.com
This works. Thanks a lot Igor! Although I had to correct the query for the mysql.

For those who would stumble here later, the final validator class is as follows (though you may need to modify the query for your DBMS):

public class UniquenessValidator extends ValidatorAdapter {
private final String attribute;
public UniquenessValidator(String attribute) {
this.attribute = attribute;
setMessage("should be unique");
}
@Override
public void validate(Model m) {
if(Base.count(Registry.instance().getMetaModel(m.getClass()).getTableName(), attribute + " = ? AND id != ?", m.get(attribute), m.getId()) > 0) {
m.addValidator(this, attribute);
}
}
}

Igor Polevoy

unread,
Feb 26, 2014, 11:33:27 PM2/26/14
to activejd...@googlegroups.com
I wonder if we might create a separate module for third party contributions like this? 
I do not feel including it because of a potential race condition, but at the same time some people might find it useful 

thoughts?

Igor Polevoy

unread,
Feb 26, 2014, 11:34:16 PM2/26/14
to activejd...@googlegroups.com
I meant to say: "I do not feel including it into the framework proper..."

Seva

unread,
Feb 27, 2014, 11:04:27 AM2/27/14
to activejd...@googlegroups.com
I think featuring it on the Validators page on the website would be great. Not sure about a separate module, but dropping it into the examples folder as part of the custom validator examples could be useful.
As for the race condition, how does Rails ActiveRecord solve this? I think the uniqueness validator definitely must be part of the framework.

Also, it would be great to open source the website so fixes, improvements and better examples could be contributed.

Igor Polevoy

unread,
Feb 27, 2014, 1:48:20 PM2/27/14
to activejd...@googlegroups.com
Seva, maybe you are right, and it should be part of framework. Will integrate iater on.
We are working on the site, but things are slower than they should be. The documentation pages are being converted to Markdown files hosted on Github, so everyone can be contribute (in about 2 weeks time)

As far as how Rails does it, not sure, but worth looking. 

thanks

Igor Polevoy

unread,
Feb 27, 2014, 1:52:46 PM2/27/14
to activejd...@googlegroups.com
filed this: https://github.com/javalite/activejdbc/issues/234
Would you like to send in a PL for this?
tx
Reply all
Reply to author
Forward
0 new messages