Spring JPA Save entity with #JoinColumn - java

I have two entities
Entity 1
#Entity
#Table(name = "Table1")
public class Table1{
#Id
#SequenceGenerator(name = "Table1_SEQ", sequenceName = "Table1_SEQ", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "Table1_SEQ")
#Column(name = "seq", updatable = false, unique = true, insertable = true)
private Long seq;
#Column(name = "number", updatable = false, insertable = true)
private Long number;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "enumber", updatable = false, insertable = true)
private Table2 table2;
Entity 2
#Entity
#Table(name = "Table2")
public class Table2 {
#Id
#SequenceGenerator(name = "Table2_SEQ", sequenceName = "Table2_SEQ", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "Table2_SEQ")
#Column(name = "enumber")
private Long eNumber;
I have a repository which extends JpaRepository
public interface Table1Repository extends JpaRepository<Table1, Long> {
}
When I try to save using
Table1Repository rep;
Table1 table1; // Assume this is initialized correctly
rep.save(table1);
I get a Foriegn Key constraint error org.springframework.dao.DataIntegrityViolationException
Table1 has a Foreign Key association with Table2 on the column enumber but I don't want to insert anything into Table2.
How do I save into Table1?
I'm not too familiar with Jpa so please let me know if you need any clarifications on what I'm asking or if you need any more information.

#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "enumber", updatable = false, insertable = true)
private Table2 table2; // THIS IS WRONG!
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "enumber", updatable = false, insertable = true)
private Long enumberFKTable2; // THIS IS CORRECT!
//getters and setters
Implement another java class similar to table1 called table2 with all the spring annotations. Then you do the following code:
//First, make sure that the foreign key exists!
number enumber = entityManager.getReference(Enumber.class, enumberId);
//test
if(enumber != null){
Table 1 table1 = new Table1();
table1.setEnumber(enumber);
entityManager.persist(table1);
}

Related

JPA Join Tables with two fields, one of them a primary key

the problems is when #ManyToOne make a #Joincolumn ID_REPORT (it´s a primary key ) and #Joincolumn ID_TEMPLATE_DEFAULT
Repeated column in mapping for entity: CurReport column: id_report (should be mapped with insert="false" update="false")
Code
First table CUR_TEMPLATE
CREATE TABLE CUR_TEMPLATE
(
ID_REPORT NUMBER(5,0) NOT NULL,
ID_TEMPLATE NUMBER(5,0) NOT NULL,
-- Other fields
);
ALTER TABLE CUR_TEMPLATE ADD CONSTRAINT PK_CUR_TEMPLATE PRIMARY KEY (ID_REPORT, ID_TEMPLATE)
-- CUR_TEMPLATE foreign keys
ALTER TABLE CUR_TEMPLATE ADD CONSTRAINT FK_CUR_PLAN_REFERENCE_CUR_REPO FOREIGN KEY (ID_REPORT)
REFERENCES CUR_REPORTS (ID_REPORT);
Second table CUR_REPORTS
-- CUR_REPORTS definition
CREATE TABLE CUR_REPORTS
(
ID_REPORT NUMBER(3,0) NOT NULL,
NAME_REPORT VARCHAR2(100) NOT NULL,
-- other fields
ID_TEMPLATE_DEFAULT NUMBER(5,0),
-- other fields
) ;
ALTER TABLE CUR_REPORTS ADD CONSTRAINT PK_CUR_REPORTS PRIMARY KEY (ID_REPORT)
ALTER TABLE CUR_REPORTS CONSTRAINT FK_CUR_REPO_REFERENCE_CUR_PLAN FOREIGN KEY (ID_REPORT, ID_TEMPLATE_DEFAULT)
REFERENCES CUR_TEMPLATE (ID_REPORT, ID_TEMPLATE)
First table CUR_REPORTS Entity CurReport
#Entity
#Table(name = "CUR_REPORTS")
#IdClass(CurPlantillaPK.class)
#Getter
#Setter
public class CurReport {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID_REPORT", nullable = false)
private Long id;
#Column(name = "NAME_REPORT", nullable = false, length = 100)
private String nombreReporte;
#ManyToOne(fetch = FetchType.LAZY) <---WHERE IS THE PROBLEM
#JoinColumn(name = "ID_REPORT", referencedColumnName = "ID_REPORTE")
#JoinColumn(name = "ID_TEMPLATE_DEFAULT", referencedColumnName = "ID_TEMPLATE")
private CurTemplate curTemplate;
#OneToMany(mappedBy = "curReport")
private Set<CurTemplate> curTemplates= new LinkedHashSet<>();
}
Second table CUR_TEMPLATE Entity CurReport
#Entity
#Table(name = "CUR_TEMPLATE")
#IdClass(CurPlantillaPK.class)
#Getter
#Setter
public class CurTemplate {
#Id
#Column(name = "ID_REPORT", nullable = false)
private Long idReport;
#Id
#Column(name = "ID_TEMPLATE", nullable = false)
private Long idTemplate;
#ManyToOne(optional = false, fetch = FetchType.LAZY)
#JoinColumn(name = "ID_REPORT", foreignKey = #ForeignKey(name = "FK_CUR_PLAN_REFERENCE_CUR_REPO"), referencedColumnName = "ID_REPORT", insertable = false, updatable = false)
private CurReport curReport;
}
When i add insertable=false, updatable=false
#JoinColumn(name = "ID_REPORT", referencedColumnName = "ID_REPORT", insertable=false, updatable=false)
said
Mixing insertable and non insertable columns in a property is not allowed: CurTemplate
How could i map those relationships?
How resolve the #JoinColumn when one field of the FK are column PK?
You can use a derived identity and map CurTemplate like this:
#Entity
#Table(name = "CUR_TEMPLATE")
#IdClass(CurTemplatePK.class)
#Getter
#Setter
public class CurTemplate {
#Id
#Column(name = "ID_TEMPLATE", nullable = false)
private Long idTemplate;
#Id
#ManyToOne(optional = false, fetch = FetchType.LAZY)
#JoinColumn(name = "ID_REPORT", foreignKey = #ForeignKey(name = "FK_CUR_PLAN_REFERENCE_CUR_REPO"), referencedColumnName = "ID_REPORT", insertable = false, updatable = false)
private CurReport curReport;
}
Then you will need an #IdClass like this:
public class CurTemplatePK {
Long idTemplate; // matches name of #Id attribute
Long curReport; // matches name of #Id attribute and type of CurReport PK
}
Then you should use a basic mapping for the default template key and provide a getter for the default template object:
#Entity
#Table(name = "CUR_REPORTS")
#IdClass(CurPlantillaPK.class)
#Getter
#Setter
public class CurReport {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID_REPORT", nullable = false)
private Long id;
#Column(name = "NAME_REPORT", nullable = false, length = 100)
private String nombreReporte;
#Column(name = "ID_TEMPLATE_DEFAULT")
private Long idDefaultTemplate;
#OneToMany(mappedBy = "curReport")
private Set<CurTemplate> curTemplates= new LinkedHashSet<>();
public CurTemplate getDefaultTemplate() {
return this.curTemplates.stream()
.filter(template -> template.getIdTemplate().equals(idDefaultTemplate))
.findFirst()
.orElse(null);
{
}
If you want to allow clients to set the default template, you will need to implement a setter that first verifies that the new default template is already in the set curTemplates.

I need to map one to many relationship between questions and answer entities. But questionID colum is not updating. How can I fix this?

I used question_id as the primary key of questions table and it is a foreign key for the answers table. #JoinColumn has used for declare referencedColumnName .
#Entity
#Table(name = "questions")
public class Question {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long question_id;
#Column(nullable = false, unique = false, length = 100)
private String question_subjectArea;
#Column(nullable = false, unique = false, length = 1000)
private String fullQuestion;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn( name = "questionID", referencedColumnName = "question_id")
List<Answer> answer = new ArrayList<>();
//Getters and setters
#Entity
#Table(name = "answers")
public class Answer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long answer_id;
#Column(nullable = true, unique = false, length = 100)
private Long answer_authorID;
#Column(nullable = false, unique = false, length = 100)
private String fullAnswer;
//Getters and setters
Application.properties configuration as follows
spring.jpa.hibernate.ddl-auto=create
In our Entity class: Answer, seems you need also define the member: Question.
#ManyToOne
#JsonIgnore
#JoinColumn(name = "question", referencedColumnName = "question_id")
private Question question;

Spring Boot: How to create an Entity with a composite key

I was creating my classes for a project using a chart for practice purposes until I stumbled upon this order_items:
I had no problem creating an Entity like Orders or Products because I knew that for Orders I just had to do something like:
#Entity
#Table(name = "orders")
public class Orders {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "order_id")
private Integer orderId;
// rest of the code...
}
And for for Products something like:
#Entity
#Table(name = "products")
public class Products {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "product_id")
private Integer productId;
// rest of the code...
}
But the table order_items has the variables order_id and item_id, does that count as a composite key? If that is the case, how should those variables look in my OrderItems class?
#Entity
#Table(name = "order_items")
public class OrderItems {
#Column(name = "order_id")
private Integer orderId;
#Column(name = "item_id")
private Integer itemId;
// rest of the code...
}
I've checked different questions and they mention using #IdClass or #EmbeddableId for composite keys, but I'd like to confirm first if that is what I should do in this situation, unless it's not the case, maybe there are more approaches.
I'd really appreciate opinions and/or any article related to this, thank your for your time.
As you mentioned you can use #EmbeddableId.
Here is example :
#Embeddable
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder(toBuilder = true)
public class OrderItemsPK implements Serializable {
private static final long serialVersionUID = 1L;
#Column(insertable = false, unique = false, updatable = false, nullable = false,name = "order_id")
private Long orderId;
#Column(insertable = false, unique = false, updatable = false, nullable = false,name = "products_id")
private Long productsId;
}
And the Order Items Class.
#Entity
public class OrderItems {
#EmbeddedId
private OrderItemsPK id;
#OneToOne
#JoinColumn(name = "products_id", nullable = false, unique = false, insertable = false, updatable = false, referencedColumnName = "products_id")
private Products products;
#OneToOne
#JoinColumn(name = "orders_id", nullable = false, unique = false, insertable = false, updatable = false, referencedColumnName = "orders_id")
private Order order;
private Long itemId;
}

Implement JPA request with JOIN

I have these 3 entities:
Payment Transactions:
#Entity
#Table(name = "payment_transactions")
public class PaymentTransactions implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, updatable = false, nullable = false)
private int id;
.....
}
WPF Payments:
#Entity
#Table(name = "wpf_payments")
public class WpfPayments implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, updatable = false, nullable = false)
private int id;
............
}
WPF Payments Payment transactions:
#Entity
#Table(name = "wpf_payment_payment_transactions")
public class WpfPaymentPaymentTransactions implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, updatable = false, nullable = false, length = 3)
private int id;
#Column(length = 4)
private Integer wpf_payment_id;
#Column(length = 4)
private Integer payment_transaction_id;
.....
}
I use these SQL requests to get proper data based in id:
SELECT `payment_transactions`.* FROM `payment_transactions` INNER JOIN `wpf_payment_payment_transactions` ON `payment_transactions`.`id` = `wpf_payment_payment_transactions`.`payment_transaction_id` WHERE `wpf_payment_payment_transactions`.`wpf_payment_id` = 75 ORDER BY `payment_transactions`.`id` ASC LIMIT 1
SELECT `payment_transactions`.* FROM `payment_transactions` INNER JOIN `wpf_payment_payment_transactions` ON `payment_transactions`.`id` = `wpf_payment_payment_transactions`.`payment_transaction_id` WHERE `wpf_payment_payment_transactions`.`wpf_payment_id` = 75
Is there some way to implement these SQL requests using JPA queries?
If you are using JPA 2.0, it is not possible to use JPQL with your queries since you cannot use the ON clause.
One solution is to implement a Bidirectional Mapping on the entities WpfPaymentPaymentTransactions,
PaymentTransactions to be able to make a join :
#Entity
#Table(name = "payment_transactions")
public class PaymentTransactions implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, updatable = false, nullable = false)
private int id;
#OneToOne(mappedBy="paymentTransactions") //or OneToMany depending on your model
private WpfPaymentPaymentTransactions wpfPaymentPaymentTransactions;
}
#Entity
#Table(name = "wpf_payment_payment_transactions")
public class WpfPaymentPaymentTransactions implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", unique = true, updatable = false, nullable = false, length = 3)
private int id;
#ManyToOne
#JoinColumn(name="wpf_payment_id")
private PaymentTransactions paymentTransactions;
}
Then you can join the two entities like this :
SELECT t FROM PaymentTransactions t
INNER JOIN WpfPaymentPaymentTransactions wppt
Starting from JPA 2.1, You can add the ON clause with JPQL query. So for the first query, it will be like this :
SELECT t FROM PaymentTransactions t
INNER JOIN WpfPaymentPaymentTransactions wppt
ON t.id = wppt.paymentTransactionId`
WHERE wppt.wpfPaymentId = :param
ORDER BY t.id ASC LIMIT 1
Hope it helps!

Cascading save Entity objects with Foreign Key as a part of composite Primary Key

I would like to persist object of my QuestionCompletion class with all child elements. One of these childs has a composite primary key. And as a part of this primary key I have also foreign key to another entity. As a result I am getting this error:
Exception caught during request processing: javax.ejb.EJBTransactionRolledbackException:
could not set a field value by reflection setter of com.example.model.domain.QuestionCompletionAnswerPK.questionCompletionId
javax.ejb.EJBTransactionRolledbackException: could not set a field value by reflection
setter of com.example.model.domain.QuestionCompletionAnswerPK.questionCompletionId
And the last "caused by" is of course NullPointerException:
Caused by: java.lang.NullPointerException
This is part of my code. The last line causes error.
QuestionCompletion questionCompletion = new QuestionCompletion();
List<QuestionCompletionAnswer> answers = new ArrayList<QuestionCompletionAnswer>();
for (;;) { // loop isn't important; it's loop for answers
ExtendedQuestion extendedQuestion = new ExtendedQuestion();
extendedQuestion.setId(extendedQuestionId); //extendedQuestionId is known to me in that place
for (;;) { // loop isn't important; it's loop for question answers
//questionCompletion and extendedQuestion are popualted here
QuestionCompletionAnswer questionCompletionAnswer = new QuestionCompletionAnswer();
questionCompletionAnswer.setQuestionCompletion(questionCompletion);
questionCompletionAnswer.setExtendedQuestion(extendedQuestion);
answers.add(questionCompletionAnswer);
}
}
questionCompletion.setAnswers(answers);
questionCompletionService.saveOrMerge(questionCompletion);
This is my basic entity class I would like to persist with all its childs elements. I have realized that List<QuestionCompletionAnswer> causes problems. I have used cascade = CascadeType.ALL to allow to persist childs elements also.
#javax.persistence.Entity
#org.hibernate.annotations.Entity(dynamicUpdate = true)
#Table(name = "question_completion")
public class QuestionCompletion implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "question_completion_gen")
#SequenceGenerator(name = "question_completion_gen", sequenceName = "question_completion_id_seq")
#Column(name = "question_completion_id")
private Long id;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "extended_question_id")
protected List<QuestionCompletionAnswer> answers;
}
This is my class - Primary Key for the QuestionCompletionAnswer class.
#Embeddable
public class QuestionCompletionAnswerPK implements Serializable {
#Column(name = "question_completion_id")
protected Long questionCompletionId;
#Column(name = "extended_question_id")
protected Long extendedQuestionId;
}
And this is class which uses my EmbeddedId. Attribues questionCompletionId and questionCompletionId are the foreign key for some another entities so I have placed below also whole objects of these entities with #MapsId annotation.
#javax.persistence.Entity
#org.hibernate.annotations.Entity(dynamicUpdate = true)
#Table(name = "extended_question_answer")
public class QuestionCompletionAnswer implements Serializable {
#EmbeddedId
private QuestionCompletionAnswerPK id;
#ManyToOne
#MapsId(value = "questionCompletionId")
#JoinColumn(name = "question_completion_id", nullable = false, insertable = false, updatable = false)
protected QuestionCompletion questionCompletion;
#ManyToOne
#MapsId(value = "extendedQuestionId")
#JoinColumn(name = "extended_question_id", nullable = false, insertable = false, updatable = false)
protected ExtendedQuestion extendedQuestion;
}
Could you tell me if my annotations are correct? Maybe I have mixed up few approaches. Or I can't in that case persist my basic object with all of its child elements.
EDIT
Now my mapping looks like:
#javax.persistence.Entity
#org.hibernate.annotations.Entity(dynamicUpdate = true)
#Table(name = "question_completion")
public class QuestionCompletion implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "question_completion_gen")
#SequenceGenerator(name = "question_completion_gen", sequenceName = "question_completion_id_seq")
#Column(name = "question_completion_id")
private Long id;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "questionCompletion")
protected List<QuestionCompletionAnswer> answers;
}
Code of the QuestionCompletionAnswerPK class is the same.
#javax.persistence.Entity
#org.hibernate.annotations.Entity(dynamicUpdate = true)
#Table(name = "extended_question_answer")
public class QuestionCompletionAnswer implements Serializable {
#EmbeddedId
private QuestionCompletionAnswerPK id;
#ManyToOne(cascade = CascadeType.PERSIST)
#MapsId(value = "questionCompletionId")
#JoinColumn(name = "question_completion_id", nullable = false)
protected QuestionCompletion questionCompletion;
#ManyToOne
#MapsId(value = "extendedQuestionId")
#JoinColumn(name = "extended_question_id", nullable = false)
protected ExtendedQuestion extendedQuestion;
}
With that mapping I am still getting the same exception.
EDIT #2
However when I have changed QuestionCompletionAnswer class in this way:
#javax.persistence.Entity
#org.hibernate.annotations.Entity(dynamicUpdate = true)
#Table(name = "extended_question_answer")
public class QuestionCompletionAnswer implements Serializable {
#EmbeddedId
private QuestionCompletionAnswerPK id;
#ManyToOne(cascade = CascadeType.PERSIST)
#JoinColumn(name = "question_completion_id", nullable = false, insertable = false, updatable = false)
protected QuestionCompletion questionCompletion;
#ManyToOne
#JoinColumn(name = "extended_question_id", nullable = false, insertable = false, updatable = false)
protected ExtendedQuestion extendedQuestion;
}
I am getting that exception:
Caused by: org.hibernate.id.IdentifierGenerationException: null id generated
for:class com.example.model.domain.QuestionCompletionAnswer
Edit 1 and 2 are still not right. You need mapsid specified on the relationship or you must set the field in the embedded id with a value yourself. And when you use mapsid, you shouldn't have the join column marked insertable=false or jpa can't insert a value for you. The last problem I see is that the new question is not persisted so it doesn't get an id assigned that the answer can reference - you need to explicitly persist the new question in the for loop or mark the relationship in the answer to it to cascade persist.
Why are you mixing jpa and hibernate annotations ? jpa annotations alone should be enough.
Try with the following mapping (I removed the class QuestionCompletionAnswerPK) :
QuestionCompletion.java
#Entity
#Table(name = "question_completion")
public class QuestionCompletion {
#Id
#SequenceGenerator(name = "question_completion_gen", sequenceName = "question_completion_id_seq")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "question_completion_gen")
#Column(name = "question_completion_id")
private Long id;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "questionCompletion")
protected List<QuestionCompletionAnswer> answers;
}
QuestionCompletionAnswer.java
#Entity
#Table(name = "extended_question_answer")
public class QuestionCompletionAnswer implements Serializable {
#Id
#ManyToOne
#JoinColumn(name = "question_completion_fk")
private QuestionCompletion questionCompletion;
#Id
#ManyToOne
#JoinColumn(name = "extended_question_fk")
private ExtendedQuestion extendedQuestion;
}
With this mapping, I run the following test case and it worked :
QuestionCompletion questionCompletion = new QuestionCompletion();
ExtendedQuestion extendedQuestion = new ExtendedQuestion();
QuestionCompletionAnswer answer = new QuestionCompletionAnswer();
answer.setQuestionCompletion(questionCompletion);
answer.setExtendedQuestion(extendedQuestion);
questionCompletion.setAnswers(Collections.singletonList(answer));
...
session.save(extendedQuestion);
session.save(questionCompletion);

Categories

Resources