I am doing Many To One relationship using JPA . While deleting child object from Child table it's throwing exception.
Below is My code:
Project.java
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="projectName")
private String projectName;
#Column(name="projectDesc")
private String projectDesc;
#ManyToOne(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
#JoinColumn(name="companyId")
Company.java
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="compName")
private String compName;
#Column(name="address")
private String address;
Below is Insert code:
InserAction.java
public static void main(String[] args) {
Company comp2 = new Company();
comp2.setCompName("IBM");
comp2.setAddress("Bangalore");
Project pro2 = new Project();
pro2.setProjectName("Management System");
pro2.setProjectDesc("System");
pro2.setCompany(comp2);
EntityManager entityManager = EntityManagerUtil.getEmf().createEntityManager();
try{
EntityTransaction entr = entityManager.getTransaction();
entr.begin();
entityManager.persist(pro2);
entr.commit();
}
}
DeleteAction.java
EntityManager entityManager = EntityManagerUtil.getEmf()
.createEntityManager();
try {
EntityTransaction entr = entityManager.getTransaction();
entr.begin();
Project project = entityManager.find(Project.class,5);
entityManager.remove(project);
entr.commit();
}
Exception is
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails (`prabha`.`project`, CONSTRAINT `FK_project_companyId` FOREIGN KEY (`companyId`) REFERENCES `company` (`id`))
Error Code: 1451
Call: DELETE FROM company WHERE (id = ?)
bind => [1 parameter bound]
Query: DeleteObjectQuery(com.demo.manytoone.Company#301db5ec)
While deleting project object from Project table it' throwing above exception how can I over come this.
#ManyToOne(cascade={CascadeType.MERGE, CascadeType.PERSIST}, fetch=FetchType.EAGER)
The above code will resolve your issue. If you observe source code of annotation ManyToOne it has an array of cascade type so you can map multiple cascade types
You should not use CascadeType.ALL, try using CascadeType.MERGE
The meaning of CascadeType.ALL is that the persistence will propagate (cascade) all EntityManager operations (PERSIST, REMOVE, REFRESH, MERGE, DETACH) to the relating entities.
It seems in your case to be a bad idea, as removing an Project would lead to removing the related Company when you are using CAscadeType.ALL. As a Company can have multiple projects, the other projects would become orphans. However the inverse case (removing the Company) would make sense - it is safe to propagate the removal of all projects belonging to a Company if this company is deleted.
You can also use various CascadeTypes, for e.g.
cascade = {CascadeType.PERSIST,CascadeType.MERGE}.
So use all those that applied to you.
For more info.
You got MySQLIntegrityConstraintViolationException. It means there are tables in the database you're binding. You should map when you set up the many to one relationship tables. Company should be able to get more than one project. So you have to define the list of projects.
Project.java
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="projectName")
private String projectName;
#Column(name="projectDesc")
private String projectDesc;
#ManyToOne
#JoinColumn(name="companyId")
private Company company;
Company.java
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="compName")
private String compName;
#Column(name="address")
private String address;
#OneToMany(mappedBy="company", fetch=FetchType.EAGER)
private List<Project> projects;
Related
i have the empty database in mysql, and two java entites. One of those have unidirectional relation. When hibernate tryes to create tables, i got the error:
Error executing DDL "alter table entry add constraint FK6ov2k83sx3crs9v3q8nvjuf1j foreign key (category_name) references category (name)" via JDBC Statement
There are my entites:
#Entity
public class Entry {
#Id
#GeneratedValue( strategy = GenerationType.IDENTITY)
private int id;
#Column
private String myfio;
private String descr;
#OneToOne(cascade = CascadeType.ALL)
private Category category;
}
And the second:
#Entity
#Table(name="category")
public class Category {
#Id
#Column
private String name;
}
How to create tables without errors?
OneToOne relationship shares the same id. So it should be the same type, but the first one is int (actually it should be Integer to allow null value for the transient (not stored) entities) and the second one is String. It seems you simply missed a line. Also, it worths to mention Vlad Mihalchea’s article https://vladmihalcea.com/the-best-way-to-map-a-onetoone-relationship-with-jpa-and-hibernate/
I am completely new to JPA and ORM concept, so I am hoping someone can lucidly explain what might be the problem with my code.
#Entity
#Table(name = "PERSISTENCE_customer")
public class Customer implements Serializable {
private static final long serialVersionUID = 1005220876458L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
#OneToMany (cascade = CascadeType.ALL, orphanRemoval = true)
private List<CustomerOrder> orders;
}
#Entity
#Table(name = "PERSISTENCE_ORDER")
public class CustomerOrder implements Serializable{
private static final long serialVersionUID = 199102142021L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotNull
String status;
#NotNull
#OneToMany (cascade = CascadeType.ALL, orphanRemoval = true)
private List<LineItem> lineItems = new ArrayList();
#NotNull
private String orderNumber;
................
................
}
#Entity
#Table(name = "PERSISTENCE_LINEITEM")
public class LineItem implements Serializable {
private static final long serialVersionUID = 1991217202100959L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#NotNull
private Integer quantity;
#NotNull
private Part part;
}
Initially, the Customer entity is created through the user interface and persisted successfully. Later, the customer has an order and I update the Customer with CustomerOrder as follow:
private void UpdateCustomer(Customer customer) {
FacesContext fc = FacesContext.getCurrentInstance();
List<ShoppingCartItem> shoppingCart = getShoppingCart();
CustomerOrder order = new CustomerOrder();
List<CustomerOrder> orders = customer.getOrders();
order.setLastUpdated(new Date());
order.setOrderNumber(getInvoiceNumber());
List<LineItem> lineItems = shoppingCart
.stream()
.map(e -> (new LineItem(e.getPart(), e.getQuantity())))
.collect(Collectors.toList());
order.setLineItems(lineItems);
order.setStatus("Pending Shipment");
order.setTotal(getTotal());
orders.add(order);
customer.setOrders(orders);
try {
updateOrders(customer, orders);
fc.addMessage(null,
new FacesMessage("Customer order added successfuly"));
} catch (ListServiceException e) {
FacesMessage errMsg = new FacesMessage(FacesMessage.SEVERITY_FATAL,
"Error while adding customer order: ", e.getMessage());
fc.addMessage(null, errMsg);
}
}
private void updateOrders(Customer cust, List<CustomerOrder> orders) throws ListServiceException {
try { //em is the EntityManager injected as the field member
if (em != null) {
if (em.isOpen()) {
Customer c = getCustomer(cust.getId());
c.setOrders(orders);
em.merge(c);
} else {
logger.severe("Entity manager is closed");
}
else {
logger.severe("Entity manager is NULL");
}
} catch (Exception e) {
throw ThrowListServiceException.wrapException(e);
}
}
Once the EntityManage merges I get the following exception. I was under the impression that I don't need to explicitly persist the LineItem and CustomerOrder entities myself. I thought that the JPA will persist all the entities in the object graph. Why am I getting this exception? (I am using GlassFish 5.1 server with EclipseLink JPA)
Thanks in advance.
Internal Exception: java.sql.SQLIntegrityConstraintViolationException: Column 'ORDERS_ID' cannot accept a NULL value.
Error Code: 30000
Call: INSERT INTO PERSISTENCE_CUSTOMER_PERSISTENCE_ORDER (orders_ID, Customer_ID) VALUES (?, ?)
bind => [2 parameters bound]
Query: DataModifyQuery(name="orders" sql="INSERT INTO PERSISTENCE_USER_PERSISTENCE_ORDER (orders_ID, User_ID) VALUES (?, ?)")
at org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:331)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:905)
...............................
.................................
Caused by: java.sql.SQLIntegrityConstraintViolationException: Column 'ORDERS_ID' cannot accept a NULL value.
Update
Using the IDE (Netbeans) debugger, I stepped through the code, and as I predicted, during the entity merge the JPA does not add new entities that are part of the object graph to the persistence context. For instance, in the updateOrders() method when I try to update the existing Customer object with a list of new CustomerOrder object, the JPA doesn't figure out that the elements in the List are not part of the persistence context yet and they need to be added. As a result, I had to modify my code to first add the List to the persistence context and then merge the Customer object with the newly persisted List. By doing so I no longer get the exception.
By the way, at moment, all the mapping relationships are unidirectional because I didn't see any reasons to use bidirectional mapping. However, would I gain anything by making these mappings bidirectionals?
Your OneToMany mapping is missing join specification or mappedBy value
I noticed that.
Firstly You should commit new order to database.Then you should link it with user.I'm not sure if this solves your problem but this is a problem.Can you check it ?
In my opinion, if you keep customer information in your Order entity, it may solve this problem.
#ManyToOne ()
private Customer customer;
And in your Customer entity you should put mappedBy=customer for orders field.
After that, instead of giving orders for customer, you can give customer for a specific order. In my opinion it will achieve a better relationship mapping;
order.setCustomer(customer);
I hope i understood it right and this will solve your problem. When you give customer detail for order, you dont need to give orderlist detail for the same customer. Only one of them should be enough.
I have two tables, Projects and TransitionAction. Both Projects and TransitionAction have a column request_no which is used perform join between them. The entity classes are as below:-
Project.java
#Entity
public class Project implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#SequenceGenerator(schema = "public", name="project_id_seq_gen",sequenceName="project_id_seq",initialValue=1,allocationSize=1)
#GeneratedValue(strategy= GenerationType.SEQUENCE,generator="project_id_seq_gen")
private Integer id;
#Column(name = "request_no")
private String request_no;
#Column(name = "title")
private String title;
#Column(name = "department")
private String department;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "requestNo")
private Set<TransitionAction> tacts;
#ManyToOne
#JoinColumn(name = "status_id")
private Status status;
#ManyToOne
#JoinColumn(name = "level_id")
private ProjectLevel level;
TransitionAction.java
#Entity
#Table(name = "transitionaction")
public class TransitionAction implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "request_no")
private String request_no;
#Column(name = "actionDate")
private Date actionDate;
The code used to retrieve the list of projects as follows:-
public interface UserInfoRepository extends JpaRepository<UserInfo, Long> {
UserInfo findByUserName(String userName);
#Query("SELECT project FROM Project project Join project.tacts pta where project.request_no= pta.request_no and project.status.id=1")
List<Project> getAllUserProjects();
}
I am getting could not extract ResultSet error. When I checked console, i found the following query being generated:
select
distinct project0_.id as id1_1_,
project0_.department as departme2_1_,
project0_.level_id as level_id6_1_,
project0_.user_nodal_officer as user_nod3_1_,
project0_.request_no as request_4_1_,
project0_.status_id as status_i7_1_,
project0_.title as title5_1_
from
project project0_
inner join
transitionaction tacts1_
on project0_.id=tacts1_.request_no
I am not getting why project. id is joined with tact.request_no and creating the error
Operator does not exist: integer = character varying
So you want to get all TransitionAction linked to Project by requestNo
You can achieve this by
adding #ManyToOne Mapping in TransactionAction like this
#ManyToOne(fetch = FetchType.LAZY)
private Project project;
Now you need to modify your query like this
#Query("SELECT project FROM Project project where project.status.id=1")
List<Project> getAllUserProjects();
To get all TransactionAction for a given project
Set<TransitionAction> allTatcts = project.getTacts();
You dont need to add join in query. Hibernate will take care of that once you pull TransactionAction from Project by an entity.
Edit 1:
But why did my query fail ? Why primary key of project joined with
request_no of TransitionAction ?
Your query failed because while doing #OneTOMany relation yodidn'tnt define #JoinColumn or #JoinTable which is a unidirectional mapping.
In this case, Hibernate will use primarykey column to map.
And since type of primarykey and column is different thus the error.
Without describing any physical mapping (no #JoinColumn or
#JoinTable), a unidirectional one to many with join table is used. The
table name is the concatenation of the owner table name, _, and the
other side table name. The foreign key name(s) referencing the owner
table is the concatenation of the owner table, _, and the owner
primary key column(s) name. The foreign key name(s) referencing the
other side is the concatenation of the owner property name, _, and the
other side primary key column(s) name. A unique constraint is added to
the foreign key referencing the other side table to reflect the one to
many.
Refer Official doc for more detail
#OneToMany
#JoinTable(
name="table_name",
joinColumns = #JoinColumn( name="columnname")
)
private Set<TransitionAction> tacts;
It is because you are having #Id on request_no in your TransactionAction class and when you join two class then they join on primary keys.
You can use cross join instead.
SELECT project FROM Project project, TransactionAction pta where project.request_no= pta.request_no and project.status.id=1
Or you can have bidirectional mapping. Add the following in your TransactionAction entity class.
#ManyToOne
private Project Project;
And then your query will be as follows.
select pta.project from TransactionAction pta where pta.request_no=pta.project.request_no and pta.project.status.id=1
I'm developing a filing system where I have 3 tables. PROJECTS table consists of projectid, project name and other details (see below). This is an existing class and populated schema and I do not want to modify this part of the application if possible.
Folders table (Called ProjectClassification) consists of folderid and foldername and is the owning side of a unidirectional onetomany relationship.
Project_Folders is a join table. I'm using JPA 2.0 (EclipseLink) and JSF 2.0 as my web framework.
My basic problem is I can't add duplicate records to the join table using a merge operation. MERGE is good for adding records until the owning key already exists, after which point it will only update the join table. I know this is the way it's supposed to work but I need to add new records even if there's a duplicate of the owning key. This will allow me to store different projects in the same folder.
I've looked through some other questions here such as:
onetomany unidirectional with jointable setup using jpa
This says what is needed to add one entity to the other in a join table but i need to know more about how to correctly persist or merge the added entity to the database.
The folder entity class:
#Entity
#Table(name = "PROJECTCLASSIFICATIONS")
public class ProjectClassifications implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int proclassid;
private int projectid;
private String classification;
#OneToMany(cascade = CascadeType.ALL)
#JoinTable(name = "PROJECT_CLASSF_JOIN",
joinColumns = #JoinColumn(name = "proclassid", referencedColumnName = "proclassid"),
inverseJoinColumns = #JoinColumn(name = "projectid", referencedColumnName = "projectid", unique = true))
private Collection<Projects> projects;
public ProjectClassifications() {
}
public ProjectClassifications(String classification) {
this.classification = classification;
}
public ProjectClassifications(int proclassid, int projectid) {
this.proclassid = proclassid;
projects = new ArrayList<Projects>();
}
public ProjectClassifications(Projects newProject) {
projects = new ArrayList<Projects>();
}
public void addProject(Projects newProject) {
if(!getProjects().contains(newProject))
getProjects().add(newProject);
}
....
....
The Project entity class is a pre existing code and I do not want to modify at all if possible:
#Entity
#Table(name = "PROJECTS")
public class Projects {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int projectid;
private String projectName;
private String projectDescription;
#Temporal(javax.persistence.TemporalType.DATE)
private Date startDate;
#Temporal(javax.persistence.TemporalType.DATE)
private Date endDate;
private String commnts;
// foreign keys to parent tables
private int fk_countryid;
private int fk_companyid;
private int fk_employeeid;
#ManyToOne(optional = true)
#JoinColumn(name = "countryid")
private Country country;
....
....
I then use two html select lists to select values for projectid and proclassid which call the following methoid using a JSF managed bean:
public String makeProClassRecord() {
newProClass = new ProjectClassifications(proclassid, projectid);
newProject = proServ.findByProjectId(projectid);
newProClass.addProject(newProject);
facade.update(newProClass);
//facade.save(newProClass);
return showProclass();
}
My questions are:
1) Is MERGE the correct operation used to add records into a join table?
2) Is there a way to add records that contain duplicate keys (foreign keys represented as new records in the join table) using MERGE?
3) Should PERSIST be used to achieve question 2?
4) Would it be better to create an entity for the join table itself and simply use a PERSIST method to insert the records?
Many thanks
So I solved this myself a couple of weeks ago and thought of sharing the answer. Instead of doing merge or persist operations on any of the target entities, I created a Join table and unidirectional OneToMany relationship from the Project entity to the below ProjectFileSystem join table entity and simply did the persist operation using that entity. I need to add duplicate folders for different projects (or store many projects under a single folder item) so it seems more efficient to do the CRUD operations in the actual join table entity rather than from the target entity. Hope this helps:
#Entity
#Table(name = "PROFOLDERSYS_JOIN")
public class ProjectFileSystem implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int foldersysid;
private int proclassid;
private int projectid;
private String projectName;
private String folderName;
public ProjectFileSystem() {
}
public ProjectFileSystem(int proclassid, int projectid,
String projectName, String folderName) {
this.proclassid = proclassid;
this.projectid = projectid;
this.projectName = projectName;
this.folderName = folderName;
}
// getters and setters
}
The method in the bean would be:
public String makeProSys() {
newProSys = new ProjectFileSystem(proclassid, projectid, classification, projectName);
newProject = proServ.findByProjectId(projectid);
projectName = newProject.getProjectName();
newProSys.setProjectName(projectName);
newProClass = facade.findByContactId(proclassid);
classification = newProClass.getClassification();
newProSys.setFolderName(classification);
profilFacade.save(newProSys);
return showProSys();
}
My entities are:
the ID of device which is deiveID
has many-to-many relationship with
the ID of Lib which is rID
my test code is :
two new device entities want to set the same new libentity
the problem is :
if i use the same entitymanager to persist that 2 new device entities, it will be ok.
but if i use 2 different entitymanager instance to persist them ,the error"primary key violation" will come out. I think the entitymanger try to persist the libentity at the second time, which has already been persisted in the first time.
--------------deviceinfo entity ------------------------------------------------
#Entity
#Table(name="deviceInfo")
public class DeviceInfoEntity extends BaseEntity implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long deviceId;
....
#ManyToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY)
#JoinTable(name = "device_lib", joinColumns = #JoinColumn(name = "deviceInfo_id",
referencedColumnName="deviceId"),
inverseJoinColumns = #JoinColumn(name = "lib_id", referencedColumnName="rId"))
private List<LibEntity> resourceList = null;
......
}
-------------------------lib entity ---------------------------------------------
#Entity
#Table(name="lib")
public class LibEntity extends BaseEntity implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long rId;
#ManyToMany(mappedBy = "resourceList", cascade=CascadeType.ALL,
fetch=FetchType.LAZY, targetEntity=DeviceInfoEntity.class)
private List<DeviceInfoEntity> deviceInfolist = null;
.....
}
my test code is:
EntityManagerFactory emFactory = Persistence.createEntityManagerFactory("testPU");
EntityManager em = emFactory.createEntityManager();
LibEntity libEntity = new LibEntity();
DeviceInfoEntity dEntity = new DeviceInfoEntity();
dEntity.setName("dadadada");
dEntity.setLibEntity(libEntity);
DeviceInfoEntity dEntity2 = new DeviceInfoEntity();
dEntity2.setName("dadadadadddddd");
dEntity2.setLibEntity(libEntity);
em.getTransaction().begin();
em.persist(dEntity);
em.getTransaction().commit();
em.close();
EntityManager em2 = emFactory.createEntityManager();
em2.getTransaction().begin();
em2.persist(dEntity2);
em2.getTransaction().commit();
it will have the error:
Unique index or primary key violation: "PRIMARY KEY ON PUBLIC.LIB(RID)"; SQL statement:
INSERT INTO lib (RID) VALUES (?) [23505-165]
but if i use the same EntityManager the error will not happen. Is there anyone know whats the reason? is that caused by cascade=CascadeType.ALL?
You are corrupting your persistence context by assign detached objects to managed objects. Managed object should only reference other managed objects.
For dEntity2 you should set the libEntity to the result of a find(), getReference() or merge() of libEntity.
i.e.
dEntity2.setLibEntity(em2.find(libEntity.getClass(), libEntity.getId());
You could probably also call merge() instead of persist() and it should resolve the detached objects.