I have the following domain classes:
class Team{
static hasMany = [localMatches: Match, visitingMatches: Match]
static mappedBy = [localMatches: 'localTeam', visitingMatches: 'visitingTeam']
List<Match> localMatches = new ArrayList<Match>()
List<Match> visitingMatches = new ArrayList<Match>()
}
class Match{
Team localTeam
Team visitingTeam
}
When I run the following:
Match match = new Match(localTeam: aLocalTeam, visitingTeam: aVisitingTeam)
match.save(flush: true, failOnError: true)
I get the exception "Caused by: org.h2.jdbc.JdbcSQLException: NULL not allowed for column "LOCAL_TEAM_ID"; SQL statement:"
So I need to set the match in each team before saving the match to avoid the exception:
Match match = new Match(localTeam: aLocalTeam, visitingTeam: aVisitingTeam)
aLocalTeam.localMatches.add(match)
aVisitingTeam.localMatches.add(match)
match.save(flush: true, failOnError: true)
Is there any way of mapping the classes so I wouldn't need to do add the match to each team before saving?
The hasMany block defines that a Match has many localMatches, but then below redefines localMatches as a relationship to a single Match. What I believe you actually mean is:
class Team {
static hasMany = [localMatches: Match, visitingMatches: Match]
static mappedBy = [localMatches: 'localTeam', visitingMatches: 'visitingTeam']
}
class Match {
Team localTeam
Team visitingTeam
}
Mapped this way, Team will have two collections of Matches and each Match will have a local and visiting Team.
Related
I have a domain model - where Org has many sites, and has many domains (these are two 'bag' collection attributes).
I want to write a query that retrieves a single, and preferably the sites and domain collections in one hit.
I tried this first
org = Org.findById (id, [fetch:[sites:"eager", domains:"eager"]]
which fails with
cannot simultaneously fetch multiple bags: [com.softwood.domain.OrgRoleInstance.sites, com.softwood.domain.OrgRoleInstance.domains]
(It will work with only one of the two collections).
I tried a criteria query like this
org = resource.withCriteria (uniqueResult: true) {
fetchMode 'sites', FetchMode.SELECT
fetchMode 'domains', FetchMode.SELECT
idEq(id)
sites {
org {
eq 'id', "$id"
}
}
} // didn't work
which errors with this
No signature of method: com.softwood.controller.OrgRoleInstanceController.fetchMode() is applicable for argument types: (java.lang.String, org.hibernate.annotations.FetchMode) values: [sites, SELECT]
This indicates it doesn't like the fetchMode function, (using either SELECT or JOIN).
How do you write a criteria query to return a single matched object by id, but returns sites, and domains collections at the same time?
The domain class looks like this. I don't want to use the static mapping ={} closure - as i want to control the loading through writing explicit queries as required
class Org {
enum OrgRoleType {
Supplier,
Customer,
Service_Provider,
Manufacturer,
Maintainer,
Indeterminate
}
String name
OrgRoleType role
Collection<NetworkDomain> domains = []
Collection<Site> sites = []
Collection<MaintenanceAgreement> mags //optional only set if role is service provider
//Collection<Device> ci
static hasMany = [domains : NetworkDomain, sites: Site, mags:MaintenanceAgreement]
static mappedBy = [mags: "maintainer"] //disambiguate column in mags
static constraints = {
name nullable:false
role nullable:true
domains nullable:true
sites nullable:true
mags nullable:true //optional
}
}
I had seen this [Grails GORM Criteria Query Eager Fetching
I'd tried to do similarly but the fetchMode (String, enum) just won't run.
Update
I changed query to this
org = resource.withCriteria (uniqueResult: true) {
join 'sites'
join 'domains'
idEq(id)
sites {
org {
eq 'id', "$id"
}
}
domains {
customer {
eq 'id', "$id"
}
}
}
This errors with
Cannot invoke method call() on null object
with the trace point to point where it access sites{org{ in the criteria.
First, testing has to be done as integration tests and not unit tests, however the result is you still can't do query finder with two eager loads. if you want to load collections you have to use criteria or possibly a where clause to do so
Here are two integration tests - the first using finders fails with exception, the second achieves the goal using criteria query:
void "query by finderById with multiple eager fetch in map conditions " () {
given :
when: "we print value from a collection "
def org = OrgRoleInstance.findById (7L, [fetch:[sites:"eager", domains:"eager"]])
println org.domains[0].name
then:
org.hibernate.loader.MultipleBagFetchException ex = thrown()
}
void "query withCriteria to do multiple eager fetch in map conditions " () {
given :
def org = OrgRoleInstance.withCriteria (uniqueResult: true) {
fetchMode 'sites', FetchMode.SELECT
fetchMode 'domains', FetchMode.SELECT
idEq (7L)
sites {}
domains {}
}
when: "we print value from a collection "
println org.domains[0].name
then:
org.domains.size() == 1
org.sites.size() == 2
}
Hi I don't really understand whay this doesnt work, but im 99% sure its a GORM issue.
here's my domains:
Class Product{
String name
static hasMany = [parts:Parts]
static mappedBy = [parts:'product']
}
Class Parts{
Product product
static hasMany = [alternatives:Alternatives]
}
so a Product is made up of parts and a part points to a product
When I do the following it doesn't save to the database.
Product p = new Product(name:"test")
Product part1 = Product.get(2)
Product part2 = Product.get(3)
Parts c = new Parts(product: part1).save(flush:true,failOnError:true)
Parts c2 = new Parts(product: part2).save(flush:true,failOnError:true)
p.addToParts(c)
p.addToParts(c2)
p.save(flush:true,failOnError:true)
This doesn't throw any error but also doesn't persist anything to the database.
Any Ideas?
Product p = new Product(name: "test")
Product part1 = Product.get(2)
Product part2 = Product.get(3)
p.addToParts(new Parts(product: part1))
p.addToParts(new Parts(product: part2))
p.save(flush: true, failOnError: true)
The best way to do this is to create the new Parts within your .addToParts() call.
It also seems as though you should have static belongsTo = Product within your Parts class. (Only allowing one side of the Many-to-Many relationship to persist can cause less confusion)
I have a domain class named Logging which stores an id of another domain class: Organization
The structure of both domains is provided:
class Logging {
Date dateCreated
long user_id
long organization_id
String memberCode
static constraints = {
user_id(nullable: false)
organization_id(nullable: false)
memberCode(nullable: true)
}
}
class Organization {
Type type
String name
String memberCode
User manager
String collateralAutoEmails
boolean isBlocked = true
static constraints = {
name(blank: false, unique: true)
manager(nullable: true)
memberCode(nullable: true)
collateralAutoEmails(nullable: true)
}
static mapping = {
manager(lazy: false)
}
}
User enters several parameters: dateCreated, the memberCode and the name of the organization. I need to select all elements from the Logging domain matching these criterias.
The tricky part for me is writing the query for the name of the organisation parameter.
According to the search rules I should check whether organization.name field contains data entered by user as a substring(case insensetive) and select the corresponding element from the Logging domain.
The two domains are not mapped directly and I can't join those tables.I have tried different approaches but still haven't found the solution.
Here you go
Logging.executeQuery("Select l from Logging l, Organization o where l.organization_id = o.id and o.dateCreated = :dateCreated and o.memberCode = :memberCode and o.name = :name", [dateCreated: dateCreated, memberCode: memberCode, name: name])
Try something like this:
Organization.executeQuery("select o from Organization o, Logging l where o.name like = :orgName AND o.id=l.organization_id", [orgName : orgName ])
I didn't tried it, if it works then more search options can be added on the query, and also % can be added on the parameter, in order to enhance the search.
During an archive process I am copying the details from an existing domain object to a new instance of that domain. Both domain objects have a hasMany relationship:
static hasMany = [pets:Pet]
When I have the following scenario:
def ownerOne = (logic to find owner)
def ownerTwo = new Owner
****ownerTwo.pets = ownerOne.pets****
How do I do that starred line? I've tired this:
Set<Pet> ownerTwoPets = new TreeSet<Pet>()
for(Pet p : ownerOne.pets) {
ownerTwoPets.add(p)
}
ownerTwo.pets = ownerTwoPets
With no luck. I can do it with String objects in a hasMany without problem. But I cannot figure it out with domain objects in a hasMany
Grails has a built in method to add to a relationship like this one. Try this:
ownerOne.pets.each { Pet p ->
ownerTwo.addToPets(p)
}
Grails 1.1.1
Goovy 1.5.7
In a relationship such this:
Author 1 -- n Book n -- 1 Publisher
Defined in Grails:
class Author {
String firstName
String lastName
static hasMany = [books: Book]
static constraints = {
books(nullable: true)
}
}
class Book {
String title
Author author
Publisher publisher
static constraints = {
author(nullable: true)
publisher(nullable: true)
}
}
class Publisher {
String name
static hasMany = [books: Book]
static constraints = {
books(nullable: true)
}
}
I want to load a Book with the values of Publisher and Author.
When i get a Book with the query:
def book2 = Book.findAllByAuthor(author)
I get the response with the autor assosiated but the publisher only have the id and name class in the other query:
def book3 = Book.findAllByPublisher(publisher)
I retrieve me the inverse result,i have the book with the publisher data but the author only have the id and the class name.
Where is the error in the defined model ? o there is an error in the way to do the queries ?
Edit:
I need the way to retrieve the values only with the query like this:
def book2 = Book.findAllByAuthor(author, [fetch:[publisher:'eager']])
In this one I can manage the value of publisher.
Question: If publisher had a hasmany or Domain related, getting the book I'm able to read the attributes?
Thanks.
Thanks.
Lazy fetching is used by default with gorm associations. If you want to enable eager fetching, you can modify the ORM DSL by adding the following mappings block to your Author domain class:
static mapping = {
books lazy:false
}
or you could change the fetch mode in the domain object by adding following code after your books relationship is defined.
static fetchMode = [books:"eager"]
Doing the same to your Publisher domain object should allow you to accomplish what you want. You do want to be careful of the consequence that you may load more data than you intend to.
Shouldn't the get() method return what you are looking for?
Example: def book2 = Book.get(author)
You'd better use Criteria and explicitly define which relations should be loaded eagerly. Just mention relation in the query.
Example:
def c = Teacher.createCriteria()
List<Teacher> results = c.list {
subjects {
attendees {}
}
}