#Embeddable or #Entity which one should we choose - spring

Let's say I have an entity Offer, that has many prices.
#Entity
#Table(name = "offer")
data class Offer(
val prices: List<Price>,
#Id
val id: Long? = null
)
Does it make sense to make Price an Entity or it's better to mark it as Embeddable? What in case there will be an offer with the same price? Perhaps we could reuse the price, or shouldn't we?
I'm looking for a good explanation of when we should use Entity and when Embeddable. Perhaps we could use it as an Entity but set the orphanRemoval = true, so the child in this case Price will be always removed together with an Offer.

I wouldn't definitely try to reuse prices between Offers. The reason is that you might change that price for whatever reason and it will inadvertently change for all Offers. To me this is just asking for troubles and bugs to appear. Unless there is a very good reason in terms of your business case that really makes it mandatory for doing so, I wouldn't do it.
Having written this, with the information that you gave I would definitely go with #Embeddable because it does seem to be. very beneficial to have a separate entity and table for the Price.

Related

how to make a spring jpa/correct repository when having mandatory relationships?

I have the following database which allows users to rent books in a book shop:
The entity class Book needs to have a Category as well as BookDescription when saved.
Those Book class looks like this:
#Entity
#Table(name = "books")
#Inheritance(strategy = InheritanceType.JOINED)
public abstract class Book {
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "book_description_id")
private BookDescription bookDescription;
#ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
private Category category;
//omitted fields, getters, setters & other relations
}
I was checking out Spring Data JPA. It has a signature of
CrudRepository<T, ID extends Serializable>
which means that I will have
CrudRepository<Book, String>
but how will I save a book?
So, generally, the user will submit a form, I will bind a BookUIObject which will contain all the details needed to save a book, pass it to a BookService witch will extract from it 3 objects: Book ( a specific implementation), Category and BookDescription. The question is will the service hook up the book relations and call the general repository.save(Book) or it will call a method like repository.save(Book, Category, BookDescription)?
Also, should I bind directly the data from the user into entity classes, or do like I said, bind to a general BookUIObject and let the service extract from it the entity classes?
Kind regards,
Typically you will have to call BookReporitory.save(book). Book has cascaring Persist for both of the relations, so if you have set the BookDescription and the Category on the book instance you save, they will also be persisted. If you didn't have cascading persist, you would have to save them using their JPARepository (unless they already existed in the Persistence context).
One thing that is important to understand in this example is that if you create a new category object and set it on a book and save the book a new category is created in the DB. So if the UI posts category=sic-fi, you have to check if the category already exists, if it does then you must used the managed category, and set that on the book rather than creating another "sci-fi" category. This is the reason I would not have cascading persist on the Category relation, because I would rather have a constraint violation because a category didn't exist, instead of an new category sci-if when someone miss spelled it in the UI.
I do not recommending binding forms directly to JPA entities, because you always need to fetch entities from JPA, as you have to use the managed versions, so in my experience it is better to have another set of beans for form binding.
Another thing that jumps out if the lack of nullable=false in #JoinColumn. If a book can't exist without being in a Category it is vital that this is communicated to the database, and if you generate tables from the JPA metadata model, this is how it is done. If I could give only one recommendation when working with databases/JPA it is to be overzealous with NOT NULL. It is a 100 time easier to get a constraint violation when you insert, than to get a NullPointerException later and have to check every possible code-path that could end up calling save and checking if the argument could be null.
In addition I would recommend that you set of some time to understand the concept of the EntityManager and the Persistence Context, most of the mistakes/assumptions developers make come back to the persistence context and how the 4 entity states work.

Relate two entities via a third one

I am trying to figure it out how to relate two entities using another one they both have in common. My model is the following:
A Customer has a list of Codes
A Promotion has a list of Codes
A Code has a list of Customers and a list of Promotions
I would like to add to the Promotion Entity a list of Customers. This list would include all customers who at least have the same codes as in the promotion (can have more). Ideally I would like to model this a field but I am not sure if this is possible. I have found no information.
Can it be done?
Thanks,
You can define
Customer Entity.
Code entity which has #ManyToMany relationship with Customer. ( This is required if customer can have code even when there is no promotion.)
Promotion Entity which has #ManyToMany relationship with Customer
and #ManyToMany with Code.
At the end I ended up doing the following
public Set<Customers> getCustomers(){
Set<Customers> customers = null;
for (Code c : codes)
if(customers == null)
customers = c.getCustomers();
else
customers.retainAll(c.getCustomers());
return customers;
}
I do a Eager retrieve for the Codes, and in the codes an Eager retrieve for the Customers. So far is performing well, but I would prefer this to be all calculated in the DBMS and not on the controller side. Still have not find a nice way to to it.

Hibernate: Child table having two different ManyToOne relationships

In the Spring/Hibernate/Java/Tomcat app I'm writing I have a OneToMany relationship between an Organization and its Contacts.
Organization 1:M Contact (has foreign key org_id)
In Organization I have this field:
#OneToMany(mappedBy="organization")
private List<Contact> contacts;
In Contact I have this field:
#ManyToOne
#JoinColumn(name="org_id")
private Organization organization;
All is working OK so far. Now I'm adding the concept of an Offer. The Offer can be made by an Organization, and you speak with the designated Contact for that particular Offer.
Offer has foreign keys for its organization (org_id) and designated contact (contact_id).
So far, the Offer would look like:
#OneToOne
#JoinColumn(...)
private Organization offering_org;
#OneToOne
#JoinColumn(...)
private Contact offering_contact;
Here comes the point of my question. I've already annotated the Contact class for use with Organization. If I try to persist the Offer object in the usual Hibernate way, I'll need to store copies of an Organization object and a Contact object into the Offer object. This seems to conflict with my existing Organization : Contact use of the two Java classes. For example, if I've a 1:1 with Offer, if I put this into the Contact class do I get an optional use of either or a mandatory simultaneous use of both?
Since the Offer is yet another relationship, do I need to write a data transfer object version of Contact for use in the Offer relationship?
Thanks,
Jerome.
Perhaps I do not fully understand the problem but I'd just do something like this:
// contact & organization being already persisted entity objects
Offer offer = new Offer();
offer.setOffering_org(organization);
offer.setOffering_contact(contact);
// Persisting the new Offer object to the database,
// implicitly making the relations.
service.saveObject(offer);
I see no reason to create copy(s) of the organization object?
It just happens to be that the collection of "contacts" in the Organization object can also be a Contact within one or more Offer objects.
I'm thinking that my original question is kind of stupid. What I did try is to put this in Offer.java:
#Column(name="org_id")
private Long orgId = null;
#Column(name="contact_id")
private Long contactId = null;
I fill orgId manually because an offer is always tied to the user's Organization. It is a hidden field in the web page.
I put a SELECT filled with appropriate Contact objects (contact.id, contact.name) in the web page.
When the web page is submitted the Offer's orgId and contactId fields are filled in the #ModelAttribute parameter. This takes me where I want to go.
To address the comments of Mr. mspringer, your example could work (you illustrated a "create new" situation) if I were willing to use an Organization or Contact list in my Offer object. It is also somewhat the topic of my original question. But since I see that I don't really want to play with the expanded objects within Offer, nor do I wish to, I can avoid the topic of my original question.
Thanks to all who looked at my exercise in confusion.

Automatically inversing relations in Doctrine2.x

In Doctrine 1.2, you used to be able to automatically define a collection on the opposite side of a relation by defining it only on the owning side. The other side needn't know about the class that was relating to it.
Is it possible to do this in Doctrine 2.x without having to create a variable and mapping on the owned side?
While it probably is allowed, I woudn't do that. Take this example:
1) Category has Many products. So you fetch a page with most-sold products and show its Category name; you must have that relation on Products side.
2) Let's say that Category can be deleted only if there are no products. So you would have a method:
public function isDeletable()
{
return count($this->getProducts())==0 ;
}
I am sure better examples could be made, I just made it quick&dirty. My suggestion would be to always put both sides because you will never know when it will become handy, and it also makes consistency in your entities.

preventing OpenJPA N+1 select performance problem on maps

When I have an entity that contains a Map, e.g.
#Entity
public class TestEntity {
#ElementCollection(fetch = FetchType.EAGER)
Map<String, String> strings = new HashMap<String, String>();
}
and I select multiple entities (SELECT z FROM TestEntity z), OpenJPA 2.0 performs one query for each TestEntity to fetch the map, even though I used FetchType.EAGER. This also happens when the Map value is an entity and I use #OneToMany instead of #ElementCollection. In principle this can be done more efficiently with one query that selects all the map entries for all returned TestEntities. For Collection-valued fields OpenJPA already does this by default (openjpa.jdbc.EagerFetchMode" value="parallel") but it seems to fail on this simple entity. (Same problem with value="join").
Could I be doing something wrong? Is there an easy way to tell OpenJPA to not perform a query per entity but only one?
Or is there already any work planned on improving this (I filed it under https://issues.apache.org/jira/browse/OPENJPA-1920)?
It is a problem for us because we wish to fetch (and detach) a list of about 1900 products which takes almost 15 seconds with OpenJPA. It takes less than a second with my own native query.
Having to write only one native query wouldn't be much of a problem but the map we use is inside a reusable StringI18N entity which is referenced from several different entities (and can be deep in the object graph), so native queries are a maintenance headache.
Any help getting performance up is greatly appreciated.
EDIT: explicitly using JOIN FETCH does not help either:
"SELECT z FROM TestEntity z JOIN FETCH z.strings"
OpenJPA's TRACE still shows that it executes one SQL statement for each individual TestEntity.
It might be a pain (correction: I know it'll be a pain) but have you tried actually mapping your 2-field TestEntity as a full JPA-persisted #Entity?
I know that Hibernate used to treat #ElementCollections rather differently to #OneToManys for example - OpenJPA could well be doing something similar.

Resources