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

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.

Related

#JoinColumn and how it links two tables

I am completely new to working with databases and a beginner to Spring Boot as well really so apologies for any wrong terminology/fuzzy logic. There are some similar questions to this on here but I have not found exactly the answer to what I was looking for so I decided to post. Ramble over.
I am reading an article about joining tables in Spring Boot. They have a teacher class and a course class and it is a one-to-many relationship. They don't have the full classes written out but it says that you can go into Teacher class and do:
#OneToMany
#JoinColumn(name = "TEACHER_ID", referencedColumnName = "ID")
private List<Course> courses;
or go to Course class and do:
#ManyToOne
#JoinColumn(name = "TEACHER_ID", referencedColumnName = "ID")
private Teacher teacher;
What threw me off is that the parameters given to JoinColumn were the same in both cases. Assuming that both tables have something called ID, how does Spring know which one to use? Does it start by looking at both tables, looking for teacher_id. Then, after finding that it switches to the other table to get the ID?
Actually this is in the methodology of One-To-Many mappings in relational databases.
To achieve 1-N relation you only need to have:
Table One -> primary_key
Table Many -> primary_key, fk_one_primary_key
With the above configuration, given a table One entity, you can get all it's Many relations using the primaryKey-foreignKey join, and vice versa.
Now, in the code you've shared, the two parameters are described as:
ID -> Primary key column of table One / Teacher
TEACHED_ID -> foreign key column of table Many / Course

Architecture Domain Model and View Model

I am trying to build application by spring boot and Domain Driven Design. I have a problem about Domain model (match with fields of table DB) and View Model (response API).
Domain Model:
EX:
class Name
#Getter
#NoArgsConstructor
#AllArgsConstructor
class Name {
String value;
}
class Product
#Getter
#NoArgsConstructor
#AllArgsConstructor
class Product{
Name name;
}
ViewModel:
#Data
#NoArgsConstructor
#AllArgsConstructor
class ProductView {
//int prodId;
String prodName;
}
Select data DB by class Product, builder to Response API by class ProductView. When that convert from DomainModel to ViewModel or vice versa, I written static method in ProductView for that.
It will become:
#Data
#NoArgsConstructor
#AllArgsConstructor
class ProductView {
//int prodId;
String prodName;
public static ProductView of(Product prod) {
String productName = prod.getName().getValue();
return new ProductView(productName)
}
}
It works well, but when the data becomes more. I think need that as CommonConvert from DomainModel to ViewModel and vice versa.
I have a solution use Mapstruct library. But Mapstruct only support to convert field same type(String with String, ex). What is the best solution for writting CommonConvert?
My advice: do not query domain models and translate them to view models for reading.
Domain model classes (e.g. aggregates) are used to represent business data and behaviour with the to purpose to adhere to business invariants when creating or changing such business entities.
For building your view models from your persistent data you can - and in my opinion you should - bypass the domain model. You can safely read the data from your database as you need it without going through domain repositories.
This is okay because you can't violate business rules by just reading data. For writing data go through domain repositories and aggregates.
In your case you can of course use view model entities using JPA annotations by designing those classes to exactly fit your viewing requirements. Keep in mind that view models often don't correlate to domain models as they might only need a subset of the data or aggregate data from different aggregates.
Another catch is that if you need to query many objects for viewing can quickly cause performance issues if you query full domain aggregates via repositories. As such aggregates always load all data from it's child entities and value objects as well to allow for performing business logic with all invariants you would end up performing lots of expensive queries which are suited for loading a single aggregate but not many of them at once.
So by querying only what you need for viewing you also address such performance issues.
When following DDD you should usually create or change only one aggregate within a business transaction. So domain models are not suited for query optimization but for keeping the business invariants in tact when writing business data.
View models and corresponding queries are optimized for reading and collecting all data required.
Simply map like this (with mapstruct) :
#Mapping(source = "name.value", target = "prodName")
public abstract ProductView toProductView(Product model);

I am using Spring boot jpa with Restful api services to insert multiple users in array or list

As I am new to Spring boot. I am not at all clear about mappings. By using #Onetomany mapping in one entity and #manytoOne mapping at other entities. Using the controller I have to write REST API functions to insert multiple users at a time inside an array or set. Can anyone please suggest some websites or provide some existing codes?
The #OneToMany and #ManyToOne mappings can be used according to your use-case, whether you need bi-directional mappping or not. For a simple example consider the following :
#Entity
#Table(name="ENTITY_A")
public class EntityA{
//...
#OneToMany(mappedBy="EntityA")
private Set<EntityB> entityBItems;
// getters and setters
}
#Entity
#Table(name="ENTITY_B")
public class EntityB{
//...
#ManyToOne
#JoinColumn(name="entityA_id", nullable=false)
private EntityA entityA;
public EntityB() {}
// getters and setters
}
What you need to look out for is the owning side of the relation indicated by the mappedBy . The owning entity can be used to persist and get the data from the database. But from the description in your question I cannot understand whether you actually need to use mappings at all as you just have to insert multiple users into a table without any relations to another entity. It will be more helpful if you could explain more about your use case and provide code samples for furthur analysis.
For details about the mappings article or article .
Official doc .
MappedBy signals hibernate that the owner of key (relationship) is on the other side.
This means that although you link 2 tables together, only 1 of those tables has a foreign key constraint to the other one.
MappedBy allows you to still link from the table not containing the constraint to the other table.
If you still want use #JoinColumn on both the Entities you can use #JsonManagedReference and #JsonBackReference from com.fasterxml.jackson
To save the multiple records at same time you can use yourRepository.saveAll(entities)

Spring Data- how to tell spring what entities to retrieve

If i have several entites, lets say :
#Entity
class Book{
String name;
Author author;
}
#Entity
class Author{
String name;
City hometown;
}
#Entity
class City{
String cityName;
}
If i want to retrieve all the books, if i use classic JPA Repository and Spring Data and just do a findAll(), it will get me all the books with all the Authors with all their home towns. I know i can use #JsonIgnore, but i think that only prevents whats being returned, not whats being looked up in the database. And also i have methods that DO want to return both books and authors, so #JsonIgnore -ing does not work for me. Is there anything like this ? To tell Spring Data what to look up and what to return ? Any links or guides or methods i don't know of, would be appreciated.
Spring Data has the concept of 'projections' which allow you to return different representations of the same Entity.
Official Documentation:
Spring Data query methods usually return one or multiple instances of
the aggregate root managed by the repository. However, it might
sometimes be desirable to create projections based on certain
attributes of those types. Spring Data allows modeling dedicated
return types, to more selectively retrieve partial views of the
managed aggregates.
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections
Where a Projection is a 'closed' projection (a projection interface whose accessor methods all match properties of the target aggregate) then the documentation notes that additionally:
Spring Data can optimize the query execution [to select only the relevant fields], because we know about
all the attributes that are needed to back the projection proxy
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections.interfaces.closed
Spring Data also allows for Projections to be specified dynamically at runtime. See further:
https://github.com/spring-projects/spring-data-commons/blob/master/src/main/asciidoc/repository-projections.adoc#dynamic-projections
First mark your relations as LAZY
Then specify what data needs to be fetched on a per-query basis.
See for example:
https://vladmihalcea.com/eager-fetching-is-a-code-smell/

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.

Resources