join a table in two columns due to polymorphic relation - join

I have the following structure:
Buro->hasOne->Address->hasOne->City->hasOne->State
Buro->hasOne->Category
and
Order->hasOne->Address->hasOne->City->hasOne->State
Order->hasOne->Category
and
Category->hasMany->Orders and Category->hasMany->Buros
I have a "One-To-Many" polymorphic relation between addresses in one side and Buro/Orders in the other side. There is just One address for a buro and just one adddress for an order, therefor I couls use [0] to get the first the first and only one element in a collection.
Address class
class Address extends Model
{
...
public function addressable()
{
return $this->morphTo();
}
...
}
Büro class
class Buro extends Model
{
....
public function addressable()
{
return $this->morphMany('App\Address', 'addressable');
}
...
}
Order class
class order extends Model
{
....
public function addressable()
{
return $this->morphMany('App\Address', 'addressable');
}
...
}
Now I am building a form in the frontend with a SELECT field in order to select just from the Buros that operate in the same State where the order was placed.
Using ELOQUENT I did this:
$all_bueros = Buero::where('category_id', $order->category_id)
->where('title', $order->client->addressable[0]->city->state->title) // <- Look at this "title" column name
->pluck('title', 'id')->all();
The problem here is that the "Title" column name is not in "Buros" table but three tables away. Exactly in: buros.addresses.city.state.title
I have tried these syntaxes but no one worked:
->where('buros.addresses.city.state.title', $order->client->addressable[0]->city->state->title) // as object notation
and
->where('buros->addressable[0]->cities->state->title', $order->client->addressable[0]->city->state->title) // using the relation
I used the query builder but there is even more messy:
$buros = DB::table('buros')
->join('categories', 'categories.id', '=', $order->category_id)
->join('addresses', 'addresses.addressable_id', '=', $order->id, 'AND','addresses', 'addresses.addressable_type', '=', 'buros') //two points to join the table
->join('cities', 'addresses.city_id', '=', 'cities.id')
->join('states', 'cities.states', '=', 'states.id')
->select('buros.title', 'buros.id')
->where('states.title', '=', $order->client->addressable[0]->city->states->title)
->get();
My problem is how to join a table in two points since there is a polymorphic relation. Because I need to join the Address Table in the "Buro-column" (addressable_type) and in the Buro-id column (addressable_id)
Any help will be appretiate

Related

findAll order by "many to one" column raises exception

I have an entity, Student, defined in Student.groovy as:
#EqualsAndHashCode(includes = ['id'])
class Student {
Long id
String name
String type
University university
static mapping = {
university column : 'UNIVERSITY_ID'
}
}
and a University entity, defined in University.groovy as:
class University {
Long id
String name
static mapping = {
id column : 'id', generator : 'assigned'
}
}
I've been trying to switch from calling
Student.list(sort: ..., order: ...)
to calling:
Student.findAll("from Student s where type = :type ", [type : 'T'], [ sort : 'name' ])
This fails to order correctly by the name field. The previous version, using list worked fine.
I've also tried calling something like
Student.findAll(sort : 'name') { type == "T" }
which worked fine like this, but when trying to sort by the university.name
Student.findAll(sort : 'university.name') { type == 'T" }
it raised an error regarding the university.name field not being found.
Anybody have any idea on how to do this properly?
Thank you.
Use executeQuery instead of findAll - they should function the same, but I've found that executeQuery is for some reason a more direct caller of the HQL, and findAll fails or returns unexpected results in some cases.
So that first query would be
Student.executeQuery(
'select s from Student s where s.type = :type order by s.name',
[type : 'T'])
and ordering by university name would be
Student.executeQuery(
'select s from Student s where s.type = :type order by s.university.name',
[type : 'T'])
I like HQL and tend to use it a lot, but it couples you to Hibernate and relational databases - if you want to switch to a NoSQL database these queries will fail. Criteria queries, "where" queries and finders all use criteria queries internally, and those are converted to native query API calls by the GORM implementation.
The equivalent criteria queries would be
Student.withCriteria {
eq 'type', 'T'
order 'name', 'asc'
}
and
Student.withCriteria {
eq 'type', 'T'
university {
order 'name', 'desc'
}
}
Some unrelated notes:
You shouldn't use id in equals or hashCode calculations; if you have a persistent Student and a new non-persistent instance with the same name, type, and University, they should be considered equal, but since the non-persistent instance's id will be null they'll be considered different.
You don't need to specify the id property - Grails adds it and the version field to the bytecode via an AST transformation during compilation.
There's no need to map the column name of the university property to 'UNIVERSITY_ID' - that's what it would be anyway.
You can omit the redundant column setting in the id mapping.
Here's the Student class with cruft removed:
#EqualsAndHashCode(includes = ['name', 'type', 'university'])
class Student {
String name
String type
University university
}
and University:
class University {
String name
static mapping = {
id generator: 'assigned'
}
}

LINQ to Entities query with join inside method for use in MVC app

In my Person table is a RequestedLocation column which stores location IDs. The IDs match the LocationId column in the Locations table, the Locations table also has the text location names, in the LocatioName column.
In my view, I need to display the string LocationName in the view which has the Person model passed to it. The view will be displaying a List of people in a telerik grid. CUrrently it works great, except the RequestedLocation column is all integers.
I am populating all my grids with methods containing LINQ queries. Here is the method that currently works:
public List<Person> GetPeople()
{
var query = from p in _DB.Person.ToList()
select p;
return query.ToList();
}
Here is the regular SQL query that works, and I need to convert into LINQ:
SELECT ApplicantID
,FirstName
,LastName
,MiddleName
,DateofBirth
,Gender
,RequestedVolunteerRole
,RequestedVolunteerLocation
,l.LocationName
FROM Form.Person p
JOIN dbo.Location l ON p.RequestedVolunteerLocation = l.LocationID
Order BY ApplicantID
Here is my attempt to convert to LINQ:
public List<NewApplicantViewModel> GetPeople()
{
var query = from pl in _DB.Person.ToList()
join l in _Elig_DB.Locations.ToList() on pl.RequestedVolunteerLocation equals l.LocationID
select new
{
pl.RequestedVolunteerLocation = l.LocationName
};
return query.ToList();
The number of errors I get from this are numerous, but most are along the lines of:
Cannot convert from type Annonymous to Type List<NewAPplicantModel>
and
Invalid annonymous type declarator.
Please help, and thank you for reading my post.
Oh, and I have only been programming for a couple months, so if I am going about this all wrong, please let me know. Only thing I have to stick with is the table structure because it is an existing app that I am updating, and changing the location or person tables would have large consequences.
public List<NewApplicantViewModel> GetPeople()
{
var query = from pl in _DB.Person
join l in _Elig_DB.Locations on pl.RequestedVolunteerLocation
equals l.LocationID
select new NewApplicantViewModel
{
LocationName = l.LocationName,
otherPropery = p.Property
};
return query.ToList();
}
Beware of calling _DB.Person.ToList() it will load all persons from DB because ToList() immediately executes the query and the join would be performed in memory (not in DB).
The reason you are getting an error is you are projecting an anonymous type
select new
{
pl.RequestedVolunteerLocation = l.LocationName
};
Instead, you need to project a NewApplicantViewModel
select new NewApplicantViewModel
{
RequestedVolunteerLocation = l.LocationName
};

Can I recover from association (joinTable) with reference to missing data

I've read through a lot of posts and docs and must be missing something.
In my application (model below) I am having a data issue that seems to be out of my control where I have a categoryId in the join table JOBORDERCATEGORIES that has no corresponding row in the CATEGORY table. I am accessing the category data through getJobCategories() in the JobOrder. This is producing the following error when my Category table is missing a referenced row:
2012-03-07 08:02:10,223 [quartzScheduler_Worker-1] ERROR listeners.SessionBinderJobListener - Cannot flush Hibernate Sesssion, error will be ignored
org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [com.matrixres.domain.Category#416191]
and my code is halting.
I have tried using ignoreNotFound but it is not helping me to get past the error above.
If I missed a post on the solution to this problem please link me to it otherwise thoughts are welcome on how to move forward. Perhaps there is a more direct route I will have to hammer in to achieve my goal of getting a good category list, but I am not familiar enough with the framework to know what is next. As a note, I cannot write to any of these tables.
thanks, rich
A simplified Version of my Model:
Job Order Object:
class JobOrder {
def getJobCategories() {
def cats = []
try {
def jocategories = this.categories
if(!jocategories.isEmpty() && jocategories!=null){
println "we got categories for ${this.id}"
jocategories.each { cat ->
if(cat?.parentCategoryID == 0){
if(cat.occupation != null){
cats << cat.occupation
} else {
cats << cat.name
}
}
}
}
} catch(e) {
cats << "Other Area(s)"
}
cats
}
static mapping = {
table 'dbo.JOBORDER'
version false
id generator: 'identity', column: 'JOBORDERID'
/*
* several other mapped columns deleted here
*/
categories joinTable:[name:'jobOrderCategories', column: 'categoryId', key:'jobOrderID']
}
/*
* several properties deleted here
*/
static hasMany = [categories: Category] //several other hasMany associations exist
}
Category Object:
class Category {
static mapping = {
table 'CATEGORY'
version false
id generator: 'identity', column: 'categoryID'
occupation column: 'OCCUPATION'
name column: 'NAME'
parentCategoryID column: 'PARENTCATEGORYID'
/*
* several other mapped columns deleted here
*/
jobOrders joinTable:[name:'jobOrderCategories', column: 'jobOrderID', key:'categoryId']
}
String name
String occupation
int parentCategoryID
/*
* several properties deleted here
*/
static belongsTo = [JobOrder]
static hasMany = [jobOrders:JobOrder]
}
Join Table:
class JobOrderCategories {
static mapping = {
table 'JOBORDERCATEGORIES'
version false
isDeleted column: 'ISDELETED'
jobOrderID column: 'JOBORDERID'
categoryId column: 'CATEGORYID'
}
Boolean isDeleted
Integer jobOrderID
Integer categoryId
}
These kinds of situations aren't the most fun, but I have had to deal with this kind of Roll-Your-Own ORM problems before ;) Basically what you're going to want to do here is store the object properties not typed as Object references, but as ints, and while you'll lose some of the dynamic finder things GORM makes so nifty, you'll have a fairly straight-forward means of accessing the data that doesn't involve tangling yourself up with Hibernate's innards.
Basically, this will involve ditching your hasMany and belongsTo properties on JobOrder and Category. Instead, you'll want to do things like
def myJobOrder = JobOrder.get(yourId);
def myCategoryIds = JobOrderCategories.findAllByJobOrderID(myJobOrder.id)
def myCategories = Categories.withCriteria {
in('id', myCategoryIds)
}
You can put variations of those traversals in helper methods on your classes, like for example, your getJobCategories method could become
class JobOrder {
//...
def getJobCategories() {
def myCategoryIds = JobOrderCategories.findAllByJobOrderID(this.id)
def myCategories = Categories.withCriteria {
in('id', myCategoryIds)
}
}
}
And so on. This is definitely not the prettiest thing in the world to deal with, and you lose your ability to traverse through things easily with GORM (ex a
jobOrder.withCriteria {
categories {
eq('name', blah)
}
}
becomes a executeQuery type of situation.)
But overall, its not too bad to deal with :)
Hope that helps!

Grails gorm query question

having the domain classes:
class A {
Date dateCreated
static hasMany = [b:B]
...
}
class B {
String name
String value
...
}
What createCriteria or HQL query can I use to return a list with:
A's creation dateB's value for A with the name entry set to 'X'
Note: Although there's no explicit constraint, there's only one "value" for each 'X' and a combination.
Thanks
The HQL would be
def results = A.executeQuery(
'select a.id, a.dateCreated, b from A a inner join a.b b ' +
'where b.name=:name',
[name: 'X'])
This will give you a List of 3-element Object[] arrays containing A.id, A.dateCreated, and the list of B instances. I added the id to the query so you can group by it client-side:
def grouped = results.groupBy { it[0] }
This will be a Map where the keys are the A ids and the values are the Lists from the original results.
Ideally you'd do the grouping at the database, but it would complicate the query, and assuming you don't have a large number of results it should be fast.

GORM, giving names for the columns of a joinTable with a composite id

Working with Grails 1.3.7, I've to deal with a legacy DB. I've a domain object ''Cake'' which have it's own collection of embedded ''Ingredients'', in a join table.
Ingredient.groovy
class Ingredient {
String name
IngredientCategory category
mapping {
table "foo_ingredient"
version false
id composite:['name', 'category']
columns {
word column:"the_name"
category column:"lol_category_id"
}
}
}
Cake.groovy
class Cake {
String name
static hasMany = [ ingredients : Ingredient ]
static mapping = {
table "foo_cake"
version false
columns {
id column:"id"
name column:"the_name"
}
ingredients joinTable: [
name : "foo_cake_ingredient",
key : "cake_id"
]
}
}
Problem is, Grails expect that the table ''foo_cake_ingredient'' to have two columns ''ingredient_name'' and ''ingredient_category_id''. I would like to specify manually those column names and not let Grails (wrongly) guess those. I can't figure how to do that.
I showed how to do this here: http://grails.1312388.n4.nabble.com/Composite-foreign-key-td3046351.html#a3046436
You probably have to create a hibernate xml mapping for the two tables in this relationship.
Have you read http://jasonrudolph.com/blog/2006/06/20/hoisting-grails-to-your-legacy-db/ ?

Resources