Is there a way to load only ID of entity/entities avoiding FetchGroup strategy ? - jpa

There some cases when loading of only ID of an entity or IDs of a collection of entities (in a relationship, for example) is needed. For example I have en entity Parent and a collection of children in it
class Parent {
private List<Child> children;
}
So, when I want to load children I want only the ids to be loaded for some cases, not whole state of children. I made a research and I found a way via Named FetchGroup, which means if I want to implement this I have to add for each Entity annotation #FetchGroup(name="id", attributes = {#FetchAttribute(name = "id")}). That work for the cases when I want to apply it for only an entity of course. For children case also LoadGroup should be configured.
The question here is: is there another strategy specially for loading of ONLY ID's, when an entity or entities are requested ? Or a way to indicate that ? Here I want to avoid annotation with #FetchGroup all entities I would like to applied that, just for loading of only ID. Of course, creation of a query like "SELECT ID FROM Parent WHERE ..." or "SELECT child.id FROM Parent INNER JOIN Parent.children WHERE ..." is also not a solution here since always should be defined, for all relationships and entities.

If you just want one entity, you can use getReference() on EntityManager.
If it is a query, or relationship, then fetch groups in EclipseLink is your only option.

Related

FOSElasticaBundle: ManyToMany relationship

I'm trying to use FOSElasticaBundle on my symfony 4 project and I have some problems.
I have an entity, "Users", with the next annotation in the property "segments":
* #var Collection $segments
* #ORM\ManyToMany(targetEntity="App\Entity\Segment", mappedBy="users", cascade={"persist", "remove"})
Which is the correct form to do the mapping in the fos_elastica.yaml file?
I would suggest you to think in your use cases and then decide which related entities are you going to use for search purposes. Once decided, you can do two things:
Option A, use a nested field. Is simpler but, if you update a child (related) entity, the Elasticsearch index will not be updated. Yo must ensure that this happens within your code. For example, each time the child entity gets updated, you set a timestamp in the parent entotity, so FosElasticaBundle catch's that chahge and runs the entity serializer so the child entity becomes serialized.
Option B, use a child parent relation in Elasticsearch. In this case FosElasticaBundle will track your entity changes correctly for each Doctrine entity, so you don't need to manage it in your code.
In both cases, you many to many relationship becomes in two one to many relations. In option A this is done by the serializers that embeds the child document in the parent one. In the second case it is tranlated to a parent child relationship.

How to set up relationships between new and existing entities in EF

My application allows the user to create a hierarchy of new entities via a UI - let's say it's a "Customer" plus one or more child "Order" entities. The user also assigns each Order entity to an existing "OrderDiscount" entity (think of these as "reference"/"lookup" items retrieved from the database). Some time later, the user will choose to save the whole hierarchy to the database, accomplished like this:-
using (var context = new MyContext())
{
context.Customers.Add(customer);
foreach (var entity in context.OrderDiscounts.Local)
{
objectStateManager.ChangeObjectState(entity, EntityState.Unchanged);
}
context.SaveChanges();
}
The foreach loop changes the state of the OrderDiscount entities to Unchanged, and prevents EF from attempting to insert them into the database, resulting in duplicates.
Great so far, but I've now hit another issue. For reasons I won't go into, the OrderDiscount entities can come from different BLL calls, resulting in a situation where two Orders in the graph may appear to reference the same OrderDiscount (i.e. both have the same PK ID, and other properties), but the entities are different object references.
When I save, the above foreach loop fails with the message "AcceptChanges cannot continue because the object's key values conflict with another object in the ObjectStateManager. Make sure that the key values are unique before calling AcceptChanges". I can see the two OrderDiscount objects in the context.OrderDiscounts.Local collection, both with the same PK ID.
I'm not sure how I can avoid this situation. Any suggestions?
This article (http://msdn.microsoft.com/en-us/magazine/dn166926.aspx) describes the scenario and provides one possible solution, which is to set just the FK ID (order.OrderDiscountId), and leave the order.OrderDiscount relationship null. Unfortunately it's not feasible in my case, as further down the line I rely on being able to traverse such relationships, e.g. ApplyDiscount(order.OrderDiscount);.

EF anonymous object query returns null collections instead of empty ones

I'm using this trick to perform conditional Include's with EF. http://blogs.msdn.com/b/alexj/archive/2009/10/13/tip-37-how-to-do-a-conditional-include.aspx
The problem I'm having is that any collections that don't have records, are null, and not empty. This is causing headaches cos I have to check each collection before I can loop through it in my mvc view, otherwise i get a null reference exception.
For example, the StudentModules collection will be null. How can I turn it into an empty list in my query? ie without having to loop through it all and checking.
I can put a constructor in the poco to initialize the list, which fixes it, but the this collection is a virtual member in the poco (based on an EF video!) - surely this is not the way to go?
var query = from module in db.Modules
where module.Id == id
select new
{
module,
QualificationModules = from qualificationModule in module.QualificationModules
where qualificationModule.IsDeleted == false
select new
{
qualificationModule,
qualificationModule.Qualification,
StudentModules = from studentModule in qualificationModule.StudentModules
where studentModule.IsDeleted == false
select new
{
studentModule,
studentModule.Student
}
},
Assessments = (from assessment in module.Assessments
where assessment.IsDeleted == false
select new
{
assessment,
assessment.AssessmentType
}
)
};
var modules = query.AsEnumerable().Select(x => x.module);
return modules.ToList().First();
Relationship fixup runs when an entity gets attached to a context - either manually by calling Attach or when the entity is materialized as a result of a query (your case).
It is based on foreign keys of an entity and works in both directions:
If the context already contains an entity A with a foreign key f to entity B and an entity B is being attached to the context that has a primary key with the same value f as the foreign key in A (i.e. the two entities are related by an FK relationship) then Entity Framework will do the following:
If A has a navigation reference property to B it will assign the attached entity B to this property.
If B has a navigation reference property to A (one-to-one relationship) it will assign A to this property.
If B has a navigation collection property to A (one-to-many relationship) it will add A to this collection in the attached entity B. If the collection is null it will instantiate the collection before adding.
If an entity B is being attached to the context that has a foreign key f to an entity A that the context already contains and that has f as primary key EF will set the navigation properties based on the same rules like above.
As a side note: The fact that relationship fixup is based on foreign keys (they are always loaded when you query an entity, no matter if the FK is exposed as property in the model class or not) is also the reason why relationship fixup does not apply to and does not work for many-to-many relationships because the two entities of a many-to-many relationship don't have a foreign key.
Now, if there are no related StudentModules in your case there is no StudentModule entity that gets loaded into the context and there is nothing what EF could target for a fixup. Keep in mind that the fixup algorithm is not related to a particular query and does not only fix relationships between entities that this query would materialize but it will consider all entities for fixup that the context already contains, no matter how they came into the context. If you would want that collections get instantiated as empty collections EF had run through all attached parent entities of StudentModules and just create an empty collection. It makes no sense to do this during fixup instead of creating empty collections up-front before entities get attached to a context.
I can put a constructor in the poco to initialize the list, which
fixes it, but the this collection is a virtual member in the poco
(based on an EF video!) - surely this is not the way to go?
In my opinion it is the best solution if you don't want to have null collections in your model class instances. It doesn't matter if the collection is declared as virtual (to enable lazy loading) or not. A collection type does not have a derived proxy type, only the instances that get added to the collection are derived proxies. In both case you can just use StudentModules = new HashSet<StudentModule>(); (or List if you prefer).

adding entries to the "Relational" table in entity model? how do i do that?

so the story is very simple.
I have one table called Products and another Called categories. In addition, i have another table called ProductCategories that hold the relationship of catetories to their corresponding products (i.e, the table has two columns, ProductId, ColumnId).
For some reason, after adding all those table to my entity model, i don't have "Access" to it, hence i can do myentityModel.ProductCategories, so i could relational items between those two tables.
And yes, the ProductCategores table is added as "Association" to the entity model. i don't really understand that.
EDIT:
I do see that as part of creating new "Product" i can pass EntityCollection of "Category". So i do query from my entity model for a list of the matching categories that the user selected (on the webpage). so for example, i get (after query the model), an Objectset of "Category". However, i encountered two issues:
the 'AddObject' accept only EntityCollection, hence i need to re-create a set and then add all the objects from the ObjectSet to the entityCollection, in this process i need to detach it from the previous model and add it to the new collection. if not, i get an exception.
when i do the SaveChanges, i see that i get an exception that it was actually trying to Create new Category rather than adding new ProductCategory. again, am i missing something here?
Thanks.
This sounds like a Many-to-Many relationship. In your entity model, you don't need to declare the join table as a separate entity. Instead, you configure the relationship between the Products and the Categories as a Many-to-Many and add metadata about the join table. In Hibernate, you would have:
#ManyToMany(targetEntity=Categories.class, cascade={CascadeType.ALL}, fetch = FetchType.LAZY)
#JoinTable(name="tb_products_categories",
joinColumns=#JoinColumn(name="category_id"),
inverseJoinColumns=#JoinColumn(name="product_id")
)
#IndexColumn(name="join_id")
public List<Categories> getCategories() {
return categories;
}
When you query, the ORM layer takes care of determining SQL and traversing table joins.

Adding object to navigation property collection creates new entity

I am using Entity Framework 4.
I am trying to associate a new entity with an existing entity. The system ends up creating a new child entity when in fact I just want to add a reference to the child object to the parent.
There is a many to many relationship between the two entities so I cannot simply set the FK property of the parent entity. I have tried parent.ChildCollection.Add(child) which simply creates a new child object in the database. This is what I am trying to avoid.
I must be doing something obviously wrong.
thanks
updated code sample
Code sample for my Self-Tracking-Entities that I have to do client side
Right now I have something like this to get all children from server then loop through to find the one i want, then add it to the object collection
List<Service.Child> childs = _client.GetChildren();
I have to loop through that collection to find the right one to add to the parent.childs collection ie.
List<Service.Child> childList = new List<Service.Child>();
foreach (Service.Child child in childList) {
if (child.ChildId == childId)
childList.Add(child);
}
contact.Childs = childList;
If an entity originally came from the database and has its own EntityKey properties populated, using Add to link it to another entity will change its EntityState to Added. Even though it is a preexisting entity, SaveChanges will create an insert command for this entity. You should consider using Attach instead:
parent.ChildCollection.Attach(child);
Using the Attach method, you can define relationships between entities that already
exist in the ObjectContext but that have not been connected automatically.