Assuming a domain class with a simple association:
class User {
Country country
}
Grails by default allows binding it by id:
new User(country: 1).country
≫ Country: 1
I'm trying to use the BindUsing annotation to bind it by another property:
class User {
#BindUsing({ obj, src ->
Country.findByCode(src['country'])
})
Country country
}
But it fails, even though the country exists:
Country.findByCode('US')
≫ Country: 1
new User(country: 'US').country
≫ null
Moreover, I can't seem to be able to debug the closure, because if I try to print anything coming from the src object, I get this error:
java.lang.IncompatibleClassChangeError: Expected static method
java.io.PrintStream.println(Ljava/lang/Object;)V
Printing constant values works.
Related
Setup: Grails 2.5.6 with Hibernate 4.3.10
I have a table with a string id. Thing is, its values are numeric strings, and this seems to mess up get() when I pass in a value such as "000000".
Domain class:
class Term
{
static mapping = {
id name: 'code', generator: 'assigned'
version false
code column: 'CODE'
description column: 'DESC'
}
String code
String description
}
Data looks like:
CODE || DESC
-------++---------------------------
000000 || The Beginning of Time
201715 || Post Secondary Winter 2017
201815 || Post Secondary Winter 2018
999999 || The End of Time
And then in testing I found the following:
assert Term.list() // works fine
assert !Term.get('foo') // works fine
//assert Term.get('000000') // throws exception
The exception thrown is:
Method threw 'org.springframework.orm.hibernate4.HibernateSystemException' exception.
Provided id of the wrong type for class Term. Expected: class java.lang.String, got class java.lang.Long
org.hibernate.TypeMismatchException: Provided id of the wrong type for class Term. Expected: class java.lang.String, got class java.lang.Long
So it looks like at some point the '000000' and the '201715' and whatever else are being inconveniently converted into Long objects. Using as String doesn't help either. Can anyone help me tell Hibernate that this String should be treated as a String?
This seems like a Grails bug and I'm guessing it is because you have not declared id to be of type String in your domain class because it is mapped to a different field (which makes sense).
You could try adding
String id
to your domain class although that may not caused desired behaviour with column generation.
I would suggest rather than using get() you could use findByCode() as you have mapped your id to the code field and the result should be the same.
I am currently doing the facebook relayjs tutorial and I need help understanding this part of the tutorial, it states
Next, let's define a node interface and type. We need only provide a
way for Relay to map from an object to the GraphQL type associated
with that object, and from a global ID to the object it points to
const {nodeInterface, nodeField} = nodeDefinitions(
(globalId) => {
const {type, id} = fromGlobalId(globalId);
if (type === 'Game') {
return getGame(id);
} else if (type === 'HidingSpot') {
return getHidingSpot(id);
} else {
return null;
}
},
(obj) => {
if (obj instanceof Game) {
return gameType;
} else if (obj instanceof HidingSpot) {
return hidingSpotType;
} else {
return null;
}
}
);
On the first argument on nodeDefinition,where did it get its' globalId? is Game and HidingSpot a name on the GraphQLSchema? What does this 'const {type, id} = fromGlobalId(globalId);' do? and also what is the 2nd argument? I need help understanding nodeDefinitions, somehow I can't find nodeDefinitions on the official documentation. Thank you.
If you were writing a GraphQL server without Relay, you'd define a number of entry points on the Query type, eg:
type Query {
picture(id: Int!): Picture
user(id: Int!): User
...etc
}
So when you want to get a User, you can easily get it because user is available as an entry point into the graph. When you build a query for your page/screen, it'll typically be several levels deep, you might go user -> followers -> pictures.
Sometimes you want to be able to refetch only part of your query, perhaps you're paginating over a connection, or you've run a mutation. What Relay's Node interface does is give you a standard way to fetch any type that implements it via a globally unique ID. Relay is capable of recognising such nodes in its queries, and will use them if possible to make refetching and paginating more efficient. We add the node type to the root Query type:
type Query {
picture(id: Int!): Picture
user(id: Int!): User
...etc
node(id: ID!): Node
}
Now for nodeDefinitions. Essentially this function lets us define two things:
How to return an object given its globalId.
How to return a type given an object.
The first is used to take the ID argument of the node field and use it to resolve an object. The second allows your GraphQL server to work out which type of object was returned - this is necessary in order for us to be able to define fragments on specific types when querying node, so that we can actually get the data we want. Without this, we couldn't be able to successfully execute a query such as this:
query Test {
node(id: 'something') {
...fragment on Picture {
url
}
...fragment on User {
username
}
}
}
Relay uses global object identification, which means, in my understanding, if your application ever try to search for an object. In your example, try to look for a game, or try to look for a hidingSpot. Relay will try to fetches objects in the standard node interface. i.e. find by {id: 123} of the Game, or find by {id:abc} of the hidingSpot. If your schema (Game, HidingSpot) doesn't set up the node interface, Relay will not be able to fetch an object.
Therefore, if your application requires a search in a "Game", in the schema, you need to define the node interfaces.
By using graphql-relay helper, use nodeDefinitions function only once in your application to basically map globally defined Ids into actual data objects and their GraphQL types.
The first argument receives the globalId, we map the globalId into its corresponding data object. And the globalId can actually be used to read the type of the object using fromGlobalId function.
The second function receives the result object and Relay uses that to map an object to its GraphQL data type. So if the object is an instance of Game, it will return gameType, etc.
Hope it will help you understand. I am on my way learning, too.
I have the following (simplified) Entity SQL query:
SELECT VALUE a
FROM Customers AS a
WHERE a.Status NOT IN { 2, 3 }
The Status property is an enumeration type, call it CustomerStatus. The enumeration is defined in the EDMX file.
As it is, this query doesn't work, throwing an exception to the effect that CustomerStatus is incompatible with Int32 (its underlying type is int). However, I couldn't find a way to define a list of CustomerStatus values for the IN {} clause, no matter what namespace I prefixed to the enumeration name. For example,
SELECT VALUE a
FROM Customers AS a
WHERE a.Status NOT IN { MyModelEntities.CustomerStatus.Reject, MyModelEntities.CustomerStatus.Accept }
did not work, throwing an exception saying it could not find MyModelEntities.CustomerStatus in the container, or some such.
Eventually I resorted to casting the Status to int, such as
SELECT VALUE a
FROM Customers AS a
WHERE CAST(a.Status AS System.Int32) NOT IN { 2, 3 }
but I was hoping for a more elegant solution.
Oooh, you are writing Entity SQL directly. I see... Any reason you aren't using DbSet instead of writing Entity SQL by hand? Could always do
var statuses = new [] { Status.A, Status.B };
var query = context.SomeTable.Where(a => !statuses.Contains(a.Status)).ToList();
I am currently trying to save a List of numbers associated to a domain model in Grails and I keep getting errors. So the scenario is:
I have a Client Domain Model that is shown below and this has a HasMany relationship with a PhoneNumbers Domain model that is also shown below. There is a view where a bunch of numbers are added and then these are stored in a String [] for processing on the controller.
Client Domain:
class Client {
String name
List numbers = new ArrayList()
//This represents a message belonging to a single department
static hasMany = [numbers:PhoneNumbers]
static constraints = {
name(blank:false)
}
}
Phone Numbers Domain:
class PhoneNumbers {
String number
//This represents a message belonging to a single department
static belongsTo = [client:Client]
static constraints = {
number(blank:false)
}
}
As you can see the Client hasMany Phone Numbers and the Phone Numbers belongTo a Client. So in my controller I presumed all's I would need to do is to pass these numbers to the Domain in an Array or List and it would handle the cascade save, my controller function is below:
//This is an array of phone numbers stored in a sesson object as String []
def numbers = session.getAttribute("phoneNumbers")
def numbersConvert = numbers as List
def client = new Client(numbers: numbersConvert, name: params.name)
if (!client.save()) {
client.errors.each{
println(it)
}
}
so the controller above I would expect to take the array of numbers which I have confirmed has values and then convert that to a List. Then save the new Client data and cascade save all the numbers that that client has in the List. However this does not work and I get the error below:
GRAILS-7799: Subtype 'java.lang.String' of reloadable type com.tool.PhoneNumbers is not reloadable: may not see changes reloaded in this hierarchy (please comment on that jira)
| Error 2013-07-30 07:56:50,831 [http-bio-8080-exec-4] ERROR property.BasicPropertyAccessor - IllegalArgumentException in class: com.smstool.PhoneNumbers, getter method of property: id
| Error 2013-07-30 07:56:50,836 [http-bio-8080-exec-4] ERROR errors.GrailsExceptionResolver - IllegalArgumentException occurred when processing
object is not an instance of declaring class. Stacktrace follows:
Message: object is not an instance of declaring class
I did also try another approach to the save the data within the controller as shown below however this did not work either:
numbers.each{
def phoneNumber = new PhoneNumber(number: it).save(flush: true)
client.addToPhoneNumber(phoneNumber).save(flush: true)
}
I presume I am missing something silly and this is probably a really easy thing to do just so tired and need a little help.
Thanks in advance
I have a grails domain class Character
class Character {
String name
int level
boolean alive
Player player
static constraints = {
name(blank:false, unique:true)
level(min:1)
player(nullable:false)
}
}
I want to query for a character with a specified player, where the value of alive is "true". I tried using the following, but it
Character.findByPlayerAndAliveEqual(p, true)
But it generates an exception
No signature of method: static java.lang.Character.findByPlayerAndAliveEqual() is applicable for argument types: (com.thestreetsgame.security.Player, java.lang.Boolean) values: [com.thestreetsgame.security.Player : 1, true]
I've also tried findByPlayerAndAlive, with the same result. How can I make this gorm query work?
Oops, the important part of the exception just jumped out at me.
java.lang.Character
I was trying to do a lookup on the core java class instead of my domain class. Need to always use the fully qualified name, or change the name of the class.
For the time being I've fully qualified the reference, and it is working.