I tried to define one to one relation in 2 different way:
Grails 2.0.3
Case 1:
class Car {
String model
Engine eng
static constraints = {
eng unique: true
}
}
class Engine {
Double capacity
static belongsTo = [car : Car]
}
Case 2:
class Car {
String model
static hasOne = [eng : Engine]
static constraints = {
eng unique: true
}
}
class Engine {
Double capacity
static belongsTo = [car : Car]
}
looks similar, and both provide one to one bidirectional mapping. Unfortunately DB has different structure in both cases.
Case 1:
Case 2:
Why once Car and once Engine keeps link to second table.
Where is my problem? When I am looking at the code, from DDD perspective, both cases suggest that Car class is more important and Car aggregates Engine. Unfortunately when I look from DB side on case 2 I would rather said that it is opposite - Engine aggregate Car. Of course I can use first approach, but most of publication I saw about grails, present second way for defining relation. Maybe I misunderstood something and I use hasOne in wrong way?
The documentation on hasOne states that using this creates a bi-directional one-to-one relationship where the foreign key is on the child.
The belongsTo means that actions performed on the parent (eg save and update) will be cascaded by hibernate to the child.
So if you want the foreign key to be on Engine use static hasOne = [engine:Engine] on Car.
If you want the foreign key to be on Car then use Engine engine on Car .
In both cases use belongsTo = [car: Car] on Engine
I think you should try to make this aproach.
class Car {
String model
Engine engine
static constraints = {
eng unique: true
}
}
class Engine {
Double capacity
Car car
}
I think that will make it. You can read it here:
By default the address association would map to a foreign key column
called address_id.
http://grails.org/doc/latest/guide/GORM.html
on the Many-to-One/One-to-One Mappings
Hope it helps :)
Related
I'm pretty new to laravel and have a really basic question related to relationships.
Here is an example of my question:
I have a migration called money_transfers.
The migration contains the following things:
user_id (transfer sent by)
sentTo_id (transfer sent to)
amount
sent_at
BOTH user_id and sentTo_id refer to a User ID.
Now, what I want to do is the following:
Fetch the user the money was sent TO the same way as the user the money was sent BY. Just like in the example below:
$transfer->sentTo->name
or
$transfer->sentTo->id
You get what I mean. Thanks in advance :)
If you defined your foreign keys correctly in your migration table, Then it's just a matter of defining the right relationship:
class MoneyTransfer extends Model
{
public function sentBy()
{
return $this->belongsTo(User::class,'user_id');
}
public function sentTo()
{
return $this->belongsTo(User::class,'sentTo_id');
}
}
This way you can access the receiver attribute like this:
$transfer->sentTo->name;
And the sender attribute like this:
$transfer->sentBy->name;
Why is it not possible to unindex a list of an objectify entity?
To demonstrate the problem I made a simple example project.
I used the entity
#Entity
public class Car {
#Id String id;
#Unindex List<Passenger> passengers;
}
an the object
public class Passenger {
String name;
}
and saved it using this simple method.
public class CarFactory {
public void writeCarEntity() {
Car car = new Car();
car.setId("myCar");
List<Passenger> passengers = new LinkedList<Passenger>();
Passenger carl = new Passenger();
carl.setName("Carl");
Passenger pete = new Passenger();
pete.setName("Pete");
Passenger jeff = new Passenger();
jeff.setName("Jeff");
passengers.add(carl);
passengers.add(pete);
passengers.add(jeff);
car.setPassengers(passengers);
ObjectifyService.register(car.getClass());
ObjectifyService.ofy().save().entity(car).now();
}
}
Looking up the entity in the datastore you get this information:
Although the passengers field has the annotation #Unindex it will be indexed, as one can see in the google "Datastore". Why does the annotation #Unindex has no effect in this example???
This is unrelated to Objectify and appears to be some new quirk of the datastore. It might just be a display glitch in the UI. Is it causing problems?
With the code you posted, Objectify will call Entity.saveUnindexedProperty() on the passengers field (even without the #Unindex annotation). But even if Objectify tried to index it, historically you can't index embedded objects, so it's unclear what it means to index a list of them. Maybe Google is rolling out some new behavior and they haven't got the GUI working correctly yet? Or maybe there is a bug in their save behavior?
If you want to be a good citizen, create a simple test case with the low level API (an Entity that contains a property of type List<EmbeddedEntity>), verify that this same behavior occurs, and file a bug in the GAE issue tracker.
We work with an existing mysql database under grails which contains a m:n relation. No problem here. But now we are in the situation that i have to add a second n relation entry, which links to the same table and has to be associated to the first n entry.
If it were for the database, I would simply create a table which looks like:
field m_id -> links to m table
field n_id1 -> links to n table
field n_id2 -> links to n table
But how can this be represented in a grails domain class?
Possibly the answer can already be found somewhere, but the searches I did were not successful, maybe due to lack of search term creativity.
EDIT:
Trying to clarify the question: we have a many-to-many relation, but with two items on one side, which have to maintain association to each other (and it also must be clear which is for example the original and which is the replacement item), so they can not be seperated into two separate entries into the relation.
Hmm... try to think of racing car drivers nominating for a series of races, and every nomination has to contain the driver and his substitute. Races would be m (left hand), driver would be n1 and substitute would be n2. (It was really hard work to find an example...)
EDIT:
By coincidence I found this question which addresses the same problem but also left off rather unsolved.
Take a look at the documentation, is pretty clear:
http://grails.github.io/grails-doc/2.5.0/guide/GORM.html#gormAssociation
Anyway I'm not sure I understand what you're trying to do, but let's try.
If it's a one-to-one relation, you could simply do something like this:
class DomainA {
DomainB domainB
DomainC firstDomainC
DomainC secondDomainC
}
class DomainB {
}
class DomainC {
}
That would create the following fields in the "domain_a" table:
"domain_b_id"
"first_domain_c_id"
"second_domain_c_id"
If it's a one-to-many relationship from DomainA to both DomainB and DomainC with two differentiated collections of DomainC in DomainA, you must have two different DomainA properties in DomainC to be able to map it.
The example with Airport and Flight in the documentation:
class Airport {
static hasMany = [outboundFlights: Flight, inboundFlights: Flight]
static mappedBy = [outboundFlights: "departureAirport",
inboundFlights: "destinationAirport"]
}
class Flight {
Airport departureAirport
Airport destinationAirport
}
The flight needs to Airport properties in order to be able to distinguish which one is mapping the right hasMany collection in Airport.
EDIT: for the race-driver example
Grails supports many-to-many relations but one of the ends must be the principal one and the other must additionally have a belongsTo to it. Though this is not the case, since neither race belongs to driver nor driver belongs to race.
I would use relation with a property: "drives" with a property "mainDriver".
That cannot be mapped directly, you need to use a domain for the relation:
class Race {
static hasMany = [ participants: DriverRace ]
def mainDrivers() {
getDrivers( false )
}
def substitutes() {
getDrivers( false )
}
private getDrivers( mainDriver ) {
DriverRace.withCriteria {
eq( "race", this )
eq( "mainDriver", mainDriver )
}.collect { it.driver }
}
}
class Driver {
static hasMany = [ races: DriverRace ]
}
class DriverRace {
static belongsTo = [ race: Race, driver: Driver ]
boolean mainDriver
}
We thought we were able to solve the problem by inserting a second reference to domain/table n in the mapping property of (left hand side) domain m. grails then seems to put a second reference to n on the right hand side of the relation. But that turned out to be a hopeful weekend dream.
I have been trying to define some Database schema to use the laravel framework. I want to model a Football match. The first step I wanted to do is to define the Entity Relationship diagram, but I found this (which I thought would be quite trivial) to be confusing in some aspects.
First, the obvious approach is to say that a Match is related with two Teams, and a Team is related to any number of Matches. So, we would have a "Many to Many" relationship.
But the implementation of a many to many relation is to have two tables and an intermediate table to relate both entities. I think this would be too much, when I know that a Match will always have two Teams and simply having two columns (local_id and visitant_id) with foreign keys to the Teams table would be enough. Plus, I want to be able to do:
Match::find(1)->local() or Match::find(1)->visitant();
So, thinking on this I am implementing a "One to Many" relation, but with this I have another issue. To retrieve all the matches a Team has played I would like to do:
Team::find(1)->matches();
But I cannot do this because I can only specify one key column when defining the matches() method in eloquent (by default it would be team_id, but it should be visitant_id and local_id).
After some more digging into the source code I found there is a way to actually keep my database schema as it is and achieve what I want (at least in Laravel 4). I posted my problem in github and Taylor Otwell (creator of the framework) gave me the correct answer: https://github.com/laravel/framework/issues/1272
Quoting him, it should be as easy as this:
class Team extends Eloquent {
public function allMatches()
{
return $this->hasMany('Match', 'visitant_id')->orWhere('local_id', $this->id);
}
}
And then...
$team = Team::find(2);
$matches = $team->allMatches;
This is one of those famous database design problems. Friendship relationships, for instance, suffer from that same difficulty. Since you are using Eloquent, I would suggest you to stick with many to many approach and have an extra boolean column local on your intermediate table
class Match extends Eloquent {
public $includes = array('team'); // Always eager load teams
public function teams() {
return $this->has_many_and_belongs_to('team')->with('local');
}
public function get_local() {
foreach ($this->teams as $team) {
if ($team->pivot->local) return $team;
}
}
public function get_visitant() {
foreach ($this->teams as $team) {
if (!$team->pivot->local) return $team;
}
}
}
class Team extends Eloquent {
public function matches() {
return $this->has_many_and_belongs_to('match')->with('local');
}
// I'm doing separate queries here because a team may have
// hundreds of matches and it's not worth looping through
// all of them to retrieve the local ones
public function matches_as_local() {
return $this->has_many_and_belongs_to('match')->with('local')
->where('pivot_local', '=', 1);
}
public function matches_as_visitant() {
return $this->has_many_and_belongs_to('match')->with('local')
->where('pivot_local', '=', 0);
}
}
Obs:
The method has_many_and_belongs_to(...)->with('field') has nothing to do with eager loading. It tells Eloquent to load the intermediate table column field and put that in the pivot.
Usage:
$match = Match::find(1);
$match->local; // returns local team
$match->visitant; // returns visitant team
$team = Team::find(1);
$team->matches; // returns all matches
$team->matches_as_local; // ...
$team->matches_as_visitant; // ...
foreach ($team->matches as $match) {
if ($match->pivot->local) {
// put nice local icon here
} else {
// put nice visitant icon here
}
}
This is my first question on any of these websites so pardon my unprofessionalism.
I use playframework with SIENA module (with GAE) and I came accross the following problem:
Given 3 entities:
public class Meeting extends Model{
#Id
public Long id;
public String place;
#Owned
Many<MeetingUser> users;
.
.
.
}
public class User extends Model{
#Id
public Long id;
public String firstName;
public String lastName;
#Owned
Many<MeetingUser> meetings;
.
.
.
}
public class MeetingUser extends Model{
#Id
public Long id;
public Meeting meeting;
public User user;
.
.
.
public User getUser(){
return Model.all(User.class).filter("id", user).get();
}
public Meeting getMeeting(){
return Model.all(Meeting.class).filter("id", meeting).get();
}
}
For instance I am listing a meeting and all their users:
public static void meetingInfo(Long meetingId){
Meeting meeting = Models.all(Meeting.class).filter("id",meetingId);
List<MeetingUser> meetingusers = meeting.asList();
List<User> users = new ArrayList<User>();
for(MeetingUser mu: meetingusers){
users.add(mu.getUser());
}
render(users);
}
This is done(is there any better way here?) however when it comes to filtering (especially dynamic filtering for many many fields) I can not use the Query's filter method on the MeetingUser as I need to filter on a MeetingUser's field's field (firstName). The same problem arise for ordering. I need the solution for both problems.
I hope my problem is clear and I appreciate any kind of help here.
Remember that you are in GAE which is a NoSQL DB.
So you can't do Join request as in RDBMS.
Yet, this is not really the pb you have so this was just to be sure you are aware of it ;)
So if you want to find the person having given firstname in a given meeting, can you try the following:
List<MeetingUser> meetingusers = meeting.users.asQuery().filter("firstname", "XXX");
(you can also order)
Nevertheless, knowing that you can't join, remember that you can't write a query searching for a meeting in which there are users whose firstname is XXX as it would require some joins and it doesn't exist in GAE. In this case, you need to change your model following NoSQL philosophy but this is another subject
regards
Let's try to give a way to do what you want...
Your relation is a Many-to-Many which is always the worst case :)
You want to filter Meeting by User's firstname.
It requires a join request which is not possible in GAE. In this case, you must change your model by denormalizing it (sometimes use redundancy also) and manage the join by yourself. Actually, you must do the job of the RDBMS by yourself. It seems overkill but in fact, it's quite easy. The only drawback is that you must perform several requests to the DB. NoSQL means No Schema (& No Join) so there are a few drawbacks but it allows to scale and to manage huge data load... it depends on your needs :)
The choice you did to create the MeetingUser which is a "joined" table and a kind of denormalization is good in GAE because it allows to manage the join yourself.
Solution:
// fetch users by firstname
List<User> users = users.all().filter("firstName", "John").fetch();
// fetch meetingusers associated to these users (verify the "IN" operator works because I didn't use that for a long time and don't remember if it works with this syntax)
List<MeetingUser> meetingusers = MeetingUser.all().filter("user IN", users);
// now you must fetch the whole meeting because in MeetingUser, only the Meeting ID is stored (other fields are Null or O)
List<Meeting> meetings = new ArrayList<Meeting>()
for(MeetingUsers mu:meetingusers) {
meetings.add(meetingusers.meeting);
}
// use the batch feature to fetch all objects
Meeting.batch(Meeting.class).get(meetings);
// you have your meetings
Hope this helps!