Sql error when using GORM's dynamic FindAllBy method - #2 is not set - sql

I have those domain classes:
class Person{
String username
}
class Course {
String code
static hasMany = [events:Event]
}
class Event {
Long id
String name
(...)
static belongsTo = [course:Course, parallel:Parallel]
static hasMany = [teachers: Person, students: Person,bought: Exchange, sold: Exchange]
}
And what I want to do is find all the events associated with a student and a course. I did this query:
(...)
println "User: ${person.username}"
println "Course: ${course.code}"
Set<Person> persons = []
persons.add(person)
def events = Event.findAllByCourseAndStudents(course, persons)
}
But that is giving me an error ERROR util.JDBCExceptionReporter - Parameter "#2" is not set; SQL statement:
What am I missing here?

The issue with the second parameter in your query Students is the fact it's an 1:M (one to many) association and querying these associations using dynamic finders is not supported.
Take a look at this quick overview of the various types of querying supported by GORM/Grails to get a better understanding of what kind of options are available to you and when to use them.
As you can see using a criteria is really what you need in this case:
def events = Event.createCriteria().list() {
eq('course', course)
students {
eqId(person.id)
}
}
}
The above only matches against a single person, but you can modify this further to use a list of persons as you intend. I will leave that exercise to you so you can become familiar with how to create and use criteria.

Related

How to get a column relationship based on field

I have a chat table that both a user and admin can chat the table is defined as follow:
id, from_id, to_id, message, is_from_admin.
what I want is, if the is_from_admin is true laravel should use the admin table at sql level for the from. otherwise it should use the user table for from and same applies to the to field. Thanks
If you have the chance, I'd rework the table a bit and name it like so:
id, from_user_type, from_user_id, to_user_id, message
The pair from_user_type and from_user_id can be used to creat a custom polymorphic relation ("type" refers to the model/table name, and "id" refers to the id of a row in this table) as seen here: https://laravel.com/docs/9.x/eloquent-relationships#one-to-many-polymorphic-relations .
If you also want to send admin-to-admin, you should also add to_user_type, to_user_id so you can create a polymorphic relationship on the receiving side as well.
The polymorphic relation will look something like this:
class ChatMessage
{
public function fromUser()
{
// This function should automatically infer `from_user_type` and `from_user_id`
// from this function name.
return $this->morphTo();
}
}
class AdminUser
{
public function chatMessages()
{
return $this->morphMany(ChatMessage::class, 'fromUser');
}
}
Laravel can not solve what you are doing, which is a polymorphic relationship, based on a boolean. Theoretically you could bind the polymorphic class definition to 0 or 1, but this is a hack at best. Alternatively you could rewrite your table structure to support polymorphic relations.
Instead i would say you achieve something that is working, with what you have. Create two relationships combined with some logic in an accessor. Create a relationship for the admin and for the user.
Chat extends Model
{
public function fromAdmin()
{
return $this->belongsTo(Admin::class, 'from_id')->where('is_from_admin', true);
}
public function fromUser()
{
return $this->belongsTo(User::class, 'from_id')->where('is_from_admin', false);
}
}
Now create the accessor on the Chat model, using your new relationships.
public function getFromAttribute()
{
return $this->fromAdmin ?? $this->fromUser;
}
With this approach, you should be able to access the attribute like this.
Chat::find(1)->from; // either a user or admin based on the data.

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
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.

Use find() and with() together in Laravel query

I have 2 tables employees and employee_locations. One employee has many locations. I need to find out one employee record with related latest employee_locations record. I wrote below query.
$employees = Employee::find([1])->with('employees.employee_locations')->latest()->first();
I am getting below error
BadMethodCallException
Method Illuminate\Database\Eloquent\Collection::with does not exist.
Your issue is that the find method retrieves a collection of Eloquent objects, on which the with method can not be used. You must first specify your relations for the Employee objects and then use find.
The below code will retrieve the employee with the ids specified in find method and locations for each employee:
$employees = Employee::with('employees.employee_locations')->find([1])
create a relation in your model. Something like this:
class Employee extends Model
{
protected $table = 'employees';
public function location()
{
return $this->hasMany(EmployeeLocation::class, 'employeed_id');
}
}
class EmployeeLocation extends Model
{
protected $table = 'employee_locations';
}
$employees = Employee::with('location')->first();
or you do
$employees = Employee::with('location')->find(<employeed_id>);
Try This method
$employees = Employee::with('employees')->where('employeesid',$employeesid)-
>get()->find($employeesid);

Which relationship type to choose in Laravel Eloquent ORM on multiple relationships case

I am new to Laravel and a bit confused about some definitions of ORM.
I am currently working on a simple Trouble ticket management system, and here is my question :
(table: column, column,...)
tickets : id, description, equipment_id
equipments: id, name, vendor_id
vendor: id, name
This is a very short resume of my tables and its relations, following Laravel's conventions. How can I build these models?
Basically I need to retrieve, for example, how many tickets were opened to a certain vendor (how many times I called the vendor for support).
Thank you in advance
What zwacky said is entirely (edit: maybe not entirely correct in the end) true for close relations, but in your situation there is nested relation:
Vendor -> Equipment -> Ticket
Then to retrieve tickets for particular vendor you would define relation on Vendor model like this:
class Vendor extends Eloquent {
public function equipment()
{
return $this->hasMany('Equipment');
}
public function tickets()
{
return $this->hasManyThrough('Ticket', 'Equipment');
}
class Equipment extends Eloquent {
public function tickets()
{
return $this->hasMany('Ticket');
}
public function vendor()
{
return $this->belongsTo('Vendor');
}
class Ticket extends Eloquent {
public function equipment()
{
return $this->belongsTo('Equipment');
}
and to get count of total tickets for the vendor (not currently open):
Vendor::find($id) // retrieve particular vendor
->tickets()->count(); // get count on tickets table
// and this way you retrieve collection of related tickets
Vendor::find($id) // retrieve particular vendor
->tickets; // get Eloquent Collection
Also you may find it helpful: http://softonsofa.com/querying-relations-with-eloquent-in-laravel-4/
you'd need to declare these relationships within their models. e.g. your Ticket.php model could look like this:
class Ticket extends Eloquent {
public function equipment()
{
return $this->hasOne('Equipment');
}
public function vendor()
{
return $this->hasOne('Vendor');
}
...
}
for retrieval you'd do it like this:
foreach (Ticket::all() as $ticket) {
$ticket->vendor()->id;
}
check this section of the laravel docs.
edit: for the specific query how many tickets are open to a certain vendor:
Ticket::where('open', '=', 1)->vendor()->where('id', '=', 42);

NHibernate.Mapping.Attributes.Filter

I'm mapping my database tables using NHibernate with NHibernate.Mapping.Attributes library and I got stuck to get the Filter attributes to work.
Suppose a class A that has a set of objects of class B. So, I have, the following:
[NHibernate.Mapping.Attributes.Set(0, Inverse = true, Lazy = NHibernate.Mapping.Attributes.CollectionLazy.False)]
[NHibernate.Mapping.Attributes.Key(1, Column = "ClassAId")]
[NHibernate.Mapping.Attributes.OneToMany(2, Class = "ClassB, Assembly")]
public virtual ISet<ClassB> ClassBs { get; set; }
I want to create a filter on this collection to bring only class B objects that satisfy a given criteria, such as Status = 1.
How can I create such Filter?
The where parameter of the Set mapping should be able help you out. Per the documentation the where parameter:
where: (optional) specify an arbitrary
SQL WHERE condition to be used when
retrieving or removing the collection
(useful if the collection should
contain only a subset of the available
data)
So to filter on Status (assuming Status is a SQL column in the table mapped for ClassB - though this column does not have to be mapped in the NHibernate mapping).
[NHibernate.Mapping.Attributes.Set(0,...., Where = "Status = 1", .....)]
...
public virtual ISet<ClassB> ClassBs { get; set; }