Using immutable, deduplicated EntityTypes in EF - entity-framework

Suppose I have a CRUD application that lets a user manage their album collections:
class Collection
{
int Id;
string Name;
List<Album> Albums //EF navigation property
}
class Album
{
int Id;
List<CdCase> Cases; //EF navigation property
string Name;
string Artist;
}
In this application, I let users add, edit, and delete albums to their collections. The collections and albums are stored in a SQL Server database using Entity Framework.
Naively implemented, there are going to be a lot of duplicate albums, so I'd like to do the following at collection-save time:
Deduplication: If an album already exists (as determined by Name/Artist equality), the collection uses that album instead of creating a new one
Immutability: If an album is edited, the edits are not persisted to the server. Instead, the album is removed from the case and a new one is created/linked.
Garbage Collection: If an edit/delete operation results in an album no longer being in any collection, it is deleted from the database entirely.
Is there a way to implement this logic at the DBContext level (i.e. changing the Set<Collection> behavior), rather than manually cleaning up the albums before submission?
EDIT I am using code-first EF 6.1.

Related

How to cascade delete document in mongodb?

I have user and photo documents in Mongodb. Each photo belongs to user and a photo maybe shared among users. Lets say user1 has p1,p2,p3 photos and user2 has p3,p4,p5 photos. If I delete user1 (manually using tools like Compass), p1 and p2 should also be deleted but not p3. How to achieve this and what kind of database structure I need to define?
Currently if I delete user1, no photos are deleted and remain in databse which now makes the database corrupted from the point of view of the application using the database.
Its Spring Boot app and User and Photo are declared as:
import lombok.Builder;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Document;
#Document
#Data
#Builder
public class User {
#Id
private String id;
#DBRef
private Set<Photo> photos;
private String name;
}
#Document
#Data
#Builder
public class Photo {
#Id
private String id;
private String fileName;
}
As mentioned by m4gic and in the questions he linked (here and here), MongoDB doesn't support cascading deletes. In your situation you should probably create an array in the User object, and put the complete child documents into that array instead of keeping them in their own collection. That way they will be deleted together with the parent, because they are a part of it.
MongoDB doesn't support for cascade delete as of now. As you are already storing the ref photos in the User model, you can get the photo ids from the reference list and delete the photos together. Or instead of storing the photos in the separate collection you can have the array of Photos embedded into the user object.
You can refer to this link as well:
What is the recommended equivalent of cascaded delete in MongoDB for N:M relationships?
#mindcraft is right, but if you wanted to keep photos in separate collection then you can add access property to the photo document like
{
ref: 'https://....',
access:[user1._id, user2._id]
}
You can then query like -
db.photos.find({access:{$in:[user1._id]}})
Although separate collection specifically for photos will not help a lot. Instead try putting photo urls in array

Attaching an related entity to new entity, without retrieving from database

I've just started working with Web API this week, and I'm struggling with something which I think should be quite simple, but haven't been able to find the answer for yet. Perhaps I'm searching using the wrong terms.
One of the calls to the API passes through a GUID. I need to create a new entity (using Entity Framework) and set one of the relations to this newly passed in GUID. This GUID is the ID of a different entity in the database.
I'm struggling to attach the entity via the relation without fetching the whole entity too.
For example,
public void DoWork(IList<Guid> userGuids)
{
Order order = new Order() // This is an entity
{
CreateDate = DateTime.Now,
CreatedBy = "Me",
Items = (from i in this.Model.Items
where i.Id == userGuid
select i).ToList<Item>();
}
Model.Orders.Add(order);
Model.SaveAll();
}
In the above, I have to do a database call to attach the Item entities to the Order. Is there not a way around this? Seems very redundant to retrieve the whole entity objects when I only require their IDs (which I already have anyway!)!
One solution is stub entities as asked here: Create new EF object with foreign key reference without loading whole rereference object
Link to the source blog referenced: http://blogs.msdn.com/b/alexj/archive/2009/06/19/tip-26-how-to-avoid-database-queries-using-stub-entities.aspx
Snip from the blog - to be applied to your situation:
Category category = new Category { ID = 5};
ctx.AttachTo(“Categories”,category);
Product product = new Product {
Name = “Bovril”,
Category = category
};
ctx.AddToProducts(product);
ctx.SaveChanges();
This way (in the example) the Product is saved without ever loading the Category object.

Maintain Many to Many References

I have a product that I am trying to associate categories to. The list of categories is static. I have set up a bi-directional many-to-many relationship up between Product and Category using Set<?> properties like so:
class Product {
#ManyToMany
public Set<Category> categories;
}
class Category {
#ManyToMany(mappedBy = "categories")
public Set<Product> products;
}
I would like certain users to maintain this relationship, but the only previous way I have seen is to just use a List<Long> to pass back to the controller and add appropriately. This works fine until the user needs to edit these mappings. I have tried clearing the relationship, but that doesn't prove to be simple either.
Is there a decent way to maintain this relationship? If my only option is to "loop and delete" the references, can someone point me in the right direction how to do so appropriately? So far my failed attempts look like this:
for(Category category : product.categories) {
category.products.remove(product);
}
and
Category.delete("categories.id = ?", product.id)
Maintaining the relationship: Yes, passing the IDs to the controller and fetching the entities there is okay.
The relationship proper, there are some things to note:
First, you need to set the cascade annotation, without it nothing in the assocation will get deleted:
#ManyToMany(cascade=CascadeType.ALL)
public Set<Category> categories;
Second, one Entity is the owner of the relation. In your case it's correctly set as the Product class (as the Categoryclass uses mappedBy). Updates only reflect when done on the owner, so to remove all categories from a product you would do
products.categories = new Set<Product>();
if you want to remove a single categorie, just remove it from the products.categories.

A select JPA query that ignores the related entities of one-to-many relation, is that possible?

I am new in JPA, so excuse me if my question seems basic.
I have an entity called User, which is related to a list of other entities like follow:
#OneToMany(cascade = CascadeType.ALL , mappedBy = "user")
private List<session> sessionList;
In a controller class, I defined a find method in a RESTFull manner like follow:
#GET
#Path("/Users")
#Produces("application/json")
public List<UserDevice> findAllUsers()
{
return em.createQuery("SELECT u FROM User u").getResultList();
}
The returned result contains all the sessions of the users which is normal, but make the result huge though I just want to retrieve the basic information of the users (all the simple columns).
My question is: is it possible to ignore the related entities and just keep the columns of the actual entity? Thank you very much
Unless you explicitely map the association as eagerly loaded (using #OneToMany(fetch = FetchType.EAGER)), the above query should only return the fields of the users, and should not load their sessionList.
If the sessions are loaded, then the association is marked as eagerly loaded, or you load them lazily by calling a method of the List<Session>.

Complex Many-to-Many JPA CriteriaQuery

I have two entities, Ablum and Image, which are in many to many relationship.
I wanna make a criteria query that to get all Albums and the counts on how many Images they have.
I don't want to get all Albums first then loop the result to get the counts as there would be so many sql requests.
I've been working for 2 nights and complete lost. If cannot find a way out maybe I need to fallback to use SQL.
Thanks to digitaljoel's inspiration, I found that CriteriaBuilder has a method call "size" that can be put on collections. Below is the code:
CriteriaBuilder cb = getCriteriaBuilder();
CriteriaQuery<Object[]> query = cb.createQuery(Object[].class);
Root<AlbumEntity> albums = query.from(AlbumEntity.class);
query.select(cb.array(albums.get(AlbumEntity_.id), cb.size(albums.get(AlbumEntity_.images))));
query.groupBy(albums.get(AlbumEntity_.id));
Here the groupBy call is a must otherwise error will occur.
But this method is to load the IDs of AlbumEntity, not the entity itself. The Album entity can be load if below code is used:
query.select(cb.array(albums, cb.size(albums.get(AlbumEntity_.images))));
query.groupBy(albums.get(AlbumEntity_.id), ...);
The groupBy must include all properites of the album entity. And it still does not work if the album entity has blob type property.
I'm going to have to make some assumptions since you haven't posted your JPA mapping, so I'm assuming each album has a List<YourImageClass> images for the many to many mapping. With that, something like this would work.
select a, size(a.images) from Album a
That would return a List<Object[]> where List.get(i)[0] would be the album and List.get(i)[1] would be the corresponding size of the image collection.
Alternately, you could define a simple bean to select into. Something like
public class AlbumResult {
private Album album;
private Integer imageCount;
public AlbumResult( Album a, Integer size ) {
album = a;
imageCount = size;
}
// getters and setters here
}
Then you could do
select new AlbumResult(a, size(a.images)) from Album a;
I never deal with criteria queries, but the JPQL is simple enough it should be trivial to translate it into a criteria query.