Let's say I have those two entities, Person & Insurance. One Person can have multiple insurances, and the insurance uniqueness is maintained by a composite key combination of (insurance type, policy number, and person id). The below code represent the the scenario...
parent class
#Entity
#Setter
#Getter
#RequiredArgsConstructor
public class Person implements Serializable {
#Id
#GeneratedValue(strategy = "GenerationType.IDENTITY")
#Column(name "person_id")
private Long personId;
#Column(name = "fst_nm")
private String fstName;
#Column(name = "lst_nm")
private String lstNm;
// ..Other columns & relationships
#OneToMany(mappedBy = "person")
private List<Insurance> insurances;
public void addInsurance(Insurance toAdd) {
getInsurances().add(toAdd);
toAdd.setPerson(this);
}
}
child class
#Entity
#Setter
#Getter
#RequiredArgsConstructor
public class Insurance implements Serializable {
#EmbeddedId
private insurancePK id;
//other data
#ManyToOne
#MapsId("personId")
private Person person;
}
composite PK class
#Setter
#Getter
#Embeddable
public class InsurancePK implements Serializable {
#Column(name = "person_id", insertable = false, updatable = false)
private Long personId;
#Column(name = "insurance_type")
private String insuranceType;
#Column(name = "pol_num")
private String polNum;
}
now, my data mapper looks something like that...
Person newPerson = new Person();
newPerson.setInsurances(new ArrayList<>());
// fill out Person Model data
// incoming insurance data
while (incomingData.hasNext()) {
Insurance insuranceData = new Insurance();
InsurancePK pk = new InsurancePK();
// set other insurance data
pk.setInsuranceType("Dental");
pk.setPolNum("123Abc00");
insuranceData.setId(pk);
person.addInsurance(insuranceData);
}
Problem is my person_id inside the composite key is always getting a null value, not sure why (shouldn't the #MapsId takes care of that value)?
I need to fetch that value dynamically, most of the JPA composite key solutions only are setting all the value manually, but that's not my scenario.
return object from saveAndflush()
{
person: {
person_id: 55,
fst_nm: blah,
lst_nm: blah,
insurances: [
{
insurance_pk: {
person_id: null,
insurance_type: "Dental",
pol_num: "123Abc00"
}
//other insurance data
}
]
}
}
any suggestions on what am I missing? Thank you in advance!
Remove the #Column(name = "person_id", insertable = false, updatable = false) annotation from the InsurancePK.personId.
Add the following annotation:
#JoinColumn(name = "name = "person_id"")
to the Insurance.person.
As mentioned in the comments, adding a cascade to my entity column started me on the right track.
just in case, that's the model that worked for me after couple of tries
Parent class
#Entity
#Setter
#Getter
#RequiredArgsConstructor
public class Person implements Serializable {
#Id
#GeneratedValue(strategy = "GenerationType.IDENTITY")
#Column(name "person_id")
private Long personId;
#Column(name = "fst_nm")
private String fstName;
#Column(name = "lst_nm")
private String lstNm;
// ..Other columns & relationships
// cascade added <-- thanks to SternK
#OneToMany(mappedBy = "person", casecade = CascadeType.ALL, orphanRemoval = true)
private List<Insurance> insurances;
public void addInsurance(Insurance toAdd) {
getInsurances().add(toAdd);
toAdd.setPerson(this);
}
}
Child class
#Entity
#Setter
#Getter
#RequiredArgsConstructor
public class Insurance implements Serializable {
#EmbeddedId
private insurancePK id;
//other data
#ManyToOne
#MapsId("personId")
// annotation added here instead of PK class <-- fix
#JoinColumn(name="person_id", nullable = false, insertable = false, updatable = false)
private Person person;
}
PK class
#Setter
#Getter
#Embeddable
public class InsurancePK implements Serializable {
//annotation removed <-- fix thanks to SternK
private Long personId;
#Column(name = "insurance_type")
private String insuranceType;
#Column(name = "pol_num")
private String polNum;
}
Related
I am facing an issue with persisting Observation entity.
The Observation entity has a list of protocols and every protocol has 1 observer. The observer can also be created before or not, so I can have many protocols which have created observers and many protocols which have not created observers.
The desired behavior for me is if I create this observation then observers with id == null will be created and observers with id will be merged.
The issue I am facing is if I specify CascadeType.Merge I can create observation with only the observers which were created before and if I specify CascadeType.Persist/CascadeType.All, I can create observation with only the observers that were not created before.
ObservationEntity:
#Entity
#Getter
#Setter
#NoArgsConstructor
public class Observation {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "observation_id", referencedColumnName = "id", nullable = false)
#OrderBy("observer ASC")
#Fetch(FetchMode.SUBSELECT)
private List<Protocol> protocols;
}
ProtocolEntity:
#Entity
#Getter
#Setter
#NoArgsConstructor
public class Protocol {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.MERGE)
#JoinColumn(name = "observer_id", nullable = false)
private Observer observer;
}
ObserverEntity:
#Entity
#Getter
#Setter
#NoArgsConstructor
public class Observer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable = false)
private String firstName;
#Column(nullable = false)
private String lastName;
private String imoCode;
}
ObservationService:
#Service
public class ObservationService {
#Autowired private ObservationMapper observationMapper;
#Autowired private ObservationRepository observationRepository;
public ObservationDTO create(ObservationDTO observationDTO) {
Observation observationCreated = observationRepository.save(observationMapper.observationDTO2Observation(observationDTO));
return observationMapper.observation2ObservationDTO(observationCreated);
}
}
Error:
org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation : sk.adambarca.serverspringboot.model.entity.Interval.protocol -> sk.adambarca.serverspringboot.model.entity.Protocol
Try this:
private List<Protocol> protocols = new ArrayList<>();
I have a class User which has a parameter of another class type ShoppingList.
As this...
#Entity
public class Employee implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(nullable = false, updatable = false)
private Long id;
private String Name;
#?????
private ShoppingList[] shoppingList;
}
How can i make this ManyToOne relationship while the variable being an array?
The idea is to have a User table and another ShoppingList table, so the user can have multiple lists at the same time.
This would be the correct way:
One Employee has many ShoppingLists.
One ShoppingList has only one Employee.
#Entity
public class Employee implements Serializable {
....
#OneToMany(mappedBy = "employee", fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
private List<ShoppingList> shoppingList;
....
}
#Entity
public class ShoppingList implements Serializable {
....
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "employee_id", nullable = false)
private Employee employee;
....
}
You can fine-tune your entities as per your need.
For more info, I would refer to this tutorial, it has helped me a lot.
I am working on hibernate and tying to associate mapping with #OneToMany relationship with composite key.
Following are the entities that currently my using .
#Embeddable
#Getter
#Setter
public class AddressKey implements Serializable {
private static final long serialVersionUID = -307823488229761699L;
#Column(name = "id")
private Long id;
#Column(name = "city")
private Long city;
#Column(name = "locale")
private String locale;
#Column(name = "type")
private String type;
#ManyToOne
#JoinColumn(name="id")
private Person person;
}
#Entity
#Table(name = "address", schema = "test")
#Setter
#Getter
public class AddressHistory {
#EmbeddedId
private AddressKey key;
#Column(name = "active")
private boolean active;
#Column(name = "current")
private boolean current;
}
#Entity
#Table(name = "person", schema="test")
#ToString
public class Person {
#Id
#Column(name = "id")
private Long id;
#OneToMany(mappedBy="key.person", fetch=FetchType.EAGER, cascade=CascadeType.ALL)
private Set<AddressHistory> addressHistory;
}
But when I am trying to run this program it gives me following error.
repeated column in mapping for entity AddressHistory.
Someone help me to fix this what's wrong in this mapping.
Thanks in advance
You repeated columns. Remove #JoinColumn(name="id") in AddressKey since you already have one column with the same name or rename it to something else and more maintainable like person_id.
I have a problem with retrieving an entity using the child's entity as a search parameter. Entities are related to many to one relationship as unidirectional and each object is fetched as FetchType.LAZY.
When I looking for an entity by a child entity, the result is null. But when I set to fetch as Eager it is correct.
My Entities:
#NoArgsConstructor
#Getter
#Entity
#Table(name = "partner")
public class PartnerEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String login;
public PartnerEntity(String login) {
this.login = login;
}
}
#NoArgsConstructor
#Getter
#Entity
#Table(name = "point")
public class PointEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "partner_Id")
private PartnerEntity partnerEntity;
public PointEntity(PartnerEntity partnerEntity) {
this.partnerEntity = partnerEntity;
}
}
#NoArgsConstructor
#Getter
#Entity
#Table(name = "orer")
public class OrdEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "PAYMENT_POINT_ID")
private PointEntity pointEntity;
public OrdEntity(PointEntity pointEntity) {
this.pointEntity = pointEntity;
}
}
#NoArgsConstructor
#ToString
#Getter
#Entity
#Table(name = "BL")
public class BLEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "PARTNER_LOGIN", referencedColumnName = "login")
private PartnerEntity partnerEntity;
private String number;
public BLEntity(PartnerEntity partnerEntity, String number) {
this.partnerEntity = partnerEntity;
this.number = number;
}
}
And I looking for BLEntity using OrdEntity child:
final OrdEntity byId = ordRepo.findById(id);
final PartnerEntity partnerEntity = order.getPointEntity().getPartnerEntity();
final BLEntity blEntityResult= blRepo.findOneByNumberAndPartner(number, partnerEntity);
The object partnerEntity is not null, it is correct object.
I got blEntityResult as null but if I change in PointEntity fetch to FetchType.EAGER, blEntityResult is not null(correct).
My custom query in repository below:
public interface BLRepo extends JpaRepository<BLEntity, Long> {
#Query("select b from BLEntity b where b.number = :number and b.partnerEntity= :partner")
BLEntity findOneByNumberAndPartner(#Param("number") String number, #Param("partner") PartnerEntity partner);
}
why does happens, if the partner object being downloaded is not null and is correct?
I think you should add the mapping in both sides,
because of default fetch type for #AllToMany=Lazy and #ManyToAll = Eager.
just add below code inside PartnerEntity.
#OneToMany(mappedBy="partnerEntity" , fetch = FetchType.Eager )
List<BLEntity> blEntity = new ArrayList<>();
I change FetchType into Eager in PointEntity:
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "partner_Id")
private PartnerEntity partnerEntity;
And everything is ok, but I don't understand why it does not work with PaymentType.Lazy. When I am looking for:
final PartnerEntity partnerEntity = order.getPointEntity().getPartnerEntity();
I get correct entity "PartnerEntity" which has proper login's field (login'field has value "test").
When I turned logged level to 'TRACE' I saw, Hibernate not binding correct login's parameter, it set null instead "test") why? :)
Student.java
#Entity
#Table(name = "Student")
#Data
#NoArgsConstructor
public class Student implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(mappedBy = "studentId", cascade = CascadeType.ALL)
private List<Subject> subjectList = new ArrayList<>();
public void addSubject(Subject subject) {
subjectList.add(subject);
subject.setStudentId(this);
}
#Column(name = "firstName")
private String firstName;
#Column(name = "lastName")
private String lastName;
}
Subject.java
#Entity
#Table(name = "Subject")
#Data
#NoArgsConstructor
public class Subject implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="studentId", nullable = false)
private Student studentId;
#Column(name = "subjectName")
private String subjectName;
#Column(name = "subjectCode")
private int subjectCode;
}
SubjectRepository.java
#Repository
public interface SubjectRepository extends JpaRepository<Subject, Long> {
}
As shown in the code structure above, I have 2 entities (Student, Subject) and a repository class (SubjectRepository). When i try to save into the Subject table, somehow the student name "Thomas" from the Student table gets updated as well in the database. I would like to only insert into Subject table without having any values from the Student table getting updated. Need some help on this. Thanks!
public static void main(String[] args) {
#Autowired protected SubjectRepository subjectRepository;
Student student = new Student();
student.setFirstName("Thomas");
Subject subject = new Subject();
subject.setSubjectName("Chemistry");
subject.setSubjectCode(12345);
student.addSubject(subject)
subjectRepository.save(subject)
}
I would like to only insert into Subject table without having any values from the Student table getting updated
You can achieve this with following code :
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="studentId", nullable = false, updatable = false)
private Student studentId;
When using Spring JPA I would suggest using the JpaRepository API. You just need to pass in your entity, and it should save as desired.
Ex:
subjectRepository.save(subject);
You have try this
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.REFRESH)
#JoinColumn(name="studentId", nullable = false, updatable = false)
private Student studentId;
#MaxExplode You have to use cascade = CascadeType.REFRESH then other details will not update. but if you are try to set updateStudent.setfirstNamr(student.getFirstName()); and then save parent object then i will update. otherwise it will not update.