Grails 3 - findAllBy with join - grails

I have a domain class:
class Owner {
Integer ownerType
Prop propertyToJoinSometimes
static constraints = {
propertyToJoinSometimes nullable: true
}
}
I usually don't want to load propertyToJoinSometimes when loading Owner, but I sometimes load many Owner objects at once using findAllBy, and a join could save a large number of calls to the database. Is there a way to do something like:
Owner.findAllByOwnerType(2, [propertyToJoinSometimes: [fetch: 'join']])

This is not exactly what you are looking for (not using the dynamic finder findAllBy) but it produces the results you are after. The grails documentation for createCriteria/withCriteria does not mention it but there is a fetchMode method within the HibernateCriteriaBuilder.
import org.hibernate.FetchMode
Owner.withCriteria {
eq('ownerType', 2)
fetchMode('propertyToJoinSometimes', FetchMode.JOIN)
}

Just to add an option using dynamic finder:
Owner.findAllByOwnerType(2, [fetch: ['propertyToJoinSometimes': 'eager']])

Related

typeorm table name specified more than once

I have the next query:
const foundDeal: any = await dealRepository.findOne({
where: { id: dealId },
relations: ['negotiationPointsDeals', 'chosenInventoryToSubtractQuantity',
'chosenInventoryToSubtractQuantity.inventoryItemType',
'chosenInventoryToSubtractQuantity.inventoryItemType.quality', 'negotiationPointsDeals.negotiationPointsTemplate',
'chosenInventoryToSubtractQuantity.addressOfOriginId', 'chosenInventoryToSubtractQuantity.currentLocationAddress',
'chosenInventoryToSubtractQuantity.labAttestationDocs',
'chosenInventoryToSubtractQuantity.labAttestationDocs.storage',
'chosenInventoryToSubtractQuantity.proveDocuments', 'chosenInventoryToSubtractQuantity.proveDocuments.storage',
'chosenInventoryToSubtractQuantity.inventoryItemSavedFields', 'chosenInventoryToSubtractQuantity.inventoryItemSavedFields.proveDocuments',
'chosenInventoryToSubtractQuantity.inventoryItemSavedFields.proveDocuments.storage',
'sellerBroker', 'sellerBroker.users',
'seller', 'seller.users',
'buyerBroker', 'buyerBroker.users',
'buyer', 'buyer.users',
'order', 'order.inventory', 'order.inventory.inventoryItemType',
'order.inventory.inventoryItemType.quality',
'order.inventory.addressOfOriginId', 'order.inventory.currentLocationAddress',
'order.inventory.inventoryItemSavedFields', 'order.inventory.inventoryItemSavedFields.proveDocuments',
'order.inventory.inventoryItemSavedFields.proveDocuments.storage',
'order.inventory.labAttestationDocs', 'order.inventory.labAttestationDocs.storage',
// 'postTradeProcessingDeal', 'postTradeProcessingDeal.postTradeProcessingStepsDeal',
'order.inventory.proveDocuments',
'order.inventory.proveDocuments.storage',
'negotiationPointsDeals.negotiationPointsTemplate.negotiationPointsTemplateChoices',
'postTradeProcessing',
],
});
So, the error is next:
error: table name "Deal__chosenInventoryToSubtractQuantity_Deal__chosenInventoryTo" specified more than once.
But I can't see any doubles in query.
I ran into this issue when switching to start using the snake case naming strategy.
I think somehow the aliases that TypeORM generates by default do not collide if you "re-join" to existing eagerly-loaded relations.
However, under the new naming strategy it threw an error if I tried to add in a relation that was already eagerly loaded.
The solution for me was to find and eliminate places where I was doing relations: ["foo"] in a query where foo was already eagerly loaded by the entity.
The issue is documented in this TypeORM issue.
After some digging, I realized this error is due to TypeORM creating some kind of variable when using eager loading that is longer than Postgres limit for names.
For example, if you are eager loading products with customer, typeorm will create something along the lines of customer_products, connecting the two. If that name is longer than 63 bytes (Postgres limit) the query will crash.
Basically, it happens when your variable names are too long or there's too much nesting. Make your entity names shorter and it will work. Otherwise, you could join the tables manually using queryBuilder and assign aliases for them.
It looks like you are using Nestjs, typeorm, and the snakeNamingStrategy as well, so I'll show how I fixed this with my system. I use the SnakeNamingStrategy for Typeorm which might be creating more issues as well. Instead of removing it, I extended it and wrote an overwriting function for eager-loaded aliases.
Here is my solution:
// short-snake-naming.strategy.ts
import { SnakeNamingStrategy } from "typeorm-naming-strategies";
import { NamingStrategyInterface } from "typeorm";
export class ShortSnakeNamingStrategy
extends SnakeNamingStrategy
implements NamingStrategyInterface
{
eagerJoinRelationAlias(alias: string, propertyPath: string): string {
return `${alias.replace(
/[a-zA-Z]+(_[a-zA-Z]+)*/g,
(w) => `${w[0]}_`
)}_${propertyPath}`;
}
}
// read-database.configuration.ts
import { TypeOrmModuleOptions, TypeOrmOptionsFactory } from "#nestjs/typeorm";
import { SnakeNamingStrategy } from "typeorm-naming-strategies";
import { ShortSnakeNamingStrategy } from "./short-snake-naming.strategy";
export class ReadDatabaseConfiguration implements TypeOrmOptionsFactory {
createTypeOrmOptions(): TypeOrmModuleOptions | Promise<TypeOrmModuleOptions> {
return {
name: "read",
type: "postgres",
...
namingStrategy: new ShortSnakeNamingStrategy(),
};
}
}
The ShortSnakeNamingStrategy Class takes each eager-loaded relationship and shortens its name from Product__change_log___auth_user___roles__permissions to P_____c____a___r__permissions
So far this has generated no collisions and kept it below the 63 character max index length.

Grails GORM find by object property's property

I am looking for an easier grails way(if it exists) to achieve below:
My UserIntegration.groovy domain class:
class UserIntegration {
User user
Integration integration
// rest of code
}
My Integration.groovy domain:
class Integration {
SomeType type
// rest of code
}
My SomeType.groovy domain:
class SomeType {
String name
// rest of code
}
So basically what I need is a way so that I can find a UserIntegration which has Integration with type.name = "someTypeName"
In other words, I am looking for UserIntegration.findByIntegrationTypeName("someTypeName");
Obviously such a dynamic finder does not exist, but is there any easier groovier way to do it? I can currently find out all Integration objects with type = "someTypeName" and then use this integration in findAllByIntegration but looking for an easier solution if it exists, preferably a dynamic finder.
You can also use HQL to do same
def list = UserIntegration.executeQuery(from UserIntegration userInt
inner join fetch userint.integration integration
inner join fetch integration.type sometype
where sometype.name =:name,[name:'someTypeName'] )
UserIntegration.createCriteria().list(max:1){
'integration'{
'type'{
eq('name',"someTypeName")
}
}
}
Hope it works

Grails Criteria Projection - No signature of method projections() is applicable for argument types

As per Grails documentation
Grails also lets you write your domain model in Java or reuse an existing one that already has Hibernate mapping files. Simply place the mapping files into grails-app/conf/hibernate and either put the Java files in src/java or the classes in the project's lib directory if the domain model is packaged as a JAR. You still need the hibernate.cfg.xml though!
So This is exactley what i did.
I have used java domain model and hibernate.cfg.xml file for mapping. I also use
{DomainName}Constraints.groovy for adding Grails constraints. I also used to add functions to {DomainName}Constraints. For example, below is the content of my EmployeeConstraints.groovy
Employee.metaClass.static.findByDepartment = {depCode ->
createCriteria().list {
department{
inList ('code', depCode)
}
}
}
Now this works fine. But, when i add projection to it(code below), just to get the employee code.
Employee.metaClass.static.findByDepartment = {depCode ->
createCriteria().list {
projections { property('empCode', 'empCode') }
department { inList ('code', depCode) }
}
}
I get the below error..
" No signature of method: com.package.script142113.projections() is applicable for argument types.. "
Can someone point me to whats wrong with the code?
Thanks!
The property projection is used to return a subset of an object's properties. For example, to return just the foo and bar properties use:
projections {
property('foo')
property('bar')
}
You're getting an error because you've called the property method with 2 arguments instead of one.
By the way, I see another potential with your code. Grails will automatically create a dynamic finder findByDepartment that has the same name as the method your trying to add via the meta-class. I have no idea which one will take precendence, but I would suggest you avoid this potential problem and simplify your code, by adding this query using Grails' named query support, and call it something like getByDepartment so that the name doesn't class with a dynamic finder.
The answer by Dónal should be the correct one, but I found a strange behavior with grails 3.1. I got the same message using this call:
Announcement.createCriteria().list {
projections {
property('id')
property('title')
}
} .collect { [id: it['id'], title: it['title']] } // it['id'] not found
I fixed it by removing projections closure:
Announcement.createCriteria().list {
property('id')
property('title')
} .collect { [id: it['id'], title: it['title']] } // got the it['id']
Hope this help.

Object Id set as null when saving - AjaxDependencySelection

So I decided to use AjaxDependencySelection Plugin for Grails, and it has proven to be very useful. However, I am trying to implement autoComplete boxes, and it does not seem to be saving the object id when using an Autocompleted selection. Here is my implementation in my gsp
<g:selectPrimary id="template" name="template"
domain='dms.nexusglobal.Template'
searchField='templateName'
collectField='id'
domain2='dms.nexusglobal.Tag'
bindid="template.id"
searchField2='tagName'
collectField2='id'
hidden="hiddenNew"
noSelection="['': 'Please choose Template']"
setId="tag"
value="${documentPartInstance?.template}"/>
<g:selectSecondary id="tag" name="tag"
domain2='dms.nexusglobal.Subtag'
bindid="tag.id"
searchField2='subtagName'
collectField2='id'
autocomp="1"
noSelection="['': 'Please choose Tag']"
setId="subtag"
value="${documentPartInstance?.tag}"/>
<g:autoCompleteSecondary id="subtag" name="subtagId"
domain='dms.nexusglobal.Subtag'
primarybind='tag.id'
hidden='tag'
hidden2='hidden5'
searchField='subtagName'
collectField='id'
value='${documentPartInstance?.subtag}'/>
<input type=hidden id="hidden5" name="subtagId" value="${documentPartInstance?.subtag}"/>
However, everytime I save it, I am presented with this error Column 'subtag_id' cannot be null . Here is my domain class definition for Subtag
class Subtag {
static scaffold = true
String subtagName
static belongsTo = [tag : Tag]
public Subtag()
{
}
public Subtag(String s)
{
subtagName = s
}
static constraints = {
}
String toString(){
subtagName
}
}
Tag hasMany subtags as well
It seems to be creating new Subtag instances when using the autoselect box (as an error shows up saying Could not find matching constructor for:packagename.Subtag(java.lang.String) Although this is a feature I am looking to implement in my application at later stages (being able to create new Subtags on the fly when creating a document Part), right now, all I would like to be able to do is just choose from my already existing subtags.
When I add in a string constructor, it comes back with the error that Column subtag_id cannot be null
I have developed it so will try help you through your issue.
The problem is that you are trying to push a value from selectSecondary and update the elementId of g:autocomplete which is actually a seperate entity.
I will update the plugin with a new method, need to test it out first.. Also take a look at g:selectAutoComplete. Although this method would only work if your secondary was the primary task... so no good in that case either..
hang on and look out for 0.37 release
Released 0.37 documentation on how to do such a thing here: https://github.com/vahidhedayati/ajaxdependancyselection/wiki/from-selection-to-autocomplete---how-to

my own id in GORM

I tried to change the standard 'id' in grails:
calls Book {
String id
String title
static mapping {
id generator:'assigned'
}
}
unfortunately, I soon noticed that this breaks my bootstrap. Instead of
new Book (id:'some ISBN', title:'great book').save(flush:true, failOnError:true)
I had to use
def b = new Book(title:'great book')
b.id = 'some ISBN'
b.save(flush:true, failOnError:true)
otherwise I get an 'ids for this class must be manually assigned before calling save()' error.
but that's ok so far.
I then encountered the same problem in the save action of my bookController. But this time, the workaround didn't do the trick.
Any suggestions?
I known, I can rename the id, but then I will have to change all scaffolded views...
That's a feature of databinding. You don't want submitted data to be able to change managed fields like id and version, so the Map constructor that you're using binds all available properties except those two (it also ignores any value for class, metaClass, and a few others).
So there's a bit of a mismatch here since the value isn't managed by Hibernate/GORM but by you. As you saw the workaround is that you need to create the object in two steps instead of just one.
I can't replicate this problem (used Grails 2.0.RC1). I think it might be as simple as a missing equal sign on your static mapping = { (you just have static mapping {)
Here's the code for a domain object:
class Book {
String id
String name
static mapping = {
id generator:'assigned'
}
}
And inside BootStrap.groovy:
def init = { servletContext ->
new Book(name:"test",id:"123abc").save(failOnError:true)
}
And it works fine for me. I see the id as 123abc.
You need to set the bindable constraint to true for your id prop, e.g.
class Employee {
Long id
String name
static constraints = {
id bindable: true
}
}

Resources