Access Relationship Table in Grails - grails

I have the following domains classes:
class Posts{
String Name
String Country
static hasMany = [tags:Tags]
static constraints = {
}
}
class Tags{
String Name
static belongsTo = Posts
static hasMany = [posts:Posts]
static constraints = {
}
String toString()
{
"${TypeName}"
}
}
Grails creates an another table in the database i.e. Posts_Tags.
My requirement is:
E.g. 1 post has 3 tags.
So, in the Posts_Tags table there are 3 rows.
How can I access the table Posts_Tags directly in my code so that I can manipulate the data or add some more fields to it.

If you want to access the join table (Posts_Tags) directly, or add properties to it, then you must define it as a separate PostTag domain class. You then split your many-many relationship between Post and Tag into 2 one-to-many relationships (one from Post to PostTag and one from Tag to PostTag).
Here's a comprehensive example of how to perform the mapping and add properties to the join table - in this example Membership is the join table.

Use the normal groovy sql API. For an example of how to get a groovy SQL object and execute sql queries see this

Related

Joins in Grails Criteria

I'm trying to write a criteria query in grails, for the following domains:
class Data {
Long createdById // this is user id
// other fields
}
class User {
Company company
// other fields
}
Now data, as the name suggest stores some data. It is not directly related to User but has a field createdById which is the id of User. (I cannot give direct reference of User in Data because these belongs to different projects, where Data is from a custom reusable plugin.)
Now I want to write criteria on Data and list all records where it was created by users of a given company. that is data.id == User.id and user.company == givenCompany
In order to use any GORM/Hibernate query method (criteria, where, or HQL) you need an association between the domain classes. Since you can't make an association from Data to User, you cannot use GORM to write your query with your domain classes are they are. In other words, you need to use SQL; HQL (DomainClass.executeQuery() will not work). But...
With HQL
If you're willing to modify User and maintain some redundant data, you can use HQL.
class User {
Company company
// other fields
/* Add uni-directional one-to-many */
static hasMany = [data: Data]
}
def data = AnyDomainClass.executeQuery('SELECT d FROM User as u INNER JOIN u.data AS d WHERE u.company = :company', [company: givenCompany])
The uni-directional one-to-many association creates a join table which would need to be maintained to reflect Data.createdById. The HQL join syntax hints at the fact that associations are required. If you must use a criteria query...
With criteria query
To use a criteria query you'll need to modify User and add an additional domain class. The reason is that a criteria query cannot project an association like HQL can. For example, this will not work:
def data = User.withCriteria {
eq('company', givenCompany)
projections {
property('data')
}
}
Due to not being able to modify Data, this --which is the simplest solution-- will also not work:
def data = Data.withCriteria {
user {
eq('company', givenCompany)
}
}
So, you need a middle-man in order to project Data instances:
class User {
Company company
// other fields
static hasMany = [userData: UserData]
}
class UserData {
Data data
static belongsTo = [user: User]
}
def data = User.withCriteria {
eq('company', givenCompany)
projections {
userData {
property('data')
}
}
}
As you can imagine, this method also requires some data maintenance.
Using SQL
Lastly, you have the option of using SQL and having the query return domain class instances. It boils down to something like this:
AnyDomainClass.withNewSession { session ->
def data = session.createSQLQuery('SQL GOES HERE').addEntity(Data).list()
}
More info
For more information, check out my following articles:
Domain class associations and how they work on the DB level
Joining domain classes with GORM queries
Using SQL and returning domain class instances. Available on Jan 22nd.
If you are able to create sql query for this and not able to create criteria for this then you can look for executeQuery
I hope it should work

GORM and Composite Keys in associations

I have legacy database and some tables have composite ids
class Client {
String id
static hasMany = [
settings: Setting
]
static mapping = {
id column: 'client_id', generator: 'assigned'
}
}
class Setting {
Client client
String nodeId
String ccyPairPattern
Character qualifier
static mapping = {
id composite: ['client', 'nodeId', 'pattern', 'qualifier']
}
}
I want to delete entry from GORM association:
client.get('1').removeFromSettings(settingToRemove)
// settingToRemove.delete(flush: true)
// delete-orphans does not help
This always raises exception after flush
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) :
This happens because method removeFrom* sets client property to null and generates query to delete with clientId = null as client is part of composite key
What is the best solution in this case. Looks like GORM has poor support for composite keys or my mapping is incorrect.
When you use hasMany without a belongsTo on the many side, in other words a unidirectional association, you get a join table. For example...
class PurchaseOrder {
static hasMany = [items: Item]
}
class Item { }
Would yield three database tables: purchase_order, item, and purchase_order_items. The purchase_order_items table would contain two columns: purchase_order_id and item_id. You can read more about join tables here.
Since you're dealing with a legacy database, I think the best solution is not to use addTo*() and removeFrom*().
Ensure you don't have a join table. If you have a join table, remove the hasMany association.
You'll need to add/remove Setting instances manually.
Example:
def client = Client.get(1)
// Adding a setting
def setting = new Setting(client: client, nodeId: blah...)
setting.save()
// Removing a setting
/*
The prototype is used to search for a domain instance with a composite primary key.
So simply set the composite key properties accordingly.
*/
def prototype = new Setting(client: client, nodeId: blah...)
def setting = Setting.get(prototype)
setting.delete()
Lacking a hasMany association, you won't be able to access a client's settings via the client.settings property. Instead you'd have to query for them like this:
def settings = Setting.findByClient(client)
A consequence of using a legacy database is that if the database doesn't align with that GORM/Hibernate expects it will be limited in what it can do for you.

Creating one-to-many & many-to-many for same domain class in grails

I want to create a domain class as like , One user can post many orders [Bidirectional] and one order can be liked by many users [unidirectional].
I have written a domain class as shown below ,
Class User {
String userName;
List orders
static hasMany = [Order]
}
Class Order {
String orderId
String orderName
//Indicates this order belongs to only one user
static belongsTo =[owner : User ] // Bidirectional
//Indicates order can be liked by many users
static hasMany = [likedUser : User] //Unidirectional
}
But I am getting am error saying invalid schema . Any body please help...
This post looks similar to my question but I am not getting , Please help.
First, order is a reserved word in SQL. Since GORM by default creates a table with the same name as your class, you'll need to either rename your class or provide a different name to use when mapping to SQL tables.
For example:
class Order {
static mapping = {
table 'user_order'
}
// ...
}
Another problem is that Order contains two associations to User. You need to tell GORM which one of these that is the bi-directional association from User to Order. That can be achieved using mappedBy, like this:
class User {
String userName
static hasMany = [orders: Order]
static mappedBy = [orders: 'owner']
}
Hope this helps.

GORM/Grails - add extra column to a joinTable expression

I have a Domain Class setup similar to this
class NewsStory {
String headline
static hasMany = [channels:Channel]
static mapping = {
table 'NewsStory'
addresses joinTable:[name:'Article_Channel', key:'ArticleId', column:'ChannelId']
}
}
in the Article_Channel table i need to have an extra column populated called ArticleType say. Its value will always be the same e.g. 'news' for this domain class, but will be differnt for others e.g. 'blog'
Channel is just something like 'Security' etc
Is there a way?
Thanks
One option would be be to create your own many-to-many mapping class and add the field in there.
http://grails.org/Many-to-Many+Mapping+without+Hibernate+XML
So, for example:
class ArticleChannel {
NewsStory newsStory
Channel channel
String articleType
}
Then, your NewsStory and Channel classes would hasMany the ArticleChannel class.

Grails domain class relationship to itself

I need a way to be able to have a domain class to have many of itself. In other words, there is a parent and child relationship. The table I'm working on has data and then a column called "parent_id". If any item has the parent_id set, it is a child of that element.
Is there any way in Grails to tell hasMany which field to look at for a reference?
This is an example of what you are looking for (it's a snippet code I am running and it generates column parent_id). I don't think you need SortedSet:
class NavMenu implements Comparable {
String category
int rank = 0
String title
Boolean active = false
//NavMenu parent
SortedSet subItems
static hasMany = [subItems: NavMenu]
static belongsTo = [parent: NavMenu]
}
Furthermore, you can give name to the hasMany clause using the Mapping DSL, which is explained at http://grails.org/GORM+-+Mapping+DSL

Resources