I'm new on Java. I'm doing a HTTPDeleting with JpaRepository(DeleteById) and i recived the following error: ConstraingViolationException - FK_QUESTION_ID cannot be null.
#Entity
#Data
public class Question {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "QUESTION_ID")
private int id;
#Column(nullable = false)
private String title;
#Column(nullable = false)
private String description;
// QUESTION_ID => FOREING KEY COLUMN
#OneToMany(orphanRemoval = true, fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "FK_QUESTION_ID")
private List<Answer> answers;
#Column(name = "LIKE_COUNT")
private int likeCount;
#Column(name = "INTEREST_AREA_ID", nullable = false)
private int interestAreaId;
#Column(name = "USER_ID", nullable = false)
private int userId;
#Column
private boolean active;
#Column(name = "CREATED_DT", nullable = false)
private Date createdDate;
}
#Entity
#Data
public class Answer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ANSWER_ID")
private int answerId;
#Column
private String description;
#Column(name = "LIKE_COUNT")
private int likeCount;
#Column(name = "USER_ID", nullable = false)
private int userId;
#Column
private boolean active;
#Column(name = "CREATED_DT", nullable = false)
private Date createdDate;
#Column(name = "FK_QUESTION_ID")
private int questionId;
}
I read that use CascadeType.Delete is not a good practice, so i used orphanRemoval but even in this way doesn't worked.
questionRepository.deleteById(id);
Your setup is almost correct, except you need to change couple of things here and there. Two important things are:
mappedBy = "question" in the parent entity (Question) targets question field of child entity (Answer)
#JoinColumn should be placed in the child class, through which you specify the column name FK_QUESTION_ID in the Answer table.
Hope, it helps.
#Entity
#Data
public class Question {
#OneToMany(mappedBy = "question", orphanRemoval = true, fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<Answer> answers;
}
#Entity
#Data
public class Answer {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "FK_QUESTION_ID", nullable = false)
private Question question;
// since you seem to use lombok, you can also use
// answer.getQuestion().getId() instead of this method
public int getQuestionId() {
return this.question.getId(); // question lazy fetching
}
}
Related
enter image description here
I am trying to map some entities to tables in MySQL database using Spring Boot JPA. I have a problem with one of the tables because in that one too many foreign keys are added. I highlighted the columns in the picture. I suppose that the problem might be linked with the fact that the Tutorial table has either One to Many or Many to Many relations with the other 3 tables, but I am not sure
#Entity(name = "authors")
public class Author {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "author_id")
private Long authorId;
#Column(name = "first_name", nullable = false, length = 100)
private String firstName;
#Column(name = "last_name", nullable = false, length = 100)
private String lastName;
#Column(name = "email", length = 320, unique = true)
private String email;
#Column(name = "job_title", length = 255)
private String jobTitle;
#Lob
#Type(type = "org.hibernate.type.BinaryType")
#Column(name = "profile_picture")
private byte[] profilePicture;
#Column(name = "about", length = 2000)
private String about;
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "author_id")
private List<Tutorial> tutorials;
}
#Entity(name = "categories")
public class Category {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "category_id")
private Long categoryId;
#Column(name = "category_name", nullable = false, unique = true, length = 100)
private String categoryName;
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "category_id")
private List<Tutorial> tutorials;
}
#Entity(name = "tutorials")
public class Tutorial {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "tutorial_id")
private Long tutorialId;
#Column(name = "tutorial_title", nullable = false, length = 150)
private String tutorialTitle;
#Column(name = "tutorial_description", nullable = false, length = 2000)
private String tutorialDescription;
#Column(name = "time_to_complete")
private Integer timeToComplete;
#Column(name = "date_published")
private Long datePublished;
#Column(name = "last_updated")
private Long lastUpdated;
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
},
mappedBy = "tutorials")
private List<User> users = new ArrayList<>();
#ManyToOne(fetch = FetchType.EAGER)
private Category category;
#ManyToOne(fetch = FetchType.EAGER)
private Author author;
}
Tutorials is the table where the problems appear as 4 foreign keys are generate instead of two
#Entity(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id")
private Long userId;
#Column(name = "first_name", nullable = false, length = 100)
private String firstName;
#Column(name = "last_name", nullable = false, length = 100)
private String lastName;
#Column(name = "user_name", nullable = false, unique = true, length = 100)
private String userName;
#Column(name = "age")
private Integer age;
#Column(name = "email", length = 320, unique = true)
private String email;
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
#JoinTable(name = "users_tutorials",
joinColumns = { #JoinColumn(name = "user_id") },
inverseJoinColumns = { #JoinColumn(name = "tutorial_id") })
private List<Tutorial> tutorials = new ArrayList<>();
}
Try this changes:
remove #JoinColumn(name = "author_id")from Author and place in Tutorial:
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "author_id")
private Author author;
remove #JoinColumn(name = "category_id")from Category and place it in Tutorial as well:
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "category_id")
private Author author;
To get more information look here: Baeldung - Hibernate One to Many
I have a entity bean with a relation #ManyToOne that is in join on one column.
#Entity
#Table(name = "work_order")
public class WorkOrder implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
private Long id;
#NotNull
#Column(name = "id_order", nullable = false)
private String idOrder;
#Column(name = "description")
private String description;
#Enumerated(EnumType.STRING)
#Column(name = "status")
private StatusOrder status;
#Column(name = "creation_date")
private Instant creationDate;
#Column(name = "closing_date")
private Instant closingDate;
#Column(name = "client_id")
private Long clientId;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST) #NotFound (action = NotFoundAction.IGNORE)
#Fetch(FetchMode.JOIN)
#JoinColumn(name = "account", insertable = false, updatable = false, nullable = true)
private AnagraficaClienti account;
And the second Entity
#Entity
#Table(name = "es_account")
public class AnagraficaClienti implements Serializable {
private static final long serialVersionUID = 1L;
// da rimettere a #NotNull
#Column(name = "fk_cod_azienda", nullable = true)
private String fk_cod_azienda;
#Id
#NotNull
#Column(name = "account", nullable = false)
private String account;
// da rimettere a #NotNull
#Column(name = "tipo_cli_for", nullable = true)
private String tipoClienteFornitore;
#Column(name = "tipo_account", nullable = true)
private String tipoAccount;
....
The "es_account" table has three not nullable primary key(fk_cod_azienda, account, tipo_cli_for) and the relation with the "work_order" table is by account column.
My problem is that sometimes it is possible that the user insert or update WorkOrder with a null account value and that is not avoid by AnagraficaClienti entity because it expects a non null(and not duplicate) value.
Are there any possible way to bypass the join with AnagraficaClienti when account is null?
In my point of view, #ManyToOne is violate OOP design principle due to the creation of redundant relation. Instead, i always create a #OneToMany relation with a list of related entities. To specify the relation as nullable, just add the nullable=true property in #JoinColumn. With #ManyToOne, you must specify property optional=true. Lets try and see if it works.
WorkOrder
#Entity
#Table(name = "work_order")
public class WorkOrder implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
private Long id;
#NotNull
#Column(name = "id_order", nullable = false)
private String idOrder;
#Column(name = "description")
private String description;
#Enumerated(EnumType.STRING)
#Column(name = "status")
private StatusOrder status;
#Column(name = "creation_date")
private Instant creationDate;
#Column(name = "closing_date")
private Instant closingDate;
#Column(name = "client_id")
private Long clientId;
AnagraficaClienti
#Entity
#Table(name = "es_account")
public class AnagraficaClienti implements Serializable {
private static final long serialVersionUID = 1L;
// da rimettere a #NotNull
#Column(name = "fk_cod_azienda", nullable = true)
private String fk_cod_azienda;
#Id
#NotNull
#Column(name = "account", nullable = false)
private String account;
// da rimettere a #NotNull
#Column(name = "tipo_cli_for", nullable = true)
private String tipoClienteFornitore;
#Column(name = "tipo_account", nullable = true)
private String tipoAccount;
#OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE} )
#JoinColumn(name = "account", nullable = true)
private List<WorkOrder> workOrders;
When you want to insert the work order to the database:
workOrderRepository.save(workOrder);
When you want to create the relationship:
AnagraficaClienti client = anagraficaClientiRepository.findById(...);
client.getWorkOrders().add(newWorkOrder);
Is it possible to create one column for bi-directional relationship?
My Entities:
#Entity
#Table(name = "subscription")
#Proxy(lazy = false)
public class Subscription {
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "subscription_id")
private long id;
#Column(name = "userid", nullable = false)
private String userId;
#Column(name = "saledate", nullable = false)
#Temporal(TemporalType.DATE)
private Date saleDate;
#Column(name = "finishdate", nullable = false)
#Temporal(TemporalType.DATE)
private Date finishDate;
#Column(name = "price", nullable = false)
private long price;
#Column(name = "description", nullable = false)
private String description;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "subscription")
private List<VisitDate> visitDates = new ArrayList<>();
}
#Entity
#Table(name="visitdate")
public class VisitDate {
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "id")
private long id;
private long subscription;
#Column(name = "date", nullable = false)
#Temporal(TemporalType.DATE)
private Date date;
#ManyToOne
#JoinColumn(name="subscription_id")
private Subscription associatedSub;
}
Now I see two columns in the database and little bit confused.
I don't want to save the same data but want to display a report about how many users visit on some day.
Update:
You are not required to create a separate field "subscription" in VisitDate class. Hibernate will automatically create a field to store subscription id. The code needs to be slightly changed.
#Entity
#Table(name = "subscription")
public class Subscription {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name = "subscription_id")
private long id;
#Column(name = "userid", nullable = false)
private String userId;
#Column(name = "saledate", nullable = false)
#Temporal(TemporalType.DATE)
private Date saleDate;
#Column(name = "finishdate", nullable = false)
#Temporal(TemporalType.DATE)
private Date finishDate;
#Column(name = "price", nullable = false)
private long price;
#Column(name = "description", nullable = false)
private String description;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "associatedSub")
private List<VisitDate> visitDates = new ArrayList<>();
}
Notice, that I have changed the mappedBy property to point at associatedSub in the above class.
#Entity
#Table(name="visitdate")
public class VisitDate {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name = "id")
private long id;
#Column(name = "date", nullable = false)
#Temporal(TemporalType.DATE)
private Date date;
#ManyToOne
#JoinColumn(name="subscription_id")
private Subscription associatedSub;
}
You can use Uni-Directional relationship for the same purpose. You just need to add a list/set of Visits for a particular subscription, You don't have to create a list of subscription for a particular visit.
for reference Visit [Java JPA] :(https://en.wikibooks.org/wiki/Java_Persistence/OneToMany#Undirectional_OneToMany.2C_No_Inverse_ManyToOne.2C_No_Join_Table_.28JPA_2.0_ONLY.29)!
#Entity
#Table(name = "subscription")
#Proxy(lazy = false)
public class Subscription {
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "subscription_id")
private long id;
#Column(name = "userid", nullable = false)
private String userId;
#Column(name = "saledate", nullable = false)
#Temporal(TemporalType.DATE)
private Date saleDate;
#Column(name = "finishdate", nullable = false)
#Temporal(TemporalType.DATE)
private Date finishDate;
#Column(name = "price", nullable = false)
private long price;
#Column(name = "description", nullable = false)
private String description;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "subscription")
private List<VisitDate> visitDates = new ArrayList<>();
}
#Entity
#Table(name="visitdate")
public class VisitDate {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "id")
private long id;
private long subscription;
#Column(name = "date", nullable = false)
#Temporal(TemporalType.DATE)
private Date date;
}
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.
I have 2 entities linked together using oneToMany mapping. In the Dao layer when i apply restrictions on the linked entity it fetches all the results. It seems that the restrictions are not working on the linked entity. I want to apply restrictions on both entities.
DAO
Criteria criteria = createEntityCriteria()
.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
.add(Restrictions.eq("status" , "APPROVED"))
.addOrder(Order.desc("approvedAt"))
.createAlias("purchaseDemandDetails" , "pds")
.add(Restrictions.ge("pds.approvedQuantity" , 1));
return criteria.list();
PurchaseDemand.java
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToOne
#JoinColumn(name = "created_by", referencedColumnName = "id")
private User createdBy;
#Column(name = "created_at")
private Date createdAt;
#ManyToOne
#JoinColumn(name = "updated_by" , referencedColumnName = "id")
private User updatedBy;
#Column(name = "updated_at")
private Date updatedAt;
#ManyToOne
#JoinColumn(name = "approved_by" , referencedColumnName = "id")
private User approvedBy;
#Column(name = "approved_at")
private Date approvedAt;
#Column(name = "status")
private String status;
#OneToMany(mappedBy = "purchaseDemand", cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
private Set<PurchaseDemandDetail> purchaseDemandDetails = new HashSet<PurchaseDemandDetail>();
public void setPurchaseDemandDetails(Set<PurchaseDemandDetail> purchaseDemandDetails)
{
this.purchaseDemandDetails.addAll(purchaseDemandDetails);
}
public Set<PurchaseDemandDetail> getPurchaseDemandDetails()
{
return this.purchaseDemandDetails;
}
PurchaseDemandDetail.java
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToOne
#JoinColumn(name = "purchase_demand_id",referencedColumnName = "id")
#JsonIgnore
private PurchaseDemand purchaseDemand;
#ManyToOne
#JoinColumn(name = "product_id",referencedColumnName = "id")
private Product product;
#Column(name = "requested_quantity", nullable = false)
#NotNull(message = "Quantity is required")
private int requestedQuantity;
#Column(name = "approved_quantity", nullable = false)
#NotNull(message = "Quantity is required")
private int approvedQuantity;
}