Must I create a mapping relationship between two entities in hibernate? - spring

For example, if you have an order table in Hibernate and a product table that receives an order, it is mapped as a one to many relationship.Then,Must I write the mapping relationship in code here? In my project, I permanently store order information in a database I have, but in that case, does it need to be a mapping relationship?There's nothing else to do except delete cascade i think.

If you want to use the association in your business code, you also need to model in your domain model. In the described example, I would expect a many-to-many association between the Order and the Product entity. You could model it as a uni- (= only on 1 entity) or bidirectional (= on both entities) association.
Here is a quick example. I provide a very detailed description of all kinds of associations in my guide to association mappings.
public class Order {
#ManyToMany
private Set<Product> products;
...
}
public class Product {
#ManyToMany(mappedBy = "products")
private Set<Order> orders;
...
}

Related

How can I get an Entity with its referenced entity ids in a ManyToMany relation?

I have an basic spring application that uses hibernate and mapstruct
There are two Entities, each are implemented to have their subchild entities as List attribute in a ManyToMany relation
So there is
EntityA.class
with List<EntityB> (fetchType Lazy)
and vice versa
Now when my client calls, it wants to get a DTO that represents like following:
EntityADTO
with List<Long> entityBIds
How can I get my EntityA with only the Ids of EntityB most efficient and without loading the complete EntityB and post process it after?
Thanks a lot!
The #ManyToMany association information is persisted in a dedicated (join-)table and is loaded lazily on collection access, so there needs to be another query.
Instead of querying for the complete information of all associated entities, you could specifically query only for the needed id property.
Possible queries could look e.g. like this:
// Spring-Data repository (requires an extra interface for the result):
interface IdOnly(){
Long getId();
}
interface EntityBRepository extends JpaRepository<EntityB, Long> {
List<IdOnly> getIdByEntityAId(Long enitityAId);
}
// alternative JPQL query (does not need the interface):
#Query("SELECT b.id FROM EntityB b JOIN b.entityAs as a WHERE a.id=:entityAId")
List<Long> getIdByEntityAIdJpaQuery(#Param("enitityAId") Long enitityAId);
This way, only the needed EntityB ids for an associated EntityA are loaded from the DB.
For even further tuning, one could also write a native query directly accessing only the join-table, which avoids all joins:
#Query(nativeQuery = true, //
value = "SELECT entityBId FROM entityA_entityB WHERE enitityAId=:enitityAId")
List<Long> getIdByEntityAIdNative(#Param("enitityAId") Long enitityAId);
For executing the query when mapping with mapstruct, you can use the spring repository bean e.g. as described here: https://stackoverflow.com/a/51292920
In addition to #Fladdimir's answer which is a great approach if you only need the list of values occasionally, JPA allows defining Entity Graphs that can specify what in an object graph you want loaded. This can allow you to define your entity and specific attributes from child/referenced entities in the graph, allowing objects to be returned but the bulk of the data unfetched. This can allow you to process Entity B instances, but without them being fully populated.
There are many tutorials but I've referenced https://www.baeldung.com/jpa-entity-graph more than once. As the tutorial referenced mentions though, Hibernate might have some issues with how it handles attributes that are normally eagerly fetched, so it might not work the way you want (but will with other JPA providers like EclipseLink, which is where I've used this).
Alternatively, if this is a collection of IDs you are going to want/need frequently, you can modify your object model to have them fetched differently.
public class EntityA {
..
#ElementCollection
#CollectionTable(name = "RELATION_TABLE_NAME", joinColumns = #JoinColumn(name = "A_ID", insertable=false, updatable=false))
#Column(name = "B_ID", insertable=false, updatable=false)
List<Long> bIds;
}
This allows fetching the foriegn keys automatically in your AEntity. I've made it read-only, assuming you'd keep the existing A->B relationship and use that to set things. Doing so though means that these two relationships are entirely separate, and so might result in different queries to fetch this same set of data.
If that is a concern, you can alter things again, and remove the existing A->B relationship, and stick it in an intermediary object AB.
public class EntityA {
..
#ElementCollection
#CollectionTable(name = "RELATION_TABLE_NAME", joinColumns = #JoinColumn(name = "A_ID"))
List<AB> listOfBs;
}
#Embeddable
public class AB {
#Column("B_ID", insertable=false, updatable=false)
Long bId;
#ManyToOne(fetch=LAZY)
#JoinColumn(name = "B_ID")
B b;
}
This would allow you to fetch As and use B's ID values without having to fetch from the B table. Note that I've marked the basic bId property as read-only, assuming that your existing app would be setting things by assigning a B reference to the relationship, but you could mark the relationship as read-only instead, and set the FK value using the bId. This might be more efficient long term, as you don't have to look up the B instance to set the relationship.
Alternatively again, you can make AB an entity instead of an embeddable, and allow it to exist and be queried upon outside of As and Bs. There are quite a few options though, and ways to map it, and not likely necessary for a simple model and use case.

Fetch a parent by a child in Many-to-Many unidirectional relationship JPA

I have two entities Estate and PropertyTags in a Spring Boot application. The Estate entity has a many-to-many relationship with the PropertyTag (PropertyTag is also used by other entities)
This is the Estate entity:
#Entity
public class Estate{
#Id
private Long id;
.
.
#ManyToMany
private Set<PropertyTag> propertyTags;
.
.
// other properties
}
And the PropertyTag class:
#Entity
public class PropertyTag{
#Id
private Long id;
private String tagName;
// getters and setters
}
The above relationship created 3 database tables with one table for foreign keys of the relationship.
I need a repository method (or query) that will retrieve an Estate that will take and argument of an estate Id and property tag object.
I tried using the hibernate keywords as below:
public interface EstateRepository extends JpaRepository<Estate, Long> {
Optional<Estate> findByIdAndPropertyTagsContaining(Long estateId, PropertyTag childTag);
}
But that did not work.
I do not want to retrieve an estate via its ID and manually loop through its property tags to check if a tag exists in its collection. I feel this can be done with a query of the database
I am not so good at writing custom queries. I need help with the query to do that.
Thank you.
To get an Estate entity by the PropertyTag entity you can also just use the id of the PropertyTag and try
Optional<Estate> findByIdAndPropertyTags_Id(Long estateId, Long propertyTagId);
Which should return the Estate containing a tag with the given ID.
Containing is used for String searching

Many to one mapping Hibernate

I am doing many to one mapping in hibernate. I am using the existing tables which I created earlier for one to many mapping (customer and order) but when I am trying to map and update those table I couldn't able to I don't know how should I processed? and I would like to insert the data meaning I would like to create some more orders using command line runner for that customer.
Could you please help me with this
Appreciate your help.
Mapping one-to-many and many-to-one association
Both associations are the same association seen from the perspective of the owing and subordinate entities and respectively.
Student one-to-many Address
Address many-to-one Student
#OneToMany annotation can be applied to a field or property value of "one" end entity class for a collection or an array representing the mapped "many" end of the association.
#ManyToONe relationship between two entities is by managing the FK(Foreign key) of the "one" end entity, as a column in the "many" entity table.
> **Bidirectional one-to-many using ```#JoinColumn```**
#Entity
public class Student{
#OneToMany(cascade = CasecadeType.ALL)
#JoinTable(name="Student_FK")
public set<Address> getAddress(){
return address;
}
}
One-to-Many side as the owing side, You have to remove the mappedBy element and set the #ManyToOne #JoinColumn as insertable and updatable to false. This Solution is not optimized and will produce some additional UPDATE Statement.
#Entity
public class Address{
#ManyToOne(cascade = CasecadeType.ALL)
#JoinColumn(name="STUDENT_FK", insertable = false, updatable = false)
public Student student;
}
For more details look at this link Link

Hibernate populate parent id to child during saving

I have an entity Order who declares #ManyToMany relationship.
#ManyToMany is represented by a separate entity with a link to Order.
When I create the instance of Order I can't save #ManyToMany because I'm getting order_id cannot be null.
So I saved Order entity separately from #ManyToMany, created #ManyToMany object, then saved it through own repository, but now I can't attach this #ManyToMany to the Order object.
And when I find Order - it has a null collection of #ManyToMany.
How to update Orders with #ManyToMany?
Your description is unclear, but I'll assume you need to make sure to set your associations right before saving.
Let's assume your other class is called OtherClass. It should have a method called "addOrder", that you need to call before saving it with a repository/dao.
#Entity
public class OtherClass {
#ManyToMany
private List<Order> orders;
public void addOrder(Order order) {
orders.add(order);
order.getOthers().add(this);
}
}

Spring Data Repository query hook

In Spring Data is it possible to extend a query that is generated by the find* functions of the repo interfaces?
Given following use case:
My legacy database has an inheritance by table. So given following
#Entity public class Person {
private int id;
private String className;
}
#Entity #PrimaryKeyJoinColumn(name="id") public class Musician extends Person {
String instrument;
}
#Entity #PrimaryKeyJoinColumn(name="id") public class Lawyer extends Person {
String discipline;
}
My repository for Musician:
public interface MusicianRepository extends CrudRepository<Musician, int> {
List<Musician> findAll();
}
Now an entry for a new musician in SQL would be:
insert into Person(id, className) values(1, 'Musician');
insert into Musician(id, instrument) values(1, 'piano');
When a Musician got migrated to a lawyer the old system added one row to Lawyer table without removing Musician by:
insert into Lawyer(id, discipline), values(1, 'finance');
update Person set ClassName = 'Lawyer' where ID = 1;
My MusicianRepo would now find the lawyer since the row in Musician still exists.
I would need some kind of post processor where I could extend the query by adding a where clause with "ClassName = 'Musician'" on all find* methods.
Is this possible somehow?
I think that your JPA mapping is just not correct in terms of inheritance.
I think you want to have "Joined, Multiple Table Inheritance"
Citing from here:
Joined inheritance is the most logical inheritance solution because it
mirrors the object model in the data model. In joined inheritance a
table is defined for each class in the inheritance hierarchy to store
only the local attributes of that class. Each table in the hierarchy
must also store the object's id (primary key), which is only defined
in the root class. All classes in the hierarchy must share the same id
attribute. A discriminator column is used to determine which class the
particular row belongs to, each class in the hierarchy defines its own
unique discriminator value.
Some JPA providers support joined inheritance with or without a
discriminator column, some required the discriminator column, and some
do not support the discriminator column. So joined inheritance does
not seem to be fully standardized yet.
The className column in Person would be your descriminator column. It determines the subclass to instantiate.
Your mapping would be something like this:
#Entity
#Inheritance(strategy=InheritanceType.JOINED)
#DiscriminatorColumn(name="className")
public class Person {
private int id;
private String className;
}
#Entity
#DiscriminatorValue("Musician")
public class Musician extends Person {
String instrument;
}
#Entity
#DiscriminatorValue("Lawyer")
public class Lawyer extends Person {
String discipline;
}
This way if you query for Lawyer entities JPA would automatically add the where clause to just read rows with className=Lawyer
I did not try the mapping - it should just illustrate the way you should be going.

Resources