how to add #onetoone mapping for self entity in hibernate - spring

how to add one-to-one mapping for the self entity. Like in this example. I want to have parent-child relationship for the Person itself.
#Entity
#Table(name="PERSON")
public class Person {
#Id
#Column(name="personId")
private int id;
#OneToOne
#JoinColumn()
private Person parentPerson;
}

Here is example of bidirectional self mapping #OneToOne (I change column names to SQL notation):
#Entity
#Table(name="PERSON")
public class Person {
#Id
#Column(name="person_id")
private int id;
#OneToOne
#JoinColumn(name = "parent_person_id")
private Person parentPerson;
#OneToOne(mappedBy = "parentPerson")
private Person childPerson;
}
But, I don't understand why you want to use #OneToOne in this case.

I am using it like this:
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "PARENT_ID", nullable = true)
private Person parent;
In order to add parent from your service layer, you need to already have at least one Person in the database.
Lets presume you do. Then create a new person. For e.g.:
#Transactional
public void createPerson() {
Person parent = //get your parent
Person child = new Person();
if (parent != null) {
child.setParent(parent);
}
}
If this is what you mean..

Related

Automatic JPA refresh ManyToOne objects with #Version feature

I'm getting an exception:
org.hibernate.TransientPropertyValueException:
object references an unsaved transient instance
- save the transient instance before flushing :
com.example.jpamapstruct.entity.Member.club ->
com.example.jpamapstruct.entity.Club
while saving the member entity:
#Transactional
public MemberDto save(MemberDto memberDto){
Member entity = memberMapper.toEntity(memberDto);
return memberMapper.toDto(repository.save(entity));
}
How to fix this case in a proper way?
Possible solution:
I can get and set a club object before saving a member but is it only one and the best approach in such scenario?
Member entity = memberMapper.toEntity(memberDto);
clubRepository.getReferencedById(memberDto.getClubId()).ifPresent(entity::setClub);
return memberMapper.toDto(repository.save(entity));
Questions:
Should I put this getReferencedById code explicity? I mean what if we have several child objects (unidirectional ManyToOne), for each we need to get data from DB.
Is there any way to handle this by JPA (Spring Data/JPA) "automatically"?
Maybe it is possible to hit DB only one time with f.e join fetch somehow for all childs (with using custom #Query or querydsl or criteria/specification)?
Next, hoow to handle collections (unidirectional manyToMany)? In my case set of events in member object. Also need to loop thru and get all objects one by one before saving member?
Where should I put such logic in a service or maybe better in a mapstuct mapper?
If so, how to use repositories in such mapper?
#Mapper(componentModel = "spring")
public interface MemberMapper extends EntityMapper<MemberDto, Member> {
#AfterMapping
default void afterMemberMapping(#MappingTarget Member m, MemberDto dto) {
var club = clubRepo.findById(m.getClub().getId())
m.setClub(club)
}
Source code:
#Entity
public class Club extends AbstractEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Long id;
}
public class ClubDto extends AbstractDto {
private Long id;
}
#Entity
public class Member {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, nullable = false)
private Long id;
// commented out as don't want to save child object as it should already exist
// #ManyToOne(cascade = CascadeType.ALL)
#ManyToOne
Club club;
#ManyToMany
#JoinTable(name = "member_events",
joinColumns = #JoinColumn(name = "member_id"),
inverseJoinColumns = #JoinColumn(name = "event_id")
)
List<Event> events = new ArrayList<>();
}
public class MemberDto {
private Long id;
private ClubDto club;
}
#MappedSuperclass
public abstract class AbstractEntity {
#Version
private Integer version;
}
public abstract class AbstractDto {
private Integer version;
}
//MemberMapper above

fetch list based on id present in another entity

this is my order entity,
#Data
#NoArgsConstructor
#AllArgsConstructor
#ToString
#Entity
#Table(name = "ordertab")
public class Order {
#Id
private int orderId;
private String orderDate;
#ManyToMany(targetEntity = Medicine.class,cascade = CascadeType.ALL)
#JoinTable(name="ord_med",
joinColumns = {#JoinColumn(name="ord_id")},
inverseJoinColumns = {#JoinColumn(name="med_id")})
private List<Medicine> medicineList;
private String dispatchDate;
private float totalCost;
#ManyToOne(targetEntity = Customer.class,cascade = CascadeType.ALL)
#JoinColumn(name= "custord_fk",referencedColumnName = "customerId")
private Customer customer;
private String status;
}
and this is my medicine entity,
#Data
#NoArgsConstructor
#AllArgsConstructor
#ToString
#Entity
public class Medicine {
#Id
private String medicineId;
private String medicineName;
private float medicineCost;
private LocalDate mfd;
private LocalDate expiryDate;
**#ManyToMany(cascade = CascadeType.ALL, mappedBy = "medicineList")
private List<Order> orderList;** //order/ medicine many to many mapping
// OneToOne Mapping
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "categoryId", referencedColumnName = "categoryId")
private Category category;
in my order service interface i have a method,
List showAllOrder(string medId);
I have to fetch all orders that has the matching med id.
this many to many mapping have created a additional table ord_med with two columns named ord_id,med_id(type foreign keys).In addition to that due to this bidirectional mapping(i believe it is) while creating object of medicine entity its asking me to add orderlist ,how to approach this method or how exactly should i solve this. thankyou.
in your OrderRepository you can implements this method
findByMedicineId(String id);
if i go for findByMedicineId(String id);
it gives error saying no property medicineId is found in Order entity,cuz the property medicineId is in Medicine entity,while defining custom method in repository follows rules, refer https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.query-creation
anyway I have found the solution for this,
public List<Order> getOrderListBasedOnMedicineId(String medicineid) {
Optional<Medicine> med=medicineRepo.findById(medicineid);//find if medicine is present in database with the id.
if(med.isEmpty()) {
return null;
}
List<Order> orders = medicineServ.getOrderList(); //getorderlist defined in service implementation of medicine.
List<Order> ordersWithMedId = new ArrayList();//new list to add all orders that has atleast one medicineId that matches.
for(int i=0;i<orders.size();i++) {
List<Medicine> medicines= orders.get(i).getMedicineList();
for(int j=0;j<medicines.size();j++) {
ordersWithMedId.add(orders.get(i));
}
}
return ordersWithMedId;//returning the list of orders.
}
#Override
public List<Order> getOrderList() {//medicine service implementation
return orderRepo.findAll();
}
//OrderController
#GetMapping("/orders/list/{id}")
public ResponseEntity<List<Order>> getOrderListBasedOnMedicineId(#PathVariable("id") String id) {
List<Order> ord= orderService.getOrderListBasedOnMedicineId(id);
if(ord==null) {
throw new OrderNotFoundException("Order not found with medicine id:"+id);
}
return new ResponseEntity<List<Order>>(orderService.getOrderListBasedOnMedicineId(id),HttpStatus.OK);
}

spring data - how to make unique constraint with custom logic?

using spring data, I created User 1:N UserDog N:1 Dog relation. Both 1:N relations are unidirectional #ManyToOne with UserDog being the custom relation table.
User entity:
#Entity
public class User {
#Id
#GeneratedValue
private long id;
#Column(nullable = false)
private String name;
}
Dog entity:
#Entity
public class Dog {
#Id
#GeneratedValue
private long id;
#Column(nullable = false)
private String name;
}
User dog relation table:
#Entity
public class UserDog {
#Id
#GeneratedValue
private long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn
#OnDelete(action = OnDeleteAction.CASCADE)
private User user;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn
#OnDelete(action = OnDeleteAction.CASCADE)
private Dog dog;
#Column(nullable = false)
private Instant createdOn = Instant.now();
#Column
private Instant disabledOn;
}
Use case
Use case is to store history of User-Dog bindings, where the concrete Dog can be bound only to one User at the time. That's why I added createdOn and disabledOn columns to UserDog. disabledOn being null indicates that the relation is active and the Dog can't be assigned another User. If disabledOn is not null, then the record is stored only for evidence purposes and the Dog can be assigned to the same or another User again.
Question
How to ensure that the combination of Dog's id and disabledOn being null is unique in UserDog table?
In pseudo code I want something like this:
#Entity
#UniqueConstraint({#UniqueConstraint(this.dog.id), #NullConstraint(this.disabledOn)})
public class UserDog {...}
You can simply create a unique constraint for dogId and disabledOn.
It does add the limitation that no two relationships may end at the same time but this seems to fit your use case.

How to create JPA Specification for multiple tables?

I had entities User, BlockUnit, Block and Unit as Follows.
User has ManyToMany relation with blockunit.
#Entity
public class User {
#ManyToMany
private Set<BlockUnit> blockUnits;
}
#Entity
public class BlockUnit {
#Id
private Long id;
#OneToOne(targetEntity = Block.class)
#JoinColumn(name = "block_id")
private Block block;
#OneToOne(targetEntity = Unit.class)
#JoinColumn(name = "unit_id")
private Unit unit;
#ManyToMany(fetch = FetchType.LAZY)
private Set<User> users = new HashSet<>();
}
BlockUnit has OneToOne relation with Block and Unit.
#Entity
public class Block {
#Id
private Long id;
}
#Entity
public class Unit {
#Id
private Long id;
}
How should I create the JPA criteria specification for above in order to select user's having Block ID and Unit ID?
Thank you very much in advance!

jpaRepository findAll with parent and child use pagable

I'm using spring boot and jpa, I'm trying to get data from parent entity and child entity using jparepository.
Parent Entity:
#Entity
#Table(name = "parent")
public class Parent {
#Id
private int id;
private String name;
#OneToMany(mappedBy = "parent", fetch = FetchType.LAZY)
private Set<Child> children;
}
Child Entity:
#Entity
#Table(name = "child")
public class Child {
#Id
private int id;
private String name;
private int parent_id;
#ManyToOne
#JoinColumn(name = "parent_id", referencedColumnName = "id")
private Parent parent;
jpaRepository:
public interface ParentRepository extends JpaRepository<Parent, Integer>, JpaSpecificationExecutor<Parent> {
}
the reason I set the fecth to FetchType.LAZY is that sometimes I just want to get parent without child.
So, here is my question:
when I use
parentRepository.findAll(pagable);
the result only contains parents, not child, but I want the result to contain children, and in some situation I don't want it. how to write it ?
To fetch children collection you can declare an entity graph. Something like this:
#NamedEntityGraph(
name = "parent.withChildren",
attributeNodes = {
#NamedAttributeNode("children")
}
)
And then use it with repository methods:
#EntityGraph("parent.withChildren")
Page<Parent> findWithChidren(Pageable page);

Resources