How can I set a unique constraint per class instance? - grails

Assume the user class:
class User {
String name
hasMany = [books: Book]
}
and the book class:
class Book {
String name
belongsTo = [user: User]
}
I want that the name of a book is unique per user. I.e., user1 can have books with name: [bookname1, bookname2] but he cannot have two books with the same name: [bookname1, bookname2]
User2 can also have books with name: [bookname1, bookname2] but not two books with the same name.
How can I restrict that the booknames are unique for each user?

Read the docs:
http://grails.org/doc/latest/ref/Constraints/unique.html
class Book {
String name
belongsTo = [user: User]
static constraints = {
name unique: 'user'
}
}

Related

grails grom create criteria with many-to-many mapping

I have two domain classes: User and Book.
class Book implements Serializable{
String bookName
Timestamp createdDateTime
Blob file
static belongsTo = [User]
static hasMany = [user :User]
}
I am able to add user in book using addToUser() method.
But I am stuck in create criteria while applying filter in user.
def query = Book.createCriteria();
def results = query.list () {
eq("user",userObject) // not working since user field is a list of users.
order("createdDateTime", "desc")
}
Please help me with the correct way of filtering.
You need to join the user table first in a many-to-many relation. The criteria should look like:
Book.withCriteria {
user {
eq("id", userObject.id)
}
order("createdDateTime", "desc")
}
I'm not 100% sure how you're trying to model your domain but maybe you want a Book to have a single user? In which case you'd have the belongsTo relationship on Book e.g.
class Book {
String bookName
Timestamp createdDateTime
Blob file
static belongsTo = [user: User]
}
Then have the hasMany relationship on User e.g.
class User {
String name
static hasMany = [books: Book]
}
Then you can look Books up with criteria like:
def user = User.findByName( 'bob' )
def results = Book.createCriteria().list () {
eq( "user", user )
order( "createdDateTime", "desc" )
}

Grails GORM: Change foreign key column name

I'm trying to change the foreign key column name that is used in Visitor table for User's id. The column is named now user_id, I want to change that to who_id.
Minimal User Domain Class:
class User {
static hasMany = [
visitor: Visitor
]
String uid
...
}
Minimal Visitor Domain Class:
class Visitor {
static belongsTo = [user: User]
....
}
Question:
I've tried with mappedBy but with no success, is there another way to use a property from User as a foreign key in Visitor?
I think you want to use the mapping static block:
class Visitor {
static belongsTo = [user: User]
static mapping = { user column: 'who_id' }
}
You can mark the uid in User as the id (primary key). That will make it automatically the foreign key in the Visitors domain.
class User {
String uid
static mapping = {
id column: 'uid'
}
...

Grails inheritance and conflicting one-to-many and many-to-many relationship

I'm learning grails by trying to create a simple twitter copy. I'm currently trying to incorporate followers and groups. I originally came up with a very basic database structure, and I've had no luck in implementing it. The design for relationships is as follows:
Person:
has many: Groups, Tweets, (Person as followers through User2Person)
Group:
has many: (Person as followers through User2Person)
belongs to: Person as owner
User2Person:
belongs to: (Person or Group)
belongs to: Person
Basically, I want Person and Group to be an instance of User, and then create a table that maps User to Person. This way, only one table is created/used for the relationship between Group2Person and Person2Person.
More information: A Group is created by a Person and so it should have an "owner" (person_id). It also has many followers (i.e. members). Group cannot follow other groups, but a Person can follow either another Person or a Group.
Below is how I implemented this in grails:
User
abstract class User {
static hasMany = [followers: Person]
static mappedBy = [followers: "followed"]
String name
Date dateCreated
Date lastUpdated
static constraints = {
name shared: "mustFill", size: 3..20
}
}
Person
class Person extends User {
static belongsTo = [followed: User]
static hasMany = [tweets: Tweet, groups: Group]
static mappedBy = [groups: "owner"]
String username
String email
static constraints = {
username shared: "mustFill", unique: true, size: 4..15
email shared: "mustFill", email: true
}
static mapping = {
tweets sort: 'dateCreated', order: 'desc'
}
}
Group
class Group extends User {
Person owner
String description
def getTweets() {
return followers.tweets.flatten()
}
static transients = {
tweets
}
}
Tweet (Just in case?)
class Tweet {
static belongsTo = [author: Person]
String text
Date dateCreated
static constraints = {
text shared: "mustFill", maxSize: 140
}
}
When I run the cmd grails schema-export, I get the following error: "| Error Error loading plugin manager: Domain classes [class tweeter.Group] and [class tweeter.Person] cannot own each other in a many-to-many relationship. Both contain belongsTo definitions that reference each other. (Use --stacktrace to see the full trace)"
I was able to get the database to create almost the correct schema. Unfortunately, the join table's primary key for User2Person (a.k.a. followers) used (user_id, person_id). That meant that I could not have two records such as: (1, 2) and (2, 1) (e.g. two users are following each other). Below is the updated classes (commit):
User
class User {
static belongsTo = Person
static hasMany = [followers: Person]
String name
Date dateCreated
Date lastUpdated
static constraints = {
name shared: "mustFill", size: 3..20
}
}
Person
class Person extends User {
static hasMany = [tweets: Tweet, groups: Group, follows: User]
static mappedBy = [tweets: "author", groups: "owner"]
String username
String email
static constraints = {
username shared: "mustFill", unique: true, size: 4..15
email shared: "mustFill", email: true
}
static mapping = {
tweets sort: 'dateCreated', order: 'desc'
}
}
The follower table in the schema looked like:
create table user_follows (
user_id bigint,
follows__id bigint,
primary_key(user_id, follows__id)
)
I scoured the web for information about changing the primary key for a join table. The best I could find was about using code like:
static mappedBy = { followers joinTable: [name:"someName", ...] }
Unfortunately, I had a hard time finding good documentation on the joinTable mapping, and most sources seemed to indicate that it was not possible to change the primary key of join tables easily. I then decided to use a separate domain class to define the join table following this guide: Many-to-Many Mapping without Hibernate XML. Below is the final updated code (commit):
User
class User {
static belongsTo = Person
static hasMany = [people: UserFollower]
static mappedBy = [people: "followed"]
String name
Date dateCreated
Date lastUpdated
static constraints = {
name shared: "mustFill", size: 3..20
}
static transients = {
followers
}
def getFollowers() {
return people.collect { it.follower }
}
void addToFollowers(Person person) {
UserFollower.link(this, person)
}
void removeFromFollowers(Person person) {
UserFollower.unlink(this, person)
}
}
Person
class Person extends User {
static hasMany = [tweets: Tweet, groups: Group, users: UserFollower]
static mappedBy = [tweets: "author", groups: "owner", users:"follower"]
String username
String email
static constraints = {
username shared: "mustFill", unique: true, size: 4..15
email shared: "mustFill", email: true
}
static mapping = {
tweets sort: 'dateCreated', order: 'desc'
}
static transients = {
follows
}
def getFollows() {
return users.collect { it.followed }
}
void addToFollows(User user) {
UserFollower.link(user, this)
}
void removeFromFollows(User user) {
UserFollower.unlink(user, this)
}
}
UserFollower
class UserFollower {
User followed
Person follower
static constraints = {
followed nullable: false
follower nullable: false
}
static void link(User user, Person person) {
UserFollower f = UserFollower.findByFollowedAndFollower(user, person)
if(!f) {
f = new UserFollower()
user.addToPeople(f)
person.addToUsers(f)
f.save()
}
}
static void unlink(User user, Person person) {
UserFollower f = UserFollower.findByFollowedAndFollower(user, person)
if(f) {
f = new UserFollower()
user.removeFromPeople(f)
person.removeFromUsers(f)
f.delete()
}
}
}

inheritance in Grails domain model

My Grails app's domain model has the following requirements:
a user belong to zero or one organisations
an organisation is either a charity or a company
charities and companies have some some common fields and also some (non-nullable) fields that are unique to each organisation type
I put the common organisation fields into an abstract Organisation class which Charity and Company both extend. I can't store this hierarchy in a single table because there are non-nullable fields that are specific to each organisation type. The relevant parts of the domain model are shown below:
class User {
String name
static belongsTo = [organization: Organization]
static constraints = {
organization nullable: true
}
}
abstract class Organization {
String name
static hasMany = [users: User]
static mapping = {
tablePerHierarchy false
}
}
class Charity extends Organization {
// charity-specific fields go here
}
class Company extends Organization {
// company-specific fields go here
}
When I look at the MySQL schema generated from this model, the inheritance relationship between organisation-company and organisation-charity seems to have been completely ignored. Although there is an organisation table with a name column, it has no primary-foreign key relationship with either company or charity
I see the same result as IanRoberts for both MySQL and H2. In other words: no join table generated, but the expected organization_id FK in the users table.
With "Table per subclass" mapping (tablePerHierarchy false), you end up with an implied one-to-one relationship in the database. Primary Keys for Charity and Company will have the same value as the PK for the parent Organization. The schema generated by GORM/Hibernate3 doesn't appear to enforce this with referential integrity constraints. It's pure Hibernate magic. A bit more detail here
Solved!
Add the class below to src/java (this class cannot be written in Groovy)
package org.example;
import org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsAnnotationConfiguration;
import org.hibernate.MappingException;
import org.hibernate.mapping.JoinedSubclass;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.RootClass;
import java.util.Iterator;
public class TablePerSubclassConfiguration extends GrailsAnnotationConfiguration {
private static final long serialVersionUID = 1;
private boolean alreadyProcessed = false;
#Override
protected void secondPassCompile() throws MappingException {
super.secondPassCompile();
if (alreadyProcessed) {
return;
}
for (PersistentClass persistentClass : classes.values()) {
if (persistentClass instanceof RootClass) {
RootClass rootClass = (RootClass) persistentClass;
if (rootClass.hasSubclasses()) {
Iterator subclasses = rootClass.getSubclassIterator();
while (subclasses.hasNext()) {
Object subclass = subclasses.next();
// This test ensures that foreign keys will only be created for subclasses that are
// mapped using "table per subclass"
if (subclass instanceof JoinedSubclass) {
JoinedSubclass joinedSubclass = (JoinedSubclass) subclass;
joinedSubclass.createForeignKey();
}
}
}
}
}
alreadyProcessed = true;
}
}
Then in DataSource.groovy set this as the configuration class
dataSource {
configClass = 'org.example.TablePerSubclassConfiguration'
pooled = true
driverClassName = "org.h2.Driver"
username = "sa"
password = ""
dbCreate = "update"
url = "jdbc:h2:mem:testDb;MVCC=TRUE;LOCK_TIMEOUT=10000"
}
I've submitted a pull request to Grails that fixes this. The fix was be included in Grails 2.3.9.
ORM is not RDBS.
tablePerHierarchy false
so You Have three tables: Organization, Charity, Company. User belongs to only Organization (not Charity or Company). How are you going to get value of specific fields?
There is USER. We know ORGANIZATION, but we don't know Charity or Company. I think you underst...
I can suggest you three solutions:
1. tablePerHierarchy true (But you need to have nullable charity\Company -specific fields )
2.
class User {
static belongsTo = [charity: Charity, company: Company]
}
class Charity {
String name
static hasMany = [users: User]
// charity-specific fields go here
}
class Company {
String name
static hasMany = [users: User]
// company-specific fields go here
}
3.
class User {
static belongsTo = [organization: Organization]
}
class Organization {
String name
Charity charity //nullable
Company company //nullable
static hasMany = [users: User]
}
class Charity {
static belongsTo = [organization: Organization]
// charity-specific fields go here
}
class Company {
static belongsTo = [organization: Organization]
// company-specific fields go here
}

How to model a Friend - Friendship relationship in Grails

How would you model a friend - friendship relationship in Grails? Until now my User class had many followers
class User {
//Searchable plugin
static searchable = true
String userId
String password
boolean enabled = true
// For Spring Security plugin's user registration.
String email
String userRealName
boolean emailShow
Date dateCreated
Profile profile
static hasMany = [
posts : Post,
tags : Tag,
following : User,
authorities : Role,
notifications: Notification,
locations: Location,
incomingLocations:IncomingLocation,
]
static belongsTo = Role
static constraints = {
userId(blank: false, size:3..20, unique: true)
password(blank: false)
dateCreated()
profile(nullable: true)
userRealName(nullable: true, blank: true)
email(nullable: true, blank: true)
}
static mapping = {
profile lazy:false
}
}
But I would like to change the following:User for something like friendships:Friendship and create a Friendship class as following:
class Friendship {
static belongsTo= [user:User]
User friend2
boolean areFriends
}
Is this an ideal implementation?
How would you implement the handshake (accept/reject a pending friendship)?
You might not need to model Friendship directly. You can just have a hasMany relationship that relates the Users as friends. You don't create that relationship until someone accepts a FriendRequest. If they no longer want to be friends, then just remove the relationship between the 2 Users.
class User {
static hasMany = [friends:User]
}
class FriendRequest {
User fromUser
User toUser
}
That way Friendship doesn't have to do 2 things (relate users and track statuses). And friends becomes a natural object relationship which can make some things like optimizing fetching a bit easier.

Resources