JPA: Is it possible to persist child objects in the array only when the main object is persisted? - jpa

I am developing a web application using JSF and JPA(Eclipse link). I have two entities with bidirectional OneToMany relationship. The owner entity is contact and target entity is customer. Single customer can have multiple contacts, like email, phone, etc. When the end user is adding a new customer, he also adds the contacts straight away. There is a need to cancel the saving of a new customer, even after adding contacts to that customer. I tried to add that functionality, but failed in the following way.
Can that senario be achieved directly by persistence?
Contact Entity
....
public class Contact implements Serializable {
....
#ManyToOne
Customer customer;
....
Customer Entity
....
public class Customer implements Serializable {
....
#OneToMany(mappedBy = "customer")
private List<Contact> contacts;
....
Adding a new contact to Customer (current is an object of Customer class)
Contact contact = new Contact();
contact.setCustomer(current);
....
current.getInstitutionContacts().add(contact);
This works when the current is already a persisted one. If I tried to add a contact to yet to persist one, there is a java.lang.NullPointerException.
I can work around to achieve the functionality, but is there any way we can just collect the contacts to the array and persist them only when (and if only) the customer is persisted? By using cascade persist or lazy fetch, etc?

Sounds like you want Contacts to be Components, not Entities.
The difference is that an entity has it's own lifecycle; it lives outside the scope of its association, and deleting the parent does NOT necessarily have to delete the child. Also, if a child is an Entity, other classes can also have relationships with that child.
Components are completely bound to the parent. They automatically go away if the parent goes away. They cannot be referenced by other associations or by other Entities. It's like they are simple properties of the parent class.
The only caveat is that I don't know if all JPA implementations support having a collection of components.
See this documentation. Particularly the part that says: "You can also use association annotations in an embeddable object (ie #OneToOne, #ManyToOne, #OneToMany or #ManyToMany). To override the association columns you can use #AssociationOverride."
If the JPA implementation you are using does, you can use the #Embeddable annotation and #OneToMany
Edit: -- I also found info here http://en.wikibooks.org/wiki/Java_Persistence/Embeddables#Collections.

Related

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

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.

Delete a child from an aggregate root

I have a common Repository with Add, Update, Delete.
We'll name it CustomerRepository.
I have a entity (POCO) named Customer, which is an aggregate root, with Addresses.
public class Customer
{
public Address Addresses { get; set; }
}
I am in a detached entity framework 5 scenario.
Now, let's say that after getting the customer, I choose to delete a client address.
I submit the Customer aggregate root to the repository, by the Update method.
How can I save the modifications made on the addresses ?
If the address id is 0, I can suppose that the address is new.
For the rest of the address, I can chose to attach all the addresses, and mark it as updated no matter what.
For deleted addresses I can see no workaround...
We could say this solution is incomplete and inefficient.
So how the updates of aggregate root childs should be done ?
Do I have to complete the CustomerRepository with methods like AddAddress, UpdateAddress, DeleteAddress ?
It seems like it would kind of break the pattern though...
Do I put a Persistence state on each POCO:
public enum PersistanceState
{
Unchanged,
New,
Updated,
Deleted
}
And then have only one method in my CustomerRepository, Save ?
In this case it seems that I am reinventing the Entity "Non-POCO" objects, and adding data access related attribute to a business object...
First, you should keep your repository with Add, Update, and Delete methods, although I personally prefer Add, indexer set, and Remove so that the repository looks like an in memory collection to the application code.
Secondly, the repository should be responsible for tracking persistence states. I don't even clutter up my domain objects with
object ID { get; }
like some people do. Instead, my repositories look like this:
public class ConcreteRepository : List<AggregateRootDataModel>, IAggregateRootRepository
The AggregateRootDataModel class is what I use to track the IDs of my in-memory objects as well as track any persistence information. In your case, I would put a property of
List<AddressDataModel> Addresses { get; }
on my CustomerDataModel class which would also hold the Customer domain object as well as the database ID for the customer. Then, when a customer is updated, I would have code like:
public class ConcreteRepository : List<AggregateRootDataModel>, IAggregateRootRepository
{
public Customer this[int index]
{
set
{
//Lookup the data model
AggregateRootDataModel model = (from AggregateRootDataModel dm in this
where dm.Customer == value
select dm).SingleOrDefault();
//Inside the setter for this property, run your comparison
//and mark addresses as needing to be added, updated, or deleted.
model.Customer = value;
SaveModel(model); //Run your EF code to save the model back to the database.
}
}
}
The main caveat with this approach is that your Domain Model must be a reference type and you shouldn't be overriding GetHashCode(). The main reason for this is that when you perform the lookup for the matching data model, the hash code can't be dependent upon the values of any changeable properties because it needs to remain the same even if the application code has modified the values of properties on the instance of the domain model. Using this approach, the application code becomes:
IAggregateRootRepository rep = new ConcreteRepository([arguments that load the repository from the db]);
Customer customer = rep[0]; //or however you choose to select your Customer.
customer.Addresses = newAddresses; //change the addresses
rep[0] = customer;
The easy way is using Self Tracking entities What is the purpose of self tracking entities? (I don't like it, because tracking is different responsability).
The hard way, you take the original collection and you compare :-/
Update relationships when saving changes of EF4 POCO objects
Other way may be, event tracking ?

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>.

Entity Framework 4.1: saving many to many relationship saves exsting child entities again in the db

I have a very frustrating problem (for me anyway):
I have a simple scenario where I have a Product entity and a CombinedProduct Entity. There is a many to many relationship between them, so a Product can belong to multiple CombinedProducts and a CombinedProduct can contain many products.
I first create a product and save this to the database. Later on I create a CombinedProduct and add this product. When I try to save this CombinedProduct, the product entity is added again to the database by Entity Framework instead of just adding a relationship... This is really driving me nuts.
I already tried to attach the product again to the context before saving, but Entity complains that it already has a product with the same key...
Below you find the code for all of this (simplified and code stripped):
Product Entity
Public Class SingleProduct
Property SingleProductId As Integer
Property CombinedProducts As ICollection(Of CombinedProduct)
End Class
CombinedProduct
Public Class CombinedProduct
Public Sub New()
Me.Products = New HashSet(Of SingleProduct)()
End Sub
Property CombinedProductId As Integer
Property Products As ICollection(Of SingleProduct)
End Class
Many to Many Relastionship definition
Protected Overrides Sub OnModelCreating(modelBuilder As DbModelBuilder)
modelBuilder.Entity(Of CombinedProduct)().
HasMany(Function(c) c.Products).
WithMany(Function(p) p.CombinedProducts).
Map(Sub(m)
m.ToTable("CombinedProductSingleProducts")
m.MapLeftKey("SingleProductId")
m.MapRightKey("CombinedProductId")
End Sub)
End Sub
Code used for saving
Using myDataContext As New DataContext
myDataContext.CombinedProducts.Add(product)
myDataContext.SaveChanges()
End Using
Already tried attaching before saving but that didn't work
For Each prd In product.Products
If myDataContext.Entry(prd).State = EntityState.Detached Then
myDataContext.SingleProducts.Attach(prd)
End If
Next
One "solution" I found was right before saving, clearing the products list and getting the products again from the database and add those to the CombinedProduct, but that can hardly be the solution.
Hope someone can help me, this is driving me nuts!
(I use Entity Framework 4.1, with Code First)
Edit
Adding the product: This is done in it's own datacontext at some other form:
Using myDataContext As New DataContext
myDataContext.SingleProducts.Add(singleProduct)
myDataContext.SaveChanges()
End Using
The combined product creation:
Dim myCombinedProduct = New CombinedProduct
myCombinedProduct.Products.Add(product)
The product I add is first fetched again in it's own datacontext:
Using myDataContext As New DataContext
Return myDataContext.Products.FirstOrDefault(Function(p) p.ProductId = id)
End Using
Edit
The full story in the hopes of being more clear:
I have a winforms application with two forms: one for managing products and one for managing combined products. The application is N-Tier (User layer, business layer and data layer).
On the form for managing you products you can simply add/update/delete products. On this form everything works fine.
The combined product form is another matter:
when loading the form I retrieve all products from the database, going through the business and datalayer. The function to retrieve the products in the datalayer has it's own DataContext (using block). This function returns an iEnumerable of Products.
The retrieved products are added to a number of comboboxes as objects.
You select the products you want to add to the combined product by selecting them
when saving I create a new CombinedProduct entity in the user layer, retrieve the product objects from the comboboxes and add them to the new CombinedProduct Object
I send the CombinedProduct object to the business layer where I perform a number of business rules
If all is well, the combinedProduct is send to the datalayer, where I try to save it again in it's own datacontext (using-block).
So I have multiple DataContexts as they live and die in the datalayer.
Hope this makes things a bit more clear.
I don't understand exactly in which order you are doing which steps but the right order should be:
If everything happens in one context:
Using myDataContext As New DataContext
Dim product = myDataContext.Products.FirstOrDefault(Function(p) p.ProductId = id)
Dim myCombinedProduct = New CombinedProduct
myCombinedProduct.Products.Add(product)
myDataContext.CombinedProducts.Add(myCombinedProduct)
End Using
Loading the product attaches to the context and avoids the duplication. It must happen before you add myCombinedProduct to the context.
If you load the product in another context:
Using myDataContext As New DataContext
myDataContext.Products.Attach(product)
Dim myCombinedProduct = New CombinedProduct
myCombinedProduct.Products.Add(product)
myDataContext.CombinedProducts.Add(myCombinedProduct)
End Using
You have to attach the product to the new context, again before you add myCombinedProduct to the context.
Edit
If your new myCombinedProduct including the product collection comes into the datalayer in a detached state the following should work:
Using myDataContext As New DataContext
For Each prd In myCombinedProduct.Products
myDataContext.SingleProducts.Attach(prd)
Next
myDataContext.CombinedProducts.Add(myCombinedProduct)
End Using
In EF all entities that you are trying to relate must belong to the same datacontext,other wise it would not recognize your product as an existing one, and would add a new one instead
in your first usings, your product must be fetched from db first.
I never had to attatch/detach entities, but i have seen most people run into issues doing that.
Also in many to many, both entities have to be added to the context, relationship is just going to fill the join table
looking at the discussion it looks like you have a repository for each entity type, and are getting objects through those repository methods. If thats the case, then IMO this design has a problem, since object relationships are stored in a common context.In your case all these repositories should have the same dbcontexts. You might think that going to the database has some performance issues, but you are going to the db even when you attach.