Hello I have two domain classes as following
class Users {
String password
String firstName
String lastName
String emailAddress
String username
Company company
.....
static hasMany = [projects:Projects];
}
Another class
class Projects {
String projectName
String description
Users projectLead
Date dateCreated
Date lastUpdated
static belongsTo = Users
}
These classes obviously has one to many relationship but now I want to change it to many to many relationship by adding "ProjectMembership" class but the problem I have is that my application has already gone into production and there are people who are already using the app. In such a case they already have one user->many projects in the the db. In such a case how can I migrate this existing data and change my prod app to have m2m relationship which will looks like following.
class Users {
String password
String firstName
String lastName
String emailAddress
String username
Company company
.....
static hasMany = [projectMemberships:ProjectMemberships];
}
Another class
class Projects {
String projectName
String description
Users projectLead
Date dateCreated
Date lastUpdated
static hasMany = [projectMemberships:ProjectMemberships];
}
and
class ProjectMemberships{
Users u
Projects p
}
This is best done with a migration tool like Liquibase, and the http://grails.org/plugin/database-migration plugin is probably your best be in Grails since it uses Liquibase and is tightly integrated with GORM. But this one's easy enough to do by hand.
I wouldn't use hasMany since you can easily manage everything from the ProjectMemberships class, so your Users and Projects classes would be
class Users {
String password
String firstName
String lastName
String emailAddress
String username
Company company
.....
}
and
class Projects {
String projectName
String description
Date dateCreated
Date lastUpdated
}
I'd go with a ProjectMemberships class that uses a composite key, which requires that it implement Serializable and have a good hashCode and equals:
import org.apache.commons.lang.builder.HashCodeBuilder
class ProjectMemberships implements Serializable {
Users u
Projects p
boolean equals(other) {
if (!(other instanceof ProjectMemberships)) {
return false
}
other.u?.id == u?.id && other.p?.id == p?.id
}
int hashCode() {
def builder = new HashCodeBuilder()
if (u) builder.append(u.id)
if (p) builder.append(p.id)
builder.toHashCode()
}
static ProjectMemberships get(long userId, long projectId) {
find 'from ProjectMemberships where u.id=:userId and p.id=:projectId',
[userId: userId, projectId: projectId]
}
static ProjectMemberships create(Users u, Projects p, boolean flush = false) {
new ProjectMemberships(u: u, p: p).save(flush: flush, insert: true)
}
static boolean remove(Users u, Projects p, boolean flush = false) {
ProjectMemberships instance = ProjectMemberships.findByUsersAndProjects(u, p)
if (!instance) {
return false
}
instance.delete(flush: flush)
true
}
static void removeAll(Users u) {
executeUpdate 'DELETE FROM ProjectMemberships WHERE u=:u', [u: u]
}
static void removeAll(Projects p) {
executeUpdate 'DELETE FROM ProjectMemberships WHERE p=:p', [p: p]
}
static mapping = {
id composite: ['p', 'u']
version false
}
}
Use ProjectMemberships.create() to add a relationship between a user and a project, and ProjectMemberships.remove() to remove it.
Run grails schema-export to see the updated DDL (it'll be in target/ddl.sql). Run the create table statement for the project_memberships table, e.g.
create table project_memberships (
p_id bigint not null,
u_id bigint not null,
primary key (p_id, u_id)
)
Then populate it with this SQL (depending on your database you might need a slightly different syntax):
insert into project_memberships(p_id, u_id) select id, project_lead_id from projects
and finally drop the project_lead_id column from the projects table.
Of course do a database backup before making any changes.
You can get a user's projects with
def projects = ProjectMemberships.findAllByUsers(user)*.p
and similarly a project's users with
def users = ProjectMemberships.findAllByProjects(project)*.u
Related
I need to have two domains which are extends from one domain. However when i create the domains subdomains' tables are not created.
class BaseEntity {
Date created
Boolean isDeleted
Date modified
Integer version
static mapping = {
tablePerHierarchy false
version false
table name: "base_entity", schema: "user"
isDeleted defaultValue: Boolean.FALSE
}
}
First domain extends from BaseEntity
class UserEx extends BaseEntity {
String firstName
String lastName
String password
String userName
static mapping = {
version false
table name: "user_ex", schema: "user"
}
}
This is the second domain
class UserType extends BaseEntity {
Integer name
static belongsTo = [parent: UserType]
static hasMany = [child:UserType]
static mapping = {
version false
table name: "user_type", schema: "user"
}
}
In the db the only created table is BaseEntity. If i remove tablePerHierarchy property from BaseEntity domain, created table(which is BaseEntity) contains all fields of userEx and userType - this is not we like to have-
To sum up I want to have those three tables in my db without losing extends relationship.
Thanks in advance
I have two domain classes (db tables) in my Grails project:
class Doctor {
String role;
String name;
String surname;
String address;
...
#NotNull
static belongsTo = [secUser:SecUser]
....
}
class SecUser {
transient springSecurityService
String username
String password
...
}
I would like to find all SecUser that have not a correspondent Doctor.
How can I do it?
I think you need something like this:
SecUser.executeQuery(" FROM SecUser secUser WHERE NOT EXISTS (SELECT doctor.SecUser FROM Doctor doctor")
How to access firstname and lastname of user depending on which statusItem is displayed which is the child of the user.
class UserAccount implements Serializable {
static transients = ['pass','passConfirm','familyPicTmp', 'familyPicTmpFilename', 'photoTmp', 'photoTmpFilename']
static hasMany = [authorities: Authorisation, memberships:FamilyMembership, progenitorIwi:Family, politicItems:PoliticItem,
relationships:Relationship, , agents:UserAccount, media:UserMedia, status:Status]
static mappedBy = [ progenitorIwi:"progenitor",relationships:'relationTo', relationships:'userAccount']
static fetchMode = [memberships:FetchMode.JOIN, agents:FetchMode.JOIN]
static mapping = {
memberships(lazy:false)
agents(lazy:false)
}
static belongsTo = [Authorisation]
STATUS DOMAIN
class Status {
static belongsTo = [userAccount:UserAccount]
def String statusMessage
Date dateCreated
Date lastUpdated
def String statusType
POLITIC DOMAIN
class PoliticItem {
SystemEntity politicItemName
UserAccount userAccount
def String politicItemValue
def boolean shared = false
Date dateCreated
Date lastUpdated
How can we load all the users that belong to all the status on to the politic's views?
I am still not sure which part you dont know. Assuming userAccount has the firstName and lastName field to access them from status try this:
status is the instance of your Status class. The one you need to get the userAccount off of it.
status.userAccount.firstName
or
status.userAccount.lastName
I am working on an application that uses EF 4.2 and database-first development, using the standard T4 template to generate a DbContext and POCOs. The T4 templates generate entities something like this:
public class Address
{
public int AddressId { get;set; }
public string Address1 { get;set; }
public string City { get;set; }
}
public class Account
{
public int AccountId { get;set; }
public string Name { get;set; }
public int AddressId { get;set; }
public Address BillingAddress { get;set; }
}
When I create a billing address for an existing account, my code is something like this:
public void Save(Account updated)
{
var existing = DbContext.Find(updated.AccountId);
MyContext.Entry(existing).CurrentValues.SetEntry(updated);
existing.Address = updated.Address;
MyContext.SaveChanges();
}
Watching SQL Server Profiler, I can see the Address entry being inserted into the database, but unfortunately, it is occurring after the Account entry is updated, so the address is detached from its parent account, and when I next load the account, the billing address is empty again.
A workaround is to add the following code after the call to SaveChanges():
if (existing.AddressId == null && existing.Address != null)
{
existing.AddressId = existing.Address.AddressId;
MyContext.SaveChanges();
}
which, while it may work, requires a second SQL UPDATE to the database, and as the entity grows and adds more associations, requires more and more hacks. Is there something obvious that I'm missing?
** UPDATE **
Following Ladislav's answer below, I added a call to the following method in the WriteNavigationProperty to my T4 template:
void WriteKeyAttribute(CodeGenerationTools code, NavigationProperty navigationProperty, MetadataTools ef)
{
var dependentProperties = navigationProperty.GetDependentProperties();
if (dependentProperties.Any())
{
var keys = new List<string>();
foreach (var key in dependentProperties)
{
keys.Add(String.Format("\"{0}\"", key.Name));
}
#>
[ForeignKey(<#= String.Join(", ", keys) #>)]
<#+
}
}
Hope that helps!
It sounds like your BillingAddress is incorrectly mapped because AddressId is not handled as the FK of the relation.
Try to add this attribute to your navigation property:
[ForeignKey("AddressId")]
public Address BillingAddress { get;set; }
If you are using EDMX with database first make sure that the there is correctly configured relation between those classes. EF uses this information in its store mapping and store mapping defines sequence of the operations. If you don't have correctly configured relation entities are processed in alphabetical order of their type names => Account is processed prior to Address.
Btw. are you sure that your Account is not duplicated during your SaveChanges call?
I have 2 domain classes which are mapped by many-to-many relationship. I followed the instruction of Grails documentation, but I still have some problem when processing data on those domains. Here are my 2 domain classes:
class User {
String name
int age
String job
static hasMany = [groups : Group]
static belongsTo = [org : Organization]
}
class Group {
String groupName
String code
static hasMany = [members : User]
}
My problems are:
1. The above relationship require one class hold belongsTo to be the "owner" of the relationship. In this context, the User belongs to the Group, but I do not know how to put the belongsTo to User class, because the standard syntax that Grails suggest is static belongsTo = [Group] (just specify the owner class name), so I cannot:
- put it into the exist belongsTo like this: static belongsTo = [org : Organization, Group]
- or define another belongsTo like this: static belongsTo = [Group]
Is below example right:
class Book {
String title
static belongsTo = Author
static hasMany = [authors:Author]
static mapping = {
authors joinTable:[name:"mm_author_books", key:'mm_book_id' ]
}
}
class Author {
String name
static hasMany = [books:Book]
static mapping = {
books joinTable:[name:"mm_author_books", key:'mm_author_id']
}
}
(Ref link: Many-to-Many link tables in grails (GORM) / hibernate)
I mean that do we need to specify the name of foreign key of the join table for each class?
If I want to find all User that are members of a specified Group whose name is "ABC", how can I use the DynamicFinder of Grails?
Thank you so much
It is very rare that m2m relationships have an owning side, so I've always found it odd to have to specify one for GORM to work correctly. Because of this, I don't do it this way. I create the join table as a domain. Then things get really simple.
class UserGroup implements Serializable {
User user
Group group
boolean equals(other) {
if (!(other instanceof UserGroup)) {
return false
}
other.user?.id == user?.id &&
other.group?.id == group?.id
}
int hashCode() {
def builder = new HashCodeBuilder()
if (user) builder.append(user.id)
if (group) builder.append(group.id)
builder.toHashCode()
}
static UserGroup get(long userId, long groupId) {
find 'from UserGroup where user.id=:userId and group.id=:groupId',
[userId: userId, groupId: groupId]
}
static UserGroup create(User user, Group group, boolean flush = false) {
new UserGroup(user: user, group: group).save(flush: flush, insert: true)
}
static boolean remove(User user, Group group, boolean flush = false) {
UserGroup instance = UserGroup.findByUserAndGroup(user, group)
instance ? instance.delete(flush: flush) : false
}
static void removeAll(User user) {
executeUpdate 'DELETE FROM UserGroup WHERE user=:user', [user: user]
}
static void removeAll(Group group) {
executeUpdate 'DELETE FROM UserGroup WHERE group=:group', [group: group]
}
static mapping = {
id composite: ['group', 'user']
version false
}
}
Then you just need to create the getters in your User and Group class. You won't have User user or Group group in either class. There is no need to map them with hasMany/belongsTo because all that will do is create the join table, which you've done by creating the UserGroup domain.
class User {
Set<Group> getGroups() {
UserGroup.findAllByUser(this).collect { it.group } as Set
}
}
class Group {
Set<User> getUsers() {
UserGroup.findAllByGroup(this).collect { it.user } as Set
}
}
Once you have these in place you can use the methods you created in the UserGroup domain and/or you can use finders on it...
def userGroupInstance = UserGroup.findByUserAndGroup(userInstance, groupInstance)
def userGroups = UserGroup.findAllByUser(userInstance)
def userGroupInstance = UserGroup.get(userId, groupId)
You get the idea. This presentation by Burt Beckwith sheds more light on why this is a good approach along with some other great tips for performance increases.
belongsTo will create another M:1 relationship (field) - you tell if there is a "main group" for a User in your case. If I wished only to enforce at least one group, I'd go with a custom validator for User.groups checking it's not empty.
(I'm not sure here) - I believe yes, if the key name differs from default Hibernat/GORM "userId"/"groupId".
findBy*() methods won't work here, you need a CriteriaBuilder, like here.