Hibernate joining tables with multiple primary keys - java

I have figured out how to join 2 tables with single primary key. But now I need to join 4 tables and some of these table with composite primary keys.
Here is my table picture
And I want to join them, so I generate classes for them:
// Record
#Getter
#Setter
#ToString
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "record")
public class Record implements java.io.Serializable{
private static final long serialVersionUID = 1L;
#Id
#Column(name = "student_id")
private Integer studentId;
#Id
#Column(name = "exam_id")
private Integer examId;
#Column(name = "total_score")
private Integer totalScore;
#Column(name = "student_score")
private Integer studentScore;
#Column(name = "submission_id")
private Integer submissionId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "student_id")
private Student student;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "submission_id")
private Submission submission;
}
// Submission
#Getter
#Setter
#ToString
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "submission")
public class Submission implements java.io.Serializable{
private static final long serialVersionUID = 1L;
#Id
#Column(name = "submission_id")
private Integer submissionId;
#Id
#Column(name = "question_id")
private Integer questionId;
#Column(name = "stu_answer")
private String stuAnswer;
#OneToOne(fetch = FetchType.LAZY, mappedBy = "submission")
private Record record;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "submission")
private Set<Question> question;
}
// Question
#Getter
#Setter
#ToString
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "question")
public class Question implements java.io.Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
#Column(name = "question_id")
private Integer questionId;
#Column(name = "content")
private String content;
#Column(name = "score")
private Integer score;
#Column(name = "is_delete")
private Integer isDelete;
#Column(name = "option_id")
private Integer optionId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "submission_id")
private Submission submission;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "optional_id")
private Optional optional;
}
// Optional
#Getter
#Setter
#ToString
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "optional")
public class Optional implements java.io.Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
#Column(name = "option_id")
private Integer optionId;
#Column(name = "content")
private String content;
#Column(name = "is_delete")
private Integer isDelete;
#Column(name = "answer")
private String answer;
#OneToOne(fetch = FetchType.LAZY, mappedBy = "optional")
private Question question;
}
// Final class to store information
public class RcdSubQuesOpt {
private Integer studentId;
private Integer examId;
private Integer questionId;
private String stuAnswer;
private String qContent;
private String oContent;
private String answer;
}
And this is code for JPA
#Override
public List<RcdSubQuesOpt> getRcdSubQuesOpt(int studentID, int examId) {
Session session = this.getSession();
List<RcdSubQuesOpt> results;
Transaction transaction = null;
try {
transaction = session.beginTransaction();
CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
CriteriaQuery<RcdSubQuesOpt> criteriaQuery = criteriaBuilder.createQuery(RcdSubQuesOpt.class);
// Try to join tables
Root<Record> pRoot = criteriaQuery.from(Record.class);
pRoot.join("submission", JoinType.INNER);
pRoot.join("question", JoinType.INNER);
pRoot.join("optional", JoinType.INNER);
criteriaQuery.multiselect(
pRoot.get(columns in RcdSubQuesOpt Class......));
// Try to add constraints
Predicate predicate = pRoot.get("examId").in(Arrays.asList(1));
criteriaQuery.where(predicate);
// try to do queries
results = session.createQuery(criteriaQuery).getResultList();
transaction.commit();
} catch (Exception e) {
results = null;
if (transaction != null) {
transaction.rollback();
}
} finally {
session.close();
}
return results;
}
But hibernate throw error as following:
Enitial SessionFactory creation failedA Foreign key refering com.domain.Submission from com.domain.Record has the wrong number of column. should be 2
Exception in thread "main" java.lang.ExceptionInInitializerError
I think it's the composite primary keys problem. But solution I searched is not suitable to solve it. Anyone give me some advice? Thanks!

To reference a composite primary key, you need to use #JoinColumns (plural).
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumns({
#JoinColumn(name = "sub_submission_id", referencedColumnName = "submission_id"),
#JoinColumn(name = "sub_question_id", referencedColumnName = "question_id")
})
private Submission submission;
However, I must admit, I don't understand your model - especially why Submission has a composite PK with question_id. It looks that one Submission has many Questions, why to include question_id as part of Submission PK?
Perhaps, I'm missing something, because the diagram is not fully visible.

Related

foreign key is null Springboot jpa

customer
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "customer")
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "customer_id")
private Long customerId;
#NonNull
#Column(name = "name")
private String name;
#NonNull
#Column(name = "address")
private String address;
#NonNull
#Column(name = "house_no")
private String houseNo;
#Column(name = "active")
private boolean active = true;
#NonNull
#Column(name = "customer_type")
private String customerType;
#NonNull
#Column(name = "pack")
private String pack;
#JsonIgnore
#OneToOne(mappedBy = "customer",cascade = CascadeType.ALL)
private Stb stb;
#JsonIgnore
#OneToOne(mappedBy = "customer",cascade = CascadeType.ALL )
private Payment payment;
#JsonIgnore
#OneToMany(mappedBy = "customer", cascade = CascadeType.ALL)
private List<History> history;
}
History
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "history")
public class History {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "history_id")
private Long historyId;
#Column(name = "amount_paid")
private Long AmountPaid;
#LastModifiedDate
#Column(name = "payment_date")
private String paymentDate;
#Column(name = "due")
private Long due;
#JsonIgnore
#ManyToOne(optional = false,fetch=FetchType.LAZY)
#JoinColumn(name = "customer_f_id",referencedColumnName = "customer_id")
private Customer customer;
payment
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "payment")
public class Payment {
public Long normalPrice =220L;
public Long sportsPrice = 250L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "payment_id")
private Long paymentId;
#Nonnull
#Column(name = "amount_paid")
private Long paid;
#Nullable
#LastModifiedDate
#Column(name = "payment_date")
private String paymentDate;
#Nullable
#Column(name = "due")
private Long due;
#JsonIgnore
#OneToOne(fetch=FetchType.EAGER,optional=false)
#JoinColumn(name = "customer_f_id",referencedColumnName = "customer_id")
private Customer customer;
stb
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "stbox")
public class Stb {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "stb_id")
private Long StbId;
#NonNull
#Column(name = "stbox_number")
private String StboxNumber;
#NonNull
#Column(name = "stbox_id")
private String StboxId;
#NonNull
#Column(name = "stbox_cust_number")
private String StboxCustNumber;
#NonNull
#Column(name = "stbox_type")
private String StboxType;
#JsonIgnore
#OneToOne(optional = false,fetch=FetchType.LAZY)
#JoinColumn(name = "customer_f_id",referencedColumnName = "customer_id")
private Customer customer;
im new to springboot , i just assigned foreign key in many ways i watched many tutorials and blogs and tried it out but it all failed , the foreign key is always set to null, anybody help , thanks in advance :) .
im trying to create a foreign key in stb , payment ,history but i refered it correctly but it ssets to null
Are you setting both sides of the joins?
public class Customer {
public void setStb(Stb stb) {
this.stb = stb;
stb.customer = this;
}
}
or outside maybe?
public void setCustomerStb(Customer customer, Stb stb) {
customer.setStb(stb);
stb.setCustomer(customer);
}
The same also applies to the other joins.

Getting error Parameter value [1465] did not match expected type [java.util.Set (n/a)] while fetchimg data from JPA

I am getting Data from MySql using Spring Data JPA. I getting this error when I am passing provider as 1465 in request.
I have tried Sending Set from the request as well.
My calling code is :
offerList = blackholeDetailRepository.findByProductVMS(ProductType.VMS, blackholeID,
pricingCopyRequestBO.getProviderId().longValue(),
OfferGradeConstant.fromString(pricingCopyRequestBO.getOfferGrade()));
Code in repository is :
#Query("SELECT detail FROM #{#entityName} AS detail JOIN FETCH detail.vmsRestrictedProviders AS provider "+
"WHERE detail.product = :product " + "AND detail.master.id= :masterId "
+ "AND detail.vmsRestrictedProviders = :providerId " + "AND provider.offerGrade= :offerGrade")
Set<BlackholeDetailEntity> findByProductVMS(#Param("product") ProductType product, #Param("masterId") Long masterId,
#Param("providerId") Long providerId, #Param("offerGrade") OfferGradeConstant offerGrade);
BlackholeDetailEntity Entity is :
#Entity
#Table(name = "blackhole_detail")
#Data
#EqualsAndHashCode(callSuper = true)
public class BlackholeDetailEntity extends PolygonDetailEntity {
private static final long serialVersionUID = 1L;
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(name = "blackhole_entity_mapping",
joinColumns = #JoinColumn(name = "detail_id", referencedColumnName = "id"))
#Column(name = "entity_id")
private Set<Integer> entities;
#Convert(converter = CsvStringSetConverter.class)
private Set<String> restrictedOffers;
#Convert(converter = CsvLongSetConverter.class)
#Column(name = "ll_me_restricted_providers", columnDefinition = "text")
private Set<Long> llMERestrictedProviders;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL,
orphanRemoval = true, mappedBy = "blackHole")
private Set<VmsProviderRestrictionEntity> vmsRestrictedProviders;
#Column(name = "is_restrict_all")
private Boolean restrictAll;
}
VmsProviderRestrictionEntity is :
#Data
#Entity
#ToString(exclude = {"blackHole"})
#EqualsAndHashCode(exclude = "blackHole")
#Table(name = "vms_provider_blackhole_restriction")
public class VmsProviderRestrictionEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Enumerated(EnumType.STRING)
private OfferGradeConstant offerGrade;
#Convert(converter = CsvLongSetConverter.class)
#Column(name = "vms_restricted_providers", columnDefinition = "text")
private Set<Long> providerIds;
#ManyToOne
#JoinColumn(name = "blackhole_id")
private BlackholeDetailEntity blackHole;
}
PolygonDetailEntity.java
#MappedSuperclass
#Data
#ToString(exclude = {"master","masterPricing"})
#EqualsAndHashCode(callSuper = false, exclude = {"master", "masterPricing"})
public class PolygonDetailEntity extends ModifiableAuditableEntity {
private static final long serialVersionUID = -9100537914314976516L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
#JoinColumn(name = "master_id")
private PolygonMasterEntity master;
#ManyToOne
#JoinColumn(name = "master_pricing_id")
private PolygonMasterPricingEntity masterPricing;
#Enumerated(EnumType.STRING)
private ProductType product;
private Integer serviceId;
#Column(columnDefinition = "text")
private String comments;
private String accessTechnology;
#Column(name = "is_active")
private Boolean active;
#Column(name = "is_deleted")
private Boolean deleted;
}

How to use #OnetoMany with Embeddedid

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.

Hibernate One to many mapping override

I am facing a hibernate problem in updainting the join table in one to many mapping with hibernate. Below are my two entity class and join table entity class.
ArticleCategoryMap.java
#Entity
#Table(name = "ARTICLECATEGORYMAP")
public class ArticleCategoryMap {
private static final long serialVersionUID = -5653708523600543988L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column ( name = "id")
Long id;
#ManyToOne(targetEntity = Article.class, fetch = FetchType.EAGER, optional = true, cascade = CascadeType.PERSIST)
#JoinColumn(name = "ARTICLE_ID", nullable = true, insertable = true, updatable = true)
private Article article;
#ManyToOne(targetEntity = Category.class, fetch = FetchType.EAGER, optional = true, cascade = CascadeType.PERSIST)
#JoinColumn(name = "CATEGORY_ID", nullable = true, insertable = true, updatable = true)
private Category category;
//setter and getter
}
Article.java
#Entity
#Table(name = "ARTICLE")
public class Article {
private long id;
private String title;
private String description;
private String keywords;
private String content;
#Id
#GeneratedValue
#Column(name = "ARTICLE_ID")
public long getId() {
return id;
}
//setter and getter
}
Category.java
#Entity
#Table(name = "CATEGORY")
public class Category {
private long id;
private String name;
#OneToMany(cascade = CascadeType.ALL, fetch=FetchType.EAGER)
#JoinTable(
name = "ARTICLECATEGORYMAP",
joinColumns = #JoinColumn(name = "CATEGORY_ID"),
inverseJoinColumns = #JoinColumn(name = "ARTICLE_ID")
)
#CollectionId(
columns = #Column(name="id"),
type=#Type(type="long"),
generator = "sequence"
)
private Collection<Article> articles;
#Id
#GeneratedValue
#Column(name = "CATEGORY_ID")
public long getId() {
return id;
}
#OneToMany(cascade = CascadeType.ALL, fetch=FetchType.EAGER)
#JoinTable(
name = "ARTICLECATEGORYMAP",
joinColumns = #JoinColumn(name = "CATEGORY_ID"),
inverseJoinColumns = #JoinColumn(name = "ARTICLE_ID")
)
#CollectionId(
columns = #Column(name="id"),
type=#Type(type="long"),
generator = "sequence"
)
// setter an getter
}
Now suppose first time I have 2 elements in article table which is mapping to one entry of the category table. so the join table will look something like
Now due to some reason, I want to update the entry where the article entry will map to a new category ID. So the final DB should look like
So My problem Is how can I update this join table.
If you want one to many relationship (1 category have many articles and 1 article to 1 category) you dont need a join table.
The entity classes should look like that:
Category Entity:
Contains a Set of articles:
#Entity
#Table(name = "CATEGORY")
public class Category {
private long id;
private String name;
#OneToMany(mappedBy="category")
private Set<Article> articles;
......
}
Article Entity:
#Entity
#Table(name = "ARTICLE")
public class Article {
#ManyToOne
#JoinColumn(name="id", nullable=false)
private Category category;
private long id;
private String title;
private String description;
private String keywords;
private String content;
.......
}
For more details take a look at hibernate-one-to-many. Hope this helps.
Also move annotation from methods to fields. This:
private long id;
#Id
#GeneratedValue
#Column(name = "CATEGORY_ID")
public long getId() {
return id;
}
Should be:
#Id
#GeneratedValue
#Column(name = "CATEGORY_ID")
private long id;
public long getId() {
return id;
}
Many to many relationship:
At your database you have 3 tables:
CATEGORY
ARTICLE
ARTICLECATEGORYMAP (join table)
For many to many relationship entities would be:
Category Entity:
#Entity
#Table(name = "CATEGORY")
public class Category {
#Id
#GeneratedValue
#Column(name = "CATEGORY_ID")
private long id;
private String name;
#ManyToMany(cascade = { CascadeType.ALL })
#JoinTable(
name = "ARTICLECATEGORYMAP",
joinColumns = { #JoinColumn(name = "CATEGORY_ID") },
inverseJoinColumns = { #JoinColumn(name = "ARTICLE_ID") }
)
Set<Article > articles = new HashSet<>();
.....
}
Article Entity:
#Entity
#Table(name = "ARTICLE")
public class Article {
#Id
#GeneratedValue
#Column(name = "ARTICLE_ID")
private long id;
private String title;
private String description;
private String keywords;
private String content;
#ManyToMany(mappedBy = "articles")
private Set<Category> categories = new HashSet<>();
.......
}
For more info take a look at many-to-many ralationship

Referential integrity constraint violation error in JPA

I am trying to parse a web request and save to database. I have 3 models and first node is virtualDocument. This is the uniq table (according to request url). VirtualRequest table has all erquest bodies and HttpHeaderList table has all thhp headers according to their virtualRequest bean id.
when I tried to save the first log I got and error like this;
org.h2.jdbc.JdbcSQLException: Referential integrity constraint violation: "FK1TW2G47F7A47580KQVMDJWGBQ: PUBLIC.T_VIRTUAL_REQUEST FOREIGN KEY(REQUEST_ID) REFERENCES PUBLIC.T_VIRTUAL_DOCUMENT(DOCUMENT_ID) (65)"; SQL statement:
insert into t_virtual_request (request_id, media_type, method_type, request_url) values (null, ?, ?, ?) [23506-192]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:345) ~[h2-1.4.192.jar:1.4.192]
here is VirtualDocument bean
#Entity
#Table(name = "t_virtual_document")
public class VirtualDocument {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "document_id")
private long documentId;
#Column(name = "real_url", unique = true)
private String realURL; //uniq
#Column(name = "virtual_url", unique = true)
private String virtualURL; //uniq
#Column(name = "simulation_mode", columnDefinition = "varchar(10) default 'STOP'")
private String simulationMode;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "request_id")
private List<VirtualRequest> requestList;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "response_id")
private List<VirtualResponse> responseList;
//getter setter without any annotation
}
here is VirtualRequest bean;
#Entity
#Table(name = "t_virtual_request")
public class VirtualRequest {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "request_id")
private long requestId;
#Column(name = "request_url")
private String requestURL;
#Column(name = "method_type")
private String methodType;
#Column(name = "media_type")
private String mediaType;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "header_id")
private List<HttpHeaderList> requestHeaders;
//getter setter without any annotation
}
here is HeaderList bean;
#Entity
#Table(name = "t_http_headers")
public class HttpHeaderList {
#Id
#Column(name = "header_id")
private long headerId;
#Column(name = "header_key")
private String headerKey;
#Column(name = "header_value")
private String headerValue;
}
I think this is what you want instead:
#Entity
#Table(name = "t_virtual_document")
public class VirtualDocument {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "document_id")
private long documentId;
#Column(name = "real_url", unique = true)
private String realURL; //uniq
#Column(name = "virtual_url", unique = true)
private String virtualURL; //uniq
#Column(name = "simulation_mode", columnDefinition = "varchar(10) default 'STOP'")
private String simulationMode;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "virtualDocument")
private List<VirtualRequest> requestList;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "virtualDocument")
// Note the mappedBy parameter. This points to the property in the entity that owns the relationship (in this case the VirtualResponse).
private List<VirtualResponse> responseList;
//getter setter without any annotation
}
#Entity
#Table(name = "t_virtual_request")
public class VirtualRequest {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "request_id")
private long requestId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "document_id")
private VirtualDocument virtualDocument;
#Column(name = "request_url")
private String requestURL;
#Column(name = "method_type")
private String methodType;
#Column(name = "media_type")
private String mediaType;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "virtualRequest")
private List<HttpHeaderList> requestHeaders;
//getter setter without any annotation
}
#Entity
#Table(name = "t_http_headers")
public class HttpHeader { /*Note this is a more appropriate name for the entity since it holds the data of a single header.*/
#Id
#Column(name = "header_id")
private long headerId;
#Column(name = "header_key")
private String headerKey;
#Column(name = "header_value")
private String headerValue;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "request_id")
private VirtualRequest virtualRequest
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "response_id")
private VirtualResponse virtualResponse;
}
Updated the answer to add mapping the headers to the request entity.

Categories

Resources