activejdbcjavalite

Virtual attribute of ActiveJDBC model


I had a ActiveJDBC model called Job, and defined some static attributes like title, salary, workplace and so on.

public class Job extends Model {

  public String getTitle() {
    return getString("title");
  }

  public void setTitle(String title) {
    setString("title", title);
  }

  public Integer getSalary() {
    return getInteger("salary");
  }

  public void setSalary(Integer salary) {
    setInteger("salary", salary);
  } 

  public String getWorkplace() {
    return getString("workplace");
  }

  public void setWorkplace(String workplace) {
    setString("workplace", workplace);
  }  
}

Now I want to find jobs based on geometry distance by below sql:

String sql = "select *, ST_distance(...) as distance from jobs... order by distance asc";
LazyList<Job> jobs = Job.findBySql(sql);

How can I read the virtual attribute distance from Job model?

I have tried to add distance column in jobs table, and it reported error ERROR: ORDER BY "distance" is ambiguous


Solution

  • Not sure what you mean by a "virtual" attribute, but ActiveJDBC models are just Java classes, so you can add whatever you want to them. JavaLite will not be handling them for you though. It is your responsibility to add appropriate setters and getters and maintain their state as if this was a regular Java class.

    So, you want this wrapped into a model:

    String sql = "select *, ST_distance(...) as distance from jobs... order by distance asc";
    LazyList<Job> jobs = Base.findAll(sql);
    

    you are on the right path. Please, read the JavaDoc for this method carefully:

    http://javalite.github.io/2.4-j8/org/javalite/activejdbc/Model.html#findBySQL-java.lang.String-java.lang.Object...-

    Especially focus on this part: "Ensure that the query returns all columns associated with this model, so that the resulting models could hydrate themselves properly.".

    The "*" in your query takes care of the first part, and the second part will be ignored by a model automatically but will participate in the selection: " Returned columns that are not part of this model will be ignored, but can be used for clauses like above."

    So, all you have to do is to write a method like this:

    
    public class Job{
    
      public List<Map> getNearJobs(){
    
         String sql = "select *, ST_distance(. ? . ? .) as distance from jobs... order by distance asc";
         return Base.findAll(sql, getLogitude(), getLatitude());
      }
    
    }
    

    this way, you will construct a query that will bring (and order) the right maps with their attributes filled appropriately.

    So, you will get a list of Maps, not Jobs, but it will include all the data you need. You can use this approach with some code acrobatics to create new models from a list of maps (Model.fromMap()) and separately inject your "distance" attribute in each model. However, I'm personally against this because then a model is not a model since a model is mapping to a table.