Imagine the following domain classes:
class User {
static hasMany = [ roles: Role ]
List<Role> roles
User() {
roles = new ObservableList<>()
roles.addPropertyChangeListener({ event -> ... } as PropertyChangeListener)
}
}
class Role {
String name
}
My intention is to perform some actions each time an element is added to / removed from the roles collection; this includes 1) populating roles from database, and 2) further manipulation by a user. If a User instance is just created (and not persisted), I am able to catch notifications. After the call to save() method, the ObservableList is still intact. But later, for example, when I perform a User.find...() from a controller, the original ObservableList is replaced by Hibernate's org.hibernate.collection.PersistentList, and obviously there are no notifications anymore.
How can I implement a change-aware list that would be compatible with GORM?
Related
I have an Account class that has many managers (User class) & reps (User class).
class Account {
static hasMany = { reps: User, managers: User }
}
Then, I have a User class which belongs to an account. User is differentiated into manager or rep using a Role Enum inside User class.
class User {
static belongsTo = { account: Account }
Role role
}
The problem is, when I create a user of any type and save it, Grails ends up adding that user to both managers and reps sets in the account object.
I realize I need to use mapped_by here, however I don't understand how it should be used. The manager and rep is differentiated by a Role Enum inside User class.
I have looked at several stackoverflow questions #1, #2 however most of the times, problems get solved with other relationships.
I specifically want to use 2 one-to-many relationships between the Account and User class.
Edit : Code to initialize a rep:
def addRep(manager) {
User rep = new User( account: manager.account,
role: Role.REP)
rep.save(flush: true, failOnError: true)
}
You need to specify which association is to be used :
def addRep(manager) {
User rep = new User(role: Role.REP)
manager.account.addToReps(rep) // This will do the bi-association
rep.save(flush: true, failOnError: true)
}
I'm new to Grails, some things are still vague for me.
I want to create a Twitter-like app. As a user has both a followers and a following groups, I've created a GroupMyTwitter domain.
What I don't understand, is the logic behind this. My GroupMyTwitter domain is like this :
class GroupMyTwitter
{
String name;
static constraints = { name blank : false; }
static hasMany = [users:UserMyTwitter];
static belongsTo = [owner:UserMyTwitter];
}
Then every time I create a user I also create two groups associated to him :
GroupMyTwitter followers = new GroupMyTwitter (
name:"followers",
owner: user).save(flush: true, failOnError: true)
// And same for followings
Now I simply wonder... how can I access these groups to get the followers/ings count, add or delete some?
Thanks for your help.
Based on the hasMany and belongsTo variables gorm automatically creates some instance fields for you:
static hasMany = [users:UserMyTwitter];
This will create a field users of type Set inside GroupMyTwitter.
You can access this Set like any other field:
followers.users.add(userA) // add a user
followers.users.remove(userB) // remove a user
followers.users.size() // number of users
// don't forget to save the group after you modfied the `users` collection:
followers.save(failOnError: true)
Depending on your needs it might be useful to use a List instead of a Set:
class GroupMyTwitter {
List users
static hasMany = [users:UserMyTwitter];
...
}
This causes Gorm to use a List for the users collection instead of a Set.
A List has a defined order and allows you to access certain elements by index:
UserMyTwitter user = followers.users[4] // get the 5th user
I'm writing a Grails application and I need to retrieve a persistent value for the collection of my domain class objects. Let's consider we have got the following domain object:
class UserGroup {
SortedSet<User> users
static hasMany = [ users: User ]
// ...
def beforeUpdate() {
println "BEFORE UPDATE: " + this.getPersistentValue('users');
}
}
class User implements Comparable<User> {
String name
}
And the controller which has the following action:
class UserGroupController {
def addUser() {
UserGroup group = UserGroup.get(params.long('gid'))
User user = User.get(params.long('uid'))
group.addToUsers(user)
group.save(failOnError:true, flush:true)
}
}
The problem is that when beforeUpdate() is called, the users collection already contains the recently added user. So, seems that addTo() method doesn't trigger the beforeUpdate() event.
The same problem occurs when we're talking about isDirty() method. As the changes are applied before the beforeUpdate() is called, the collection is not recognized as dirty field.
Does anyone know how to change this? I'm writing a feature which tracks the history of changes for lots of different object, so I need to have access to the previous value in order to understand whether its value was changed or not.
I have had a similar issue, where things are being updated when I wasn't expecting them when I used the .get() on domain classes. I like to use .read() now because it wont update the database when I'm not expecting it to. Grails does a lot of sneaky things behind the sense which are helpful I think but can be a bit confusing.
I have two domain objects:
Customer
CustomerConfig
Customer has a 1-1 association with CustomerConfig. There is a default CustomerConfig with default settings for Customers who do not have an explicitly saved CustomerConfig e.g.
def getConfig() {
if (!config) {
return new CustomerConfig() //the default settings
} else {
return config
}
}
The problem I am having is that when I return the default setting GORM saves the CustomerConfig instance to the database as it appears to GORM that it has changed.
In fact I do not want to save it to the database as I want to be able to control the default settings for customer and make updates for customers until they have an explicitly saved config.
I also am trying avoid using conditional logic as follows:
def config = customer.config?:new CustomerConfig()
And encapsulate it in the Customer domain object. It seems like there's a different pattern I should be following. Would welcome any advice.
Thanks,
cowper
IMHO, it's never a good idea to change behavior of default getter/setter as those are managed by GORM.
You can do something like this
class Customer {
static transients = ['setting']
public CustomerConfig getSetting(){
return getConfig()?:new CustomerConfig()
}
I need some help on adding roles to user in a many-many situation.
So I have User with many Role and Role has many User.
I figured my current Update() method in my repository wont work. How can I build a repository method that allows me to remove all previous roles and add new roles to user?
This is what I currently have:
public User UpdateUser(User user, IEnumerable<Expression<Func<User, object>>> properties)
{
if (string.IsNullOrEmpty(user.UserId))
{
throw new InvalidOperationException("user does not exist");
}
else
{
db.Users.Attach(user);
foreach (var selector in properties)
{
string propertyName = Helpers.PropertyToString(selector.Body);
db.Entry(user).Property(propertyName).IsModified = true;
}
}
db.SaveChanges();
return user;
}
Is this the right way to update a user? I'm assuming everything the detached. This is how I'm calling this to add roles to user:
User user = new User();
user.UserId = userId;
user.Roles = new Domain.Role{ RoleId = 1}; //Assuming there is a role with ID = 1
userRepo.UpdateUser(user, new List<Expression<Func<User, object>>>
{
u => u.Roles
});
If you are working in a detached scenario you might want to look into the object state manager. See these two answers for more info.
Entity Framework Code First - Add Child Entity to Parent by Primary Key
Save a relation with between two entities an N-N association
The first one is a simple example where a single child is added without roundtriping the db. The second one is more complex but I still haven't found a good way to clear the relationship without telling it which childs to delete.
There is a possibility I haven't looked at yet and it is to use the RelationshipManager which you can get from the ObjectStateManager. It contains a few methods to get releated collections so maybe you could use that somehow.