Objectify 4 save entity with Ref - objectify

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.

Related

Symfony joining tables

I'm completely new to Symfony and I'm struggling with the query builder.
I have card entity and transaction entity in manytomany relation.
card entity: (id, card_number, code)
transaction entity: (id, amount, source, destination)
card_transaction: (card_id, transaction_id)
I want to get all the transactions that have a given card number.
We'd typically need to see the source for both of those entity classes.
Have you made a repository class for the CardTransaction entity yet? You could create this to have a reusable query as follows:
<?php
namespace App\Repository;
class CardTransactionRepository extends \Doctrine\ORM\EntityRepository
{
public function findByCardNumber(int $cardNumber): array
{
if ($cardNumber < 1) {
throw new \InvalidArgumentException("Card ID invalid.");
}
$qb = $this->createQueryBuilder("ct")
->leftJoin("ct.card", "c")
->andWhere("c.cardNumber = :cardNumber")
->setParameter("cardNumber", $cardNumber)
;
return $qb->getQuery()->getResult();
}
}
That being said what you're doing is such a simple query/operation you shouldn't need a query builder to do it. This is precisely what entity classes make easy, including Doctrine's ArrayCollection class.
If you're just doing this in a controller or view and have already loaded the Card entity you don't need a query in a repository class. Just iterate $card->getCardTransactions() assuming you've created the appropriate data accessor functions in Card. -- Then again, without seeing the Card and CardTransaction entity sources I couldn't tell you exactly what method names you have (or need).
You can retrieve a Card entity by its card_number property in a controller as follows:
$card = $this->getRepository(Card::class)->findOneByCardNumber($cardNumber);
Ideally you should check it exists too though kind of like this:
if (!($cardNumber = intval($cardNumber))) { // Do something better to validate the card number that has been specified, e.g. a `preg_match()`
throw $this->createNotFoundException("Card not specified.");
}
if (!($card = $this->getRepository(Card::class)->findOneByCardNumber($cardNumber))) {
throw $this->createNotFoundException("Card not found.");
}

Ref<?> modify a field from object and save it when saving containing object

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.

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.

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.

attaching an entity with a related entity to a new entity framework context

Im trying to get my head around attaching an entity with a related entity to a new context when I want to update the entity.
I have a Person Table (Generalised to Personnel), which has a LanguageID field. This field is linked as a FK via the EF to another table Language with LanguageID as the primary key (1-M). I need to update a particular Persons language preference, however, the relationship seems to remain linked to the old context as i get a "Object cannot be referenced by multiple instances of IEntityChangeTracker" error on the line marked below. Is there any way to attach the Language entity to the new context as a relationship of the Personnel (Person) entity???
The entities were not detached in the orginal GetPersonnel() method which uses an .Include() method to return the PreferredLanguage
PreferredLanguage is the NavigationProperty name on the Person table...
public static void UpdateUser(Personnel originalUser, Personnel newUser )
{
using (AdminModel TheModel = new AdminModel())
{
((IEntityWithChangeTracker)originalUser).SetChangeTracker(null);
((IEntityWithChangeTracker)originalUser.PreferredLanguage).SetChangeTracker(null);
TheModel.Attach(originalUser);--Error Line
TheModel.ApplyPropertyChanges("Person", newUser);
TheModel.SaveChanges();
}
}
Thanks
Sean
To avoid these sort of problems you should make GetPersonnel() do a NoTracking query.
I.e.
ctx.Person.MergeOption = MergeOption.NoTracking;
// and then query as per normal.
This way you can get a graph of connected entities (assuming you use .Include()) that is NOT attached. Note this won't work if you try to manually detach entities, because doing so schreds your graph.
Hope this helps
Alex

Resources