I have found a problem with the play framework. I could also reproduce it, so here I will show a simplified reproduction szenario.
When starting the play application, I want to read sample data from a yaml file. Therefore, i use the class Fixtures. In the yaml file I have prepared a data structure of objects that stand in relation to each other.
The model classes of the data structure look like this:
#Entity
public class Album extends Model{
public String name;
#ManyToOne
public Artist artist;
}
#Entity
public class Artist extends Model{
public String name;
#OneToMany(mappedBy="artist")
public List<Album> albums = new ArrayList<Album>();
}
The Job that i use to load the yaml file and control the result looks like this:
#OnApplicationStart
public class Bootstrap extends Job {
#Override
public void doJob(){
Fixtures.deleteAllModels();
Fixtures.loadModels("sample.yml");
List<Artist> artists = Artist.findAll();
for (Artist artist : artists) {
play.Logger.info(artist.name + " has " + artist.albums.size() + " albums");
}
}
}
if i use the following structure in my yml file, then it works:
Artist(b1):
name: Nirvana
Artist(b2):
name: ACDC
Album(a1):
name: Back in Black
artist: b2
Album(a2):
name: Highway to Hell
artist: b2
Album(a3):
name: Nevermind
artist: b1
Album(a4):
name: Bleach
artist: b1
But if i do it like this, then it will NOT work:
Album(a1):
name: Back in Black
Album(a2):
name: Highway to Hell
Album(a3):
name: Nevermind
Album(a4):
name: Bleach
Artist(b1):
name: Nirvana
albums: [a3,a4]
Artist(b2):
name: ACDC
albums: [a1,a2]
However, the documentation right here tells us, that the second way should work.
Did I make a mistake in my example code, or is this really a problem with the play framework or JPA?
No, according documentation your second try should not work. Problem lies in concept of relationship owner. Only owner side (one referenced by mappedby) is consulted when bidirectional relationship is persisted.
In your case
Artist(b1):
name: Nirvana
albums: [a3,a4]
Operates to following list, which is not owner of relationship:
//owner of this relationship if attribute artist in Album entity.
#OneToMany(mappedBy="artist")
public List<Album> albums = new ArrayList<Album>();
Your first try uses artist field in Album. It works, because artist is owner of bidirectional relationship between Album and Artist. Because of same reason also example in documentation that you linked works as well.
Related
I have a simple data model
car
- make
- model
- year
- colour
- engine
- model
- no. cylinders
- size
- etc
- fuel tank
- model
- capacity
- fuel type
- etc
- etc
So I have 'car', 'engine' and 'fuel tank' entities. Each of which have many properties.
I want a list of all the 100s of cars but only want to show the following selected properties: car.make, car.model, car.year, car.engine, car.size, car.fueltype.
I can certainly use .include to bring back sub-entities in the object graph but this is a big hit as there are many properties.
My question is whether there is a neat way to do this. Or any way in fact using Entity Framework (ideally EF7/Core)?
[ I did refer to https://colinmackay.scot/2011/07/31/getting-just-the-columns-you-want-from-entity-framework/ which uses the select into an anonymous class, but could not see how this could work within multiple includes ]
Thanks you.
You only need to use Include if you want to pull the full entities back - you don't need these to do a projection. You can do a projection either anonymously, or using a defined model class. The following code should get you started:
// Define model...
public class CarModel
{
public string Make { get; set; }
public string Model { get; set; }
public int EngineCC { get; set; }
}
// Project to list of models
var cars = context.Cars.Select(c => new CarModel
{
Make = c.Make,
Model = c.Model,
EngineCC = c.Engine.CC
}).ToList();
You can make this much simpler by using a mapping library such as AutoMapper. Using AutoMapper, this becomes:
// (at start of project)
Mapper.Initialize(c => {
c.CreateMap<Car, CarModel>();
});
// Projection...
var cars = context.Cars.ProjectTo<CarModel>().ToList();
In this example, EngineCC was automatically mapped from Engine.CC, but you can manually specify any mappings which don't just work automatically. AutoMapper will create a Linq projection, only bringing back the properties you need.
I have a REST API which I have configured as follows
#Api(value="rest", description="Sweet blah!!!")
#Controller
public class abc{...}
A method in abc is annotated as follows
#ApiOperation(value="Create Account",
notes="Sweet Blah",
response=Account.class,
nickname="AccountCreation2",
produces= "application/json,application/xml",
consumes="application/json, application/xml")
#ApiImplicitParams(value=
{ #ApiImplicitParam(name="body",value="Sweet Blah.",
required=true, paramType="body", dataType="com.trrr.Account"),
#ApiImplicitParam(name="accountId", value="provides account Id for the new
account",required=true, paramType="path", dataType="Integer")
})
#RequestMapping(value = "/accounts/{accountId}", method = RequestMethod.PUT)
public ResponseEntity<?> createAccount(#PathVariable("accountId") Integer accountId,
#RequestBody Account acct){ ... }
My generated documentation using Swagger UI shows everything find however is unable to generated Model json for Account which is my model class.
Account is composed of few variables, in addition to an array of User Defined class 'Sharing'.
It is composed of another User defined class User.
Account class is annotated as follows:
#XStreamAlias("Account")
#XmlRootElement(name = "Account")
public class Account {... }
The generated documentation displays for Model Response and Request
**{
"unknownFields": {}
}**
Kindly guide as to what may be going wrong here. How to have a json version of Account object displayed. Thank you.
Well, the same thing worked on a next day. Not sure why it was not working on day one. Its appears idiotic but only purpose of me writing this is to ensure that nobody else doing the right way and gets confused with my post.
This is the domain class:
package com.sample
class Person {
String id
String name
Integer age
Address address
List children
static hasMany = [pets:Pet, children: String, aliases : Alias]
static mapWith = "mongo"
static constraints = {
address nullable:true
}
}
This is the the create page of the app:
Can someone please tell me how I can get a list to write in the create Person page and a list editable in the edit Person page. (I'm using generated views by the command grails generate-view com.sample.Person)
First, you don't need the List children in domain class. But I'm not sure if grails supports scaffolding for relations with basic non-domain types (String in your case). If removing the list wouldn't help you will need to handle this situation manually.
I'm using Morphia for MongoDB with Stripes Framework.
Let us assume I have two entities, Car (which describes a specific car, say some particular 1984 Honda Accord) and CarType (which specifies all Honda Accords of that kind):
The most natural way to model this seems:
#Entity
class Car {
#Id private String id; // VIN
private Date purchaseDate;
private Color color;
#Reference private CarType type;
// ..
}
#Entity
class CarType {
#Id private String id;
private String manufacturerId;
private float engineDisplacement;
// ..
}
This works, but is inefficient, as CarType is looked up from DB every time a Car is loaded. I would like to cache car types in memory, as they change rarely. Persistence frameworks like GORM and Hibernate would allow that out of the box, but I'm not sure how to do it under Morphia (there is a feature request raised for that).
I'd like to keep the reference to CarType, as just storing a String carTypeId would complicate the views and everything else too much.
So I thought I could do something like this:
#Entity
class Car {
#Id private String id; // VIN
private Date purchaseDate;
private Color color;
private String typeId;
#Transient private CarType type;
#Transient private CarService service = new CarServiceImpl();
public void setTypeId() {
this.typeId = typeId;
updateTypeReference();
}
#PostLoad void postLoad() {
updateTypeReference();
}
private void updateTypeReference() {
type = service.findTypeById(typeId);
}
// ..
}
class CarServiceImpl implements CarService {
#CacheResult CarType findCarTypeId(String typeId) {
datastore.get(CarType.class, typeId);
}
// ..
}
Which works and does what I want, but:
Does seem like a hack
I'd to inject the service instead using Guice, but cannot figure out how, although I have overall dependency injection working in Stripes ActionBeans.
So I'd like to either:
Learn how to inject (preferably, Guice) services into Morphia entities
or
Learn how to otherwise properly do caching for referenced entities in Morphia
or
If all else fails, switch to some other MongoDB POJO mapping approach which supports caching. But I really like Morphia so I'd rather not.
Another common approach would be to embed the CarType in each Car. That way you would only have to fetch a single entity.
Trade-offs:
You'll need an update logic for all duplicated CarTypes. Since you said that they hardly change, this should be fine performance-wise.
Duplicated data requires additional disk-space and the working set in RAM gets bigger as well.
You'll need to evaluate how this works out for your data, but data duplication to make reads faster is quite a common approach...
Since I didn't think of a better solution I am doing a #PostLoad event handler which gets the datastore class from a static variable, and can then look up the Referenced entity.
That seems like a hack and requires the datastore service to be thread-safe, but it works for me.
Wondering how some of the more experienced (or anyone with a better idea than I have) would tackle my particular modeling scenario...
I have a typical "Category -> SubCategory ->TertiarySubCategory" scenario and I'm not sure if I'm mapping it out correctly. I am mapping this directly to an MVC route since raven seems to lend itself well with this. Under the final category (which can be at the first, 2nd or 3rd levels there will be a list of items associated with only that level of a category. So, we might have something like:
Single level category: '/Politics/'
Second level category: 'Politics/People' or 'Politics/Websites'
Tri-Level Category: 'Sports/Pro/Volleyball' or 'Sports/College/Football'
In a traditional RDBMS this is easy through primary/foreign keys + a few joins... so, wondering how I would handle with Raven?
From what I have read should I store the entire 'sports/pro/volleyball' URI or Key in a list of items that fall under it?
i.e. -
public class CategoryItem
{
public string FriendlyName {get;set;} // Volleyball or Pro Volleyball
public string CategoryURI {get;set;} // i.e. - "/sports/pro/volleyball/"
public string content {get;set;} // i.e. - "Who is the best Pro Volleyball Athlete?"
public List<string> Comments {get;set;}
}
// then we could store something like this:
var survey1 = new CategoryItem();
survey1.CategoryURI = "/sports/pro/volleyball/"
survey1.Content = "Who is the best female pro volleyball player?";
survey1.Comments.Add(new Comment("Misty May"));
var survey2 = new CategoryItem();
survey2.CategoryURI = "/sports/pro/volleyball/";
survey2.Content = "Who is the best male pro volleyball player?";
survey2.Comments.Add(new Comment("Some guy I don't kow");
// asuumes ravenSession was alreadyopened...
ravenSession.Store(survey1);
ravenSession.Store(survey2);
ravenSessoin.SaveChanges();
//{ ...... etc ..... }
//Then I can query by CategoryURI without needing joins (denormalization).... i.e. -
var items = session.Query<CategoryItem>()
.Where(x => x.CategoryURI == "/sports/pro/volleyball/");
Or should I create a List items member of the actual category class? Each item would have a list of it's own comments... meaning everything's stored in a single document within Raven - i.e. -
public class Category
{
public string FriendlyName {get;set;} // i.e. - "Volleyball" or "Pro Volleyball"
public string URI {get;set;} // i.e. - "/sports/pro/volleyball" which is the MVC path
public List<CategoryItem> Items {get;set;}
}
public class CategoryItem
{
public string Content {get;set;}
public List<string> Comments {get;set;}
}
var vballCat = new Category();
vballCat.FriendlyName = "Pro Volleyball";
vballCat.URI = "/sports/pro/volleyball/"; // equivalent to the MVC route
var catItem = new CategoryItem().
catItem.Content = "Who is the best male pro volleyball player?";
catItem.Comments.Add("Misty May");
catItem.Comments.Add("Some Guy 1");
vballCat.Items.Add(catItem);
ravenSession.Store(vballCat);
ravenSession.SaveChanges();
..... now once I pull the primary cat i.e. - "/sports/pro/volleyball/" I have everything I need already under it
var items = session.Query<Category>()
.Where(x => x.URI == "/sports/pro/volleyball/");
{ ............. etc ............... }
Now here I can just iterate through the Items collection and it's collection of comments.... does this use eager loading? What if I had a million comments under one category item? When I load the main category would it load all one million comments too!?!?
I would appreciate any help you can provide. Sorry if this example/question is unclear... I'll try to clarify anything if you guys need it. Thanks again!
The answer is that it depends on the size of your data and your usage scenario.
The first example is useful if you have large number of items and want to access categories without its items.
The second example is useful if you usually access category with its items, and the size of items is limited (note that limited is still high, several thousands wouldn't cause me to blink).
Note that there is no such thing as eager / lazy loading in RavenDB, you are talking about a single document vs. multiple documents, not about relations between documents. The entire document is loaded when you need it.
Another thing to remember is that it is usually faster to query by id than querying. That means that if you have ids that already looks very much like Document Ids, you might as well MAKE them the document ids.