Ref<?> modify a field from object and save it when saving containing object - google-cloud-datastore

I am using Objectify for Google Cloud Datastore.
I am looking into Ref<?> and tried to see if I can modify a property from the object with this annotation but it doesn't seem to be saved in my datastore.
Example:
I have these classes, I'll exlude setters and getters.
#Entity
class Car {
#Id Long id;
#Load Ref<Person> driver; // Person is an #Entity
}
class Person {
#Id Long id;
String name;
}
If I do this
Car car = new Car();
car.driver = Ref.create(driverKey);
ofy().save().entity(car).now();
Car fetched = ofy().load().entity(car).now();
fetched.driver.get().setName("Pepito");
ofy().save().entity(car).now();
It won't change the name of the Person in the database.
Is there any way to achieve this?
Thanks.

References are just that - references to separate entities with separate lives. There is no cascading save. If you want to save the driver, do it explicitly.

Related

EF manual attach list of entities into parent entity

class Student{
public string Name {get; set;}
public EntityCollection<Info> Infos {get; set;}
}
class Info{
public string Title {get; set;}
public Student Student {get; set;}
}
I have two entities like this. First I will query one student entity
var student = db.Students.FirstOrDefault(s => s.StudentId = 1);
Then I query Info list of this student in a separate query
var infos = from c in db.Info where c.StudentId = 1 and ....
If I loop though infos and add it manual into student.Infos, it will cause insert new row
foreach(info in infos){
student.Infos.Add(info);
}
How to attach list of info into student entity without insert new row into Info table when db.SaveChanges(). Like
student.Infos = infos
EF does the work for you behind the scenes when you use navigation properties. It's not just a data layer to load data singularly but rather it's set up with the relationships between the data and is capable of loading an entire object graph of related data either in one hit (eager loaded) or on-demand (lazy loaded)
Firstly: you can update your Info collections to ICollection<Info> or List<Info>. I opt for List<Info> because I commonly use .AddRange(). Also, mark it as virtual to enable EF proxies and lazy loading.
From there, to access the Infos on a Student you can just use:
var student = db.Students.Include(s => s.Infos).SingleOrDefault(s => s.StudentId = 1);
This will eager-load the Infos for the selected student. No need to load them separately.
If you leave off the .Include(..) then you can still access the Infos (provided the DbContext is still in scope) though this will trigger additional SQL calls to load the Infos. (Lazy loaded)
When loading data to send outside of the scope of the DbContext, such as returned from an API call, or sent to a view, it's recommended to compose a DTO or ViewModel with just the fields that you need from the various entities, then perform a .Select() to populate them, and return the DTOs not the entities. This avoids problems with lazy loading calls after a DbContext has been disposed and unexpected performance issues if lazy loading is triggered due to serialization or the like.

can I add my own classes to entity framework?

I wanted to start using Entity Framework for my projects. A new project which we'll be starting soon, will have an Employee table. I was initially planning to have a IEmployee Interface, which will be implemented by a Manager and Staff classes, which will allow different functionality but will both store data in the Employee table, with a flag in the table distinguishing them.
If I use DB First, and design my Employee table and then use Entity Framework, i know the .tt file will have a partial class Employee. I could then make my own Manager and Staff classes which implement the partial class Employee. But then how would I store that back in the db using Entity Framework? Could I just do something like
// currentManager would be the manager object
dbContext.Emplyee.Add(currentManager);
dbContext.SaveChanges();
would entity framework be ok with this, even though i'm passing a Manager object into Employee to save? Or is there a better way to do this? And same with retrieving, how could I use Entity Framework to get back a Manager or Staff? Would I need to get a Employee back first and then cast it? Something like
var employee = from employees... // get employee
Manager currentManager = (Manager)employee;
Yes, you can use EF DB first and have multiple classes derived from a common base class that are stored in the same database table.
The "flag" you envision is called a discriminator by EF. It is a column in the table that specifies which subtype the record is an instance of.
With database first, you need to tweak the model generated by EF to get this working, but it's fairly straightforward. I think the easiest way to set this up is to
Create the database model in SQL, being sure the Employee table has a NOT NULL property like EmployeeType that can be used as the discriminator. I think this can be any type, but an int will work fine.
Create your EF model ( Add | New Item... | Data | ADO.NET Entity Data Model ), and map the Employee table (and anything else you need).
Double click the generated .edmx file to open the entity framework designer.
Right mouse click on the canvass and select Add New... | Entity and create a Manager entity that derives from Employee. Repeat for Staff. Your designer should look like this:
Right mouse click on Manager and select Table Mapping. In the mapping details, specify that it maps to Employee and add a condition that is When EmployeeType = 1. Do the same for Staff, but make the mapping When EmployeeType = 2.
Last, you need to delete the EmployeeType property from the Employee mapping.
That being done, you can extend the Manager and Staff classes (that will now be generated by EF as partial classes) to have whatever business logic you want, and do queries/etc with EF via the Employees mapping:
public class Manager : Employee
{
public void customManagerMethod() { }
}
public class Staff : Employee
{
public void customStaffMethod() { }
}
class Program
{
static void Main(string[] args)
{
using (var db = new dbfirstEntities())
{
Manager m = new Manager
{
FirstName = "Joe",
LastName = "Bigshot"
};
Staff s = new Staff
{
FirstName = "Joe",
LastName = "Schmoe"
};
db.Employees.Add(m);
db.Employees.Add(s);
db.SaveChanges();
}
}
}

Persisting Collection<T> using Objectify4 on GAE

I have been having issues with persisting a generic Collection on Google datastore using Objectify4. E.g.
#Entity
class AnimalInfo
{
#Id
String id;
Collection<Animal> animals;
}
#EntitySubClass
class Cat extends Animal
{
String name;
}
#EntitySubClass
class Dog extends Animal
{
String name;
}
#Entity
class Animal
{
#Id
String id;
}
How can I persist the AnimalInfo class and retrieve it again. I have tried:
objectify.save().entities(animalInfo).now(); but while fetching it back again: objectify.load().type(AnimalInfo.class).id(animalInfo.id).get(); doesnt have the name field corresponding to the extended class Cat or Dog.
This is also probably logical because Animal class doesnt have a field name. But how do I get this to work? A generic interface IAnimal (in place of Animal class) is a better solution design-wise, but that doesnt work with Objectify as it needs concrete types.
Any solution for the above problem??
Thanks in advance.
Shaun
To summarize, it looks like you want a collection of references to polymorphic entities. Do this:
#Entity
class AnimalInfo {
#Id String id;
Collection<Ref<Animal>> animals = new ArrayList<Ref<Animal>>();
}
You need Refs to create the reference to the other entities. You could use Key too, but it will be less convenient. You may also want to look into the #Load annotation.

Example of how to add secondary index when storing object in Riak with Java Client?

I am storing a hash-map in a riak bucket like this:
bucket.store(key, docHashMap).execute();
I would like to store the object with a secondary index.
How would I accomplish this? I am aware that the IRiakObject has a addIndex method, but how do I access the IRiakObject before it is stored?
I would think that what I am trying to do is the expected use-case, yet I am not able to find any documentation or examples on this. If you can point me to one that would be greatly appreciated.
Thanks!
Update:
#Brian Roach answered this on the Riak mailing list and below. Here is the custom class that I wrote that extends the HashMap:
class DocMap extends HashMap<String, Object> {
/**
* Generated id
*/
private static final long serialVersionUID = 5807773481499313384L;
#RiakIndex(name="status") private String status;
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}
I can still use the object as an ordinary hashmap and store keys and values, but it will also write "status" to a secondary index (and actually end up being called "status_bin" since it's a string.
If you're just passing in an instance of the core Java HashMap ... you can't.
The way the default JSONConverter works for metadata (such as indexes)
is via annotations.
The object being passed in needs to have a field annotated with
#RiakIndex("index_name"). That field can be a Long/Set<Long> or
String/Set<String> (for _int and _bin indexes respectively).
These are not converted to JSON so they won't affect your serialized
data. You can have multiple fields for multiple indexes.
You don't have to append "_int" or "_bin" to the index name in the
annotation - it's done automatically based on the type.
Easiest thing to do woud be to extend HashMap and simply add the
annotated field(s).

Objectify 4 save entity with Ref

I slightly modified example from the current doc. On saving and entity with Refs that were created from the not saved entitied, do the refs get saved as well? In my case the driver has #Parent - a car.
#Entity
class Car {
#Id Long id;
#Load Ref<Person> driver; // Person is an #Entity
}
Car car = new Car();
car.driver = Ref.create(driver);
ofy().save().entity(car).now();
No, save operations do not cascade. And load operations only cascade when there is an explicit #Load annotation.
If you want to save the driver as well, do it explicitly.

Resources