I am a newbie to Grails and am having a lots of problems in a many to many relationship especially when the mappedBy comes into picture. Here is the exact problem that I am facing.
I have two domain classes which have a many to many relationship.
class Address {
Long id
String addName
static hasMany = [policy: Policy]
static belongsTo=[Policy]
Date lastUpdated
Date dateCreated
}
and
class Policy {
int id
int policyId
Date lastUpdated
Date dateCreated
static hasMany = [addressSource:Address,addressDestination: AddressSet]
}
Now this creates 4 tables, namely : address, policy, policy_src_add and policy_dest_add
The problem that I am facing is with the 'show' view (The views are the standard ones generated by Grails by the generate-views command).
In the show view of Address I can see the Policies that are referenced by policy_dest_add but not the ones that are referenced by policy_src_add.
For ex : Let us assume there is a policy:"PK" which has addressSource:"AS"(lets say id=1) and a addressDestination :"AD"(lets say : id=2)
When i go to the show view of "AD" (which is /address/show/2 ) I can see the Policy "PK" but when i go to the show view of "AS" (which is /address/show/1 ) I can NOT see the Policy "PK" in it.
Can someone please help me.Is this a view generation problem in case of many to many relationships in grails? Or is it just something I am missing ?I have tried using mappedBy in the Address as follows but to no avail :
static mappedBy = [policy:"addressSource", policy:"addressDestination"]
Thanks a ton,
Manas Shukla
Related
I have two tables:
dbo.Dashboards
Id (int PK) Title(nvarchar) WidgetIds(nvarchar)
1 Test [1,2]
dbo.Widgets
Id (int PK) Details(nvarchar)
1 {'text': 'some data'}
2 {'text': 'test'}
Expected output:
Dashboard.Id Dashboard.Title Widget.Id Widget.Details
1 Test 1 {'text': 'some data'}
1 Test 2 {'text': 'test'}
I would like to get dashboards with assigned widgets by using Entity Framework.
My first solution is to get dbo.Dashboards and then dbo.Widgets. After that I can merge it in a backend, but it is not the best practice.
Is there any option to get Dashboards with assigned Widget list?
Function Include() is not working because there isn't FK relationship between tables.
It seems to me that you have a many-to-many relationship between Dashboards and Widgets: Every Dashboard has zero or more Widgets and every Widget is used by zero or more Dashboards.
In a proper database you would have a separate junction table. Apparently you chose not to use this pattern, but create a string that contains a textual representation of the widgets that a 'Dashboard` has.
If you plan to create a serious application I strongly advise you to
use the standard pattern in many-to-many relationships
If you don't, all your queries will be more difficult. Imagine the problems you'll experience if you want to delete a Widget. You'd have to check the textual representation of every Dashboard to check if the widget that you want to remove is used somewhere and change it.
If you want to configure your many-to-many relations ship according to the Entity Framework Code-First Conventions, you will have something like this:
class Dashboard
{
public int Id {get; set;}
public string Title {get; set;}
// every Dashboard has zero or more Widgets
public virtual ICollection<Widget> Widgets {get; set;}
... // other properties
}
class Widget
{
public int Id {get; set;}
// every Widget is used in zero or more Dashboards
public virtual ICollection<Dashboard> Dashboards{get; set;}
... // other widget properties
}
class MyDbContext : DbContext
{
public DbSet<Dashboard> Dashboards {get; set;}
public DbSet<Widget> Widgets {get; set;}
}
Because you stuck to the conventions, this is all that entity framework needs to know to understand that you want to configure a many-to-many relationship between Dashboards and Widgets. Entity Framework will create the junction table for you. It will automatically update this table whenever you add a Widget to a Dashboard. It will also create the proper joins whenever you want to fetch Dashboards with their Widgets, or Widgets with the Dasheboards that use them.
Your query will be fairly simple:
var DashBoardsWithTheirWidgets = myDbcontext.Dashboards
// I only want to see the super dashboards
.Where(dashboard => dashboard.Type = DashboardType.Super)
.Select(dashboard => new
{
// Select only the properties you plan to use:
Id = dashboard.Id,
Title = dashboard.Title,
// select only the Widgets you plan to use:
Widgets = dashboard.Widgets
.Where(widget => widget.Price > 100.00)
.Select(widget => new
{
// again select only the properties you plan to use
Name = widget.Name,
Price = widget.Price,
})
.ToList();
});
See how easy it is if you stick to the conventions?
If you really want your obscure method of using foreign keys, you need a function to remove the square brackets and the commas from the widgetIds, split the string into sub-strings, Parse them to numbers, and do a join.
But before you plan to continue on this path, experiment on how to add a Widget and a Dashboard. How to add a Widget to a Dashboard, how to remove a Widget. I think the time needed to reform your database into proper format is much less than the time you'll need to implement those functions
Solution 1:
You need to restructure the dbo.dashboards table. Change the column layout of dbo.dashboards to
Auto_Generated_ID, Unique_Identifier(PK), Title, WidgetIds
I know the above column restructuring is done in a bad way. But still this will work in your case.
After redesigning it you can use join between dbo.dashboards and dbo.widgets to retrieve it in an efficient way.
Solution 2:
The below-normalized tables will work in your case
dbo.dashboard
id, title (columns)
dbo.dashboard_widget
id, dashboard_id, widget_id (columns)
dbo.widgets
id, details (columns)
Query:
select d.id, d.title, dw.widget_ids, w. details from dbo.dashboard d INNER JOIN dbo.dashboard_widget dw ON d.id = dw.dashboard_id INNER JOIN dbo.widgets w ON dw.widget_id = w.id where d.id = << id number >>
I've been trying for a while to understand how GORM works, and I think I've got it down. However, I'm having a heck of a time getting my relationships correct.
Here is my database schema setup (2 tables):
Table - Users
userhash varchar(255)
firstname varchar(255)
lastname varchar(255)
Table - Logs
userhash varchar(255)
accessdate date
There are no foreign key and primary key constraints defined in the tables. However, in the users table the userhash will be unique. I didn't design it this way, but I have to use it like this.
Now for my Grails domain classes:
Class - Users
class Users {
String userhash;
String firstname;
String lastname;
static hasMany = [logs: Logs]
}
Class - Logs
class Logs {
String userhash;
Date accessdate;
static belongsTo = Users;
}
In my controller, I do the following:
def user = Users.findByUserhash("nraboy");
println user.firstname;
println user.logs;
It prints out the correct firstname, but when I try to display the logs, it is null or empty. Am I requesting the data incorrectly for child tables or am I missing something somewhere in the domain class design?
I've tried to do the following, but had now luck as well:
Class - Logs
static mapping = {
id generator: "assigned", name: "userhash", type: "string"
}
Class - Users
static mapping = {
userhash generator: "foreign"
}
I figured the above would let me manually define the primary key and foreign key via code since it didn't exist int he tables. No luck though.
Any help would be appreciated.
Thanks,
It looks like you may have to do something with in Users for the logs association to force the foreign key to be in the Logs table, as it is. See the GORM docs One-to-Many Mapping section. Perhaps something like:
static hasMany = [logs: Logs]
static mapping = {
logs column: 'userhash'
}
I would be curious to hear if this worked...
I have a domain class UserProfile which has one to one relationship with another domain class User.The Structure of the domain is provided.
сlass UserProfile {
String fio
String position
String phone
String email
static belongsTo = [user: User]
static constraints = {
// some constraints
}
static mapping = {
//some mapping; user property is not mapped
}
I need to write a native sql query in Grails for UserProfile domain and I don't know how to refer to user property(static belongsTo = [user: User]). I have tried USER_ID but it is not working.
I can't name the column directly using mapping section; I just need to find out how user column in UserProfile domain is named in database and how it can be called in native sql query.
Very Simple if i got your question ,Grails gorm convention for storing fileds in data base is:
Like
user_profile for UserProfile -Domain
and all fileds are speparedted by underscores and most of the time gorm adds _id after a foreign key reference /or a GORM relationship like above One to One and one to Many
[belongsTo=[user]] .
Inside SQL Table
mysql describe user_profile ;
----------------------------------------------------------------
User_Profile
----------------------------------------------------------------
id
version
foo varchar(50)
postion
email
user_instance_id int
-------------------------------------------------------------------
NATIVE SQL QUERY WILL BE :
'select up.user_instance_id from user_profile as up '
the Get all the userInstance objects by querying the user table
'select * from user where user.id = 'the id you get it from the above query'
I hope you have some idea on this please ,if i didnt get it let me know.
I believe if you define user inside UserProfile, then you can access it and will automatically be mapped? It works in my previous projects, I hope it will work with this.
сlass UserProfile {
String fio
String position
String phone
String email
static belongsTo = [user: User]
User userInstance;
static constraints = {
// some constraints
}
Then you can use it
UserProfile.executeQuery("select up.userInstance from UserProfile up")
I have 3 related objects (Entry, GamePlay, Prize) and I'm trying to find the best way to query them for what I need using NHibernate. When a request comes in, I need to query the Entries table for a matching entry and, if found, get a) the latest game play along with the first game play that has a prize attached. Prize is a child of GamePlay and each Entry object has a GamePlays property (IList).
Currently, I'm working on a method that pulls the matching Entry and eagerly loads all game plays and associated prizes, but it seems wasteful to load all game plays just to find the latest one and any that contain a prize.
Right now, my query looks like this:
var entry = session.CreateCriteria<Entry>()
.Add(Restrictions.Eq("Phone", phone))
.AddOrder(Order.Desc("Created"))
.SetFetchMode("GamePlays", FetchMode.Join)
.SetMaxResults(1).UniqueResult<Entry>();
Two problems with this:
It loads all game plays up front. With 365 days of data, this could easily balloon to 300k of data per query.
It doesn't eagerly load the Prize child property for each game. Therefore, my code that loops through the GamePlays list looking for a non-null Prize must make a call to load each Prize property I check.
I'm not an nhibernate expert, but I know there has to be a better way to do this. Ideally, I'd like to do the following (pseudocode):
entry = findEntry(phoneNumber)
lastPlay = getLatestGamePlay(Entry)
firstWinningPlay = getFirstWinningGamePlay(Entry)
The end result of course is that I have the entry details, the latest game play, and the first winning game play. The catch is that I want to do this in as few database calls as possible, otherwise I'd just execute 3 separate queries.
The object definitions look like:
public class Entry
{
public Guid Id {get;set;}
public string Phone {get;set;}
public IList<GamePlay> GamePlays {get;set;}
// ... other properties
}
public class GamePlay
{
public Guid Id {get;set;}
public Entry Entry {get;set;}
public Prize Prize {get;set;}
// ... other properties
}
public class Prize
{
public Guid Id {get;set;}
// ... other properties
}
The proper NHibernate mappings are in place, so I just need help figuring out how to set up the criteria query (not looking for HQL, don't use it).
since you are doing this in each request maybe it should be better to set up two formula-properties in your entity.
The first one should fetch the latest Gameplay-Id and the other the first Gameplay-Id with a not Null property
this could be as such in the xml mapping file of Entry
<property name="LatestGameplay" formula="select top(1)gp.Id from Gameplay gp where gp.FK_EntryId = PK_EntryId order by gp.InsertDate desc" />
this leaves you with the Gameplay Id's on the Entry entity and after you fetch it it would require another round trip to the DB to GetById-fetch the gameplay's
Alternatively you could work-around using filters.
Set the collection back to "lazy"
and create these nice filters
Gameplay latest = NHibernateSession.CreateFilter(entry.GamePlays , "order by InsertDate desc").SetMaxResults(1).SetFirstResult(1).UniqueResult<Gameplay>();
Gameplay winner = NHibernateSession.CreateFilter(entry.GamePlays , "where FK_PrizeId is not null order by InsertDate asc ").SetMaxResults(1).SetFirstResult(1).UniqueResult<Gameplay>();
And IFilters can be used in a multiquery as so have 2 db hits: one for the original Entry and one for the multiquery.
Last but not least, you could define 2 bags in the Entry entity, one IList<GamePlay> Latest and one IList<Gameplay> Winner which in the Entry mapping file would be filtered with the appropriate query (although i don't remember now if you can define TOP clauses in the filters) and set those as non-lazy. Then with a single round-trip you can have all the data you want with the following (ugly) syntax
Entry entry = findEntry(phoneNumber);
Gameplay winner = entry.Winner[0]; //check this if null first
Gameplay Latest = entry.Latest[0]; //ditto
note that of all the solutions the 3rd is the one that provides a mechanism to generate additional queries, as the bag can be used in a Criteria/HQL query
I have a Project model that has a property of type IProjectWorker, this could either be a single User or a Team. In Castle ActiveRecord it's defined like this:
[Any(typeof(int), MetaType = typeof(string), TypeColumn = "WorkerType", IdColumn = "WorkerID", Cascade = CascadeEnum.None)]
[Any.MetaValue("USER", typeof(User))]
[Any.MetaValue("TEAM", typeof(Team))]
public IProjectWorker Worker { get; set; }
Now I need to be able to search for projects where the worker's name contains some text. My initial reaction was something like this:
query
.CreateAlias("Worker", "Worker")
.Add(Restrictions.InsensitiveLike("Worker.WorkerName", SearchText, MatchMode.Anywhere));
But this gives me an error-- "any types do not have a unique referenced persister". This makes sense, it doesn't know how to handle joining to the two different tables for the search.
Can I make two different aliases for each table and do a Restrictions.Or() across them? I tried it, but couldn't quite get it right. Or is there some other way to do this using criteria that I'm missing? Or am I going to have to use HQL instead?