In this code, I'm trying generate a composite key with that model class, but when I execute the post method throw an exception.
I'm using postgres as database. Then I need the hibernate generate the "RA" and Idprojeto automatically.
Model Id class
#Data
#EqualsAndHashCode(onlyExplicitlyIncluded = true)
#AllArgsConstructor
#NoArgsConstructor
class ProjetoId implements Serializable{
Long idProjeto;
Long ra;
}
Model Entity
#Entity
#Data
#IdClass(ProjetoId.class)
#EqualsAndHashCode(onlyExplicitlyIncluded = true)
#Table(name = "projeto", schema = "public")
public class Projeto {
#EqualsAndHashCode.Include
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_projeto")
private Long idProjeto;
#Valid
#ConvertGroup(from = Default.class, to = ValidationGroups.ClienteId.class)
#NotNull
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "id_cliente")
private Cliente cliente;
#Valid
#NotNull
#ElementCollection
private List<ProdutoToProjeto> produtos = new ArrayList<>();
#EqualsAndHashCode.Include
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ra")
private Long ra;
#JsonProperty(access = JsonProperty.Access.READ_ONLY)
#Enumerated(EnumType.STRING)
#Column(name = "situacao")
private StatusProjeto situacao;
#JsonProperty(access = JsonProperty.Access.READ_ONLY)
#Column(name = "data_criacao")
private LocalDate dataRegistro;
public void finalizar() {
if(naoPodeSerFinalizado()) {
throw new ProjetoException("Projeto não pode ser finalizado");
}
setSituacao(StatusProjeto.APROVADO);
}
public void cancelar() {
if(naoPodeSerFinalizado()) {
throw new ProjetoException("Projeto não pode ser cancelado");
}
setSituacao(StatusProjeto.RECUSADO);
}
public boolean podeSerFinalizado() {
return StatusProjeto.PEDENTE.equals(getSituacao());
}
public boolean naoPodeSerFinalizado() {
return !podeSerFinalizado();
}
}
Error
java.lang.IllegalArgumentException: Can not set java.lang.Long field com.ciasense.apiciasense.model.ProjetoId.ra to org.hibernate.id.IdentifierGeneratorHelper$2
at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167) ~[na:na]
at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171) ~[na:na]
at java.base/jdk.internal.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:81) ~[na:na]
at java.base/java.lang.reflect.Field.set(Field.java:778) ~[na:na]
If you use DB SEQUENCE as the auto generation type only then composite key can work. Otherwise you have to write custom key generator and assign values to both the fields of your composite key before inserting.
#EqualsAndHashCode.Include
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "id_projeto")
private Long idProjeto;
#EqualsAndHashCode.Include
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "ra")
private Long ra;
If i understand, you want to do a key composite ?
If it is, you have two thing to do :
1- annoted your class with #IdClass as you do and put #Id on properties you want to link with your key and Equals and HashCode methods (your IDE generate it easily). (this thing is already done for you)
2- In your repository, you have to define your key also like normal key
example : (seems to not be done, it is the "ProjetoId" in the extends class)
#Repository
public interface ProjetoRepository extends JpaRepository<Projeto, ProjetoId> {
List<Projeto> findByDataRegistro(Date date);
}
you can also go to see baeldung which explain it with more details than me.
https://www.baeldung.com/jpa-composite-primary-keys
Related
Getting below Error while saving data
Caused by: org.postgresql.util.PSQLException: ERROR: null value in column "header_id" of relation "invoice" violates not-null constraint
Detail: Failing row contains (...null).
Header
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "header")
public class Header implements Serializable {
private static final long serialVersionUID = 3363186434410305269L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "header_id")
private Long headerId;
#Column(name = "submitted_by", length = 17)
private String submittedBy;
#OneToMany(mappedBy = "header", cascade = CascadeType.ALL)
#Builder.Default
private List<Invoice> invoices = new ArrayList<>();
}
Invoice
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "invoice")
public class Invoice implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "invoice_id")
private Long invoiceId;
#Column(name = "serial_no")
private Integer serialNo;
#JsonIgnore
#ManyToOne
#JoinColumn(name = "header_id", nullable = false)
private Header header;
}
Please help me to solve the error.
I think this is because your headerId can be null because you use "Long" as type, wich is a good practice, but you should remove nullable = false inside Invoice.java.
Put nullable = false inside your database rules
If this problem does not come from there, check that the headerId of your Header class is not null when you create the object that you want to save in the database
I am trying to get how to write the JPA method for the class by using its foreign key instead of the primary key. Like, here I can't use findById() method, as it finds records according to primary key defined in the class. Below are the two classes for #ManyToOne and #OneToMany.
PARENT CLASS :
#Entity
#Getter
#Setter
//#Data
#NoArgsConstructor
#Table(name = "financial_plan_details", schema = "financialplanadmin")
public class FinancialPlanDao {
// This internalId is the primary key of the table.
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "internal_plan_id")
private int internalId;
// This stores the plan status into the database table.
#Column(name = "plan_status")
#Size(max = 10)
private String planStatus;
#Column(name = "presentation_file_key")
#Size(max = 500)
private String presentationFileKey;
#Column(name = "create_timestamp")
#NotNull
private Timestamp createdTimestamp;
#OneToMany(mappedBy = "financialPlan")
private List<FinancialSubPlan> subPlans;
}
CHILD CLASS:
#Entity
#Getter
#Setter
#NoArgsConstructor
#Table(name = "financial_plan_subplan", schema = "financialplanadmin")
#JsonInclude(Include.NON_NULL)
public class FinancialSubPlan {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "subplan_id")
private int subPlanId;
#Column(name = "external_subplan_id")
private String externalSubplanId;
#Column(name = "is_chosen")
private Boolean subPlanIsChosen;
#ManyToOne
#JoinColumn(name = "internal_plan_id")
private FinancialPlanDao financialPlan;
}
The table generated for FinancialSubPlan will consist of the primary key column "subplan_id" and foreign key column "Internal_plan_id". So is there any way to write the JPA method to get records of FinancialSubPlan by "internal_plan_id". Also how to get this using #Query ?
It would be something like this. IDE auto-suggest would help as you type, just in case.
findFinancialSubPlanByFinancialPlanDaoId(int internalId)
or
findFinancialSubPlanByFinancialPlanDao(int internalId)
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;
}
I'm new on Spring, i would like to store relationship between Exam and Question whith this structure:
#Entity
#Data
#EqualsAndHashCode(callSuper = true)
public class Exam extends UriEntity<Integer> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#NotBlank
#Length(min = 1, max = 256)
private String name;
#OneToMany(mappedBy="exam")
private List<ExamQuestion> exams_questions;
#Entity
#Data
#EqualsAndHashCode(callSuper = true)
public class Question extends UriEntity<Integer> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#NotBlank
#Column(unique = true)
private String statement;
#NotBlank
private String answer;
#OneToMany(mappedBy="question")
private List<ExamQuestion> exams_questions;
#Entity
#Data
#EqualsAndHashCode(callSuper = true)
public class ExamQuestion extends UriEntity<Integer> {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#ManyToOne
#JoinColumn(name = "question_id")
private Question question;
#ManyToOne
#JoinColumn(name = "exam_id")
private Exam exam;
When i want to store a new ExamQuestion object with POSTMAN
{
"exam": "http://localhost:8080/exams/28",
"question": "http://localhost:8080/questions/17"
}
I get an error 409, and the message:
NULL not allowed for column \"ID\";
Any could help me ?
Thanks in advance.
This might happen because of the #GeneratedValue(strategy = GenerationType.IDENTITY).
Make sure that in your database schema ID column is set to be auto incremented, since GenerationType.IDENTITY uses databases identity column to generate values. Thus if your column is not set to be auto incremented you get this error.
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? :)