Hibernate not saving into join table - ManyToOne and JoinTable relation - java

I'm using SpringBoot 2.0.2 with hibernate 5.2.17 and MariaDB 10.1
Customer:
#Entity
public class Customer extends Company {
#ManyToOne(fetch = FetchType.LAZY, cascade = ALL)
#JoinTable(name = "company_services",
joinColumns = #JoinColumn(name = "companyId", insertable = true, updatable = true),
inverseJoinColumns = #JoinColumn(name = "serviceId", insertable = true, updatable = true))
private Service service;
Service:
#Entity
public class Service {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
With Hibernate log enabled I can see this:
Hibernate:
/* insert com.example.company.Customer
*/ insert
into
`
companies` (
...
)
values
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
Hibernate:
select
last_insert_id()
Hibernate:
/* insert com.example.company.Customer
*/ insert
into
`
company_service` (
`serviceId`, `companyId`
)
values
(?, ?)
20:43:53.524 TRACE [cid: none] [session: none] org.hibernate.type.descriptor.sql.BasicBinder -- binding parameter [1] as [BIGINT] - [3]
20:43:53.524 TRACE [cid: none] [session: none] org.hibernate.type.descriptor.sql.BasicBinder -- binding parameter [2] as [BIGINT] - [3]
But when I confirm the result in the db the companies table is OK but the company_services record does not get saved.
Is that a known bug? I'm missing something?

Hibernate does not use join table for many to one relationship. It uses a join column for many to one relationship.

Related

Why to use Set in OneToMany Mapping in hibernate

I have two tables with a one-to-many relationship. I want to fetch those records and insert into another database which having same table by changing the primary key.
My application entity class
#Entity
#Table(name = "EM_APPLICATION")
public class ApplicationTable {
#Id
private int APPLICATION_ID;
#Id
private String CUSTOMER_ID;
private String LAST_NAME;
private String FIRST_NAME;
#OneToMany( fetch = FetchType.EAGER,cascade = CascadeType.ALL)
#JoinColumns({ #JoinColumn(name = "CUSTOMER_ID", referencedColumnName = "CUSTOMER_ID"),
#JoinColumn(name = "APPLICATION_ID", referencedColumnName = "APPLICATION_ID") })
private Set<AddressTable> address;
//Getters and setters
}
Address entity class..
#Entity
#Table(name="EM_APPL_ADDRESS")
public class AddressTable{
#Id
private int APPLICATION_ID;
#Id
private String CUSTOMER_ID;
#Id
private String ADDRESS_TYPE;
//Getters and setters
}
I have to execute a method for fetching records from DB using hibernate:
public void execute(String applId, String customerId) {
Session session = HibernateQAUtil.openSession();
Transaction tx = session.beginTransaction();
String hql = "FROM ApplicationTable WHERE CUSTOMER_ID =:CUSTOMER_ID AND APPLICATION_ID =:APPLICATION_ID";
Query query = session.createQuery(hql);
query.setParameter("CUSTOMER_ID", customerId);
query.setParameter("APPLICATION_ID", Integer.parseInt(applId));
List<ApplicationTable> list = query.list();
tx.commit();
session.close();
ApplicationTable applVO = list.get(0);
insertApplication(applVO );
}
After fetching the records, I am changing APPLICATION_ID, CUSTOMER_ID and some other columns in address table and after inserting in another database.
private void insertApplication(ApplicationTable emApplVO) {
applVO.setAPPLICATION_ID(123456);
applVO.setCUSTOMER_ID("88888888");
Set<AddressTable> addressSet = emApplVO.getAddress();
for (AddressTable address : addressSet) {
address.setAPPLICATION_ID(123456);
address.setCUSTOMER_ID("88888888");
address.setZIP(500032);
}
Session session1 = HibernateUtil.openSession();
Transaction beginTransaction = session1.beginTransaction();
session1.save(emApplVO);
beginTransaction.commit();
session1.close();
}
Hibernate queries in console log are... (below mentioned queries are too large so copied to some extent only..)
Hibernate: select em_applica0_.CUSTOMER_ID as CUSTOMER1_0_,em_applica0_.APPLICATION_ID as APPLICAT2_0_,em_applica0_.ARCHIVE_IND as ARCHIVE8_0_ where em_applica0_.CUSTOMER_ID=? and em_applica0_.APPLICATION_ID=?
Hibernate: select address0_.CUSTOMER_ID as CUSTOMER1_0_1_, address0_.APPLICATION_ID as APPLICAT2_0_1_, address0_.ADDRESS_TYPE as ADDRESS3_1_0_ where em_applica0_.CUSTOMER_ID=? and em_applica0_.APPLICATION_ID=?
Hibernate: insert into EM_APPLICATION (CUSTOMER_ID, APPLICATION_ID, APPLICATION_NBR, APPLICATION_STATUS, APPLICATION_TYPE) values (?, ?, ?, ?)
Hibernate: insert into EM_APPL_ADDRESS (CUSTOMER_ID, APPLICATION_ID, ADDRESS_TYPE) values (?, ?, ?)
Question 1: in the insert method, I have assigned address to addresSet and made some changes in addresSet, after making those changes, I am not assigned the addressSet to applVO (i.e. not written applVO.setAddress(addresSet )) but it inserted a record with updated values into the Address table. What is happening here?
When I am changing code inside insertApplication(ApplicationTable emApplVO) method to
private void insertApplication(ApplicationTable emApplVO) {
applVO.setAPPLICATION_ID(123456);
applVO.setCUSTOMER_ID("88888888");
Set<AddressTable> addressSet = emApplVO.getAddress();
Set<AddressTable> newAddressSet = new HashSet<AddressTable>();
for (AddressTable address : newAddressSet) {
address.setAPPLICATION_ID(emApplVO.getAPPLICATION_ID());
address.setCUSTOMER_ID(emApplVO.getCUSTOMER_ID());
address.setZIP(500032);
newAddressSet.add(address);
}
emApplVO.setAddress(null);
emApplVO.setAddress(newAddressSet);
Session session1 = HibernateUtil.openSession();
Transaction beginTransaction = session1.beginTransaction();
session1.save(emApplVO);
beginTransaction.commit();
session1.close();
}
Hibernate queries in console log are... It also executing update ...
Hibernate: select em_applica0_.CUSTOMER_ID as CUSTOMER1_0_,em_applica0_.APPLICATION_ID as APPLICAT2_0_,em_applica0_.ARCHIVE_IND as ARCHIVE8_0_ where em_applica0_.CUSTOMER_ID=? and em_applica0_.APPLICATION_ID=?
Hibernate: select address0_.CUSTOMER_ID as CUSTOMER1_0_1_, address0_.APPLICATION_ID as APPLICAT2_0_1_, address0_.ADDRESS_TYPE as ADDRESS3_1_0_ where em_applica0_.CUSTOMER_ID=? and em_applica0_.APPLICATION_ID=?
Hibernate: insert into EM_APPLICATION (CUSTOMER_ID, APPLICATION_ID, APPLICATION_NBR, APPLICATION_STATUS, APPLICATION_TYPE) values (?, ?, ?, ?)
Hibernate: insert into EM_APPL_ADDRESS (CUSTOMER_ID, APPLICATION_ID, ADDRESS_TYPE) values (?, ?, ?)
update EM_APPL_ADDRESS set CUSTOMER_ID=?, APPLICATION_ID=? where CUSTOMER_ID=? and APPLICATION_ID=? and ADDRESS_TYPE=?
Question 2: why is the update query executed?
Question 3: while using List<AddressTable> instead of Set<AddressTable>, I got some errors. What is the difference?

JPA/HIbernate integrity constraint violated on cascade

I have the following entity which has a relation with WorkFlowDetail class:
#Entity()
#DynamicUpdate()
#Table(schema = "RACQUISTI", name = "RICHIESTA_DI_ACQUISTO")
public class RichiestaDiAcquisto {
#Id
#Column(name = "ID_RICHIESTA_ACQUISTO")
#SequenceGenerator(name="SEQ_GEN",sequenceName="RICHIESTA_DI_ACQUISTO_SEQ", allocationSize=1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ_GEN")
private int id;
#OneToOne(fetch=FetchType.LAZY, cascade = {CascadeType.ALL})
#JoinColumn(name="ID_RICHIESTA_ACQUISTO")
private WorkFlowDetail workFlowDetail;
.....
}
The WorkFlowDetail entity is:
#Entity()
#Table(schema = "RACQUISTI", name = "WORKFLOW_DETAIL")
public class WorkFlowDetail {
#Id
#Column(name = "ID_RICHIESTA_ACQUISTO")
private int idRichiestaDiAcquisto;
#Column(name = "ID_WORKFLOW_INSTANCE")
private String idWorkFlowInstance;
....
}
The table WORKFLOW_DETAIL has a foreign key on RICHIESTA_DI_ACQUISTO primary key.
When I execute the following code:
RichiestaDiAcquisto entity = new RichiestaDiAcquisto();
getSession().saveOrUpdate(entity);
I receive this error:
Hibernate: select RICHIESTA_DI_ACQUISTO_SEQ.nextval from dual
Hibernate: select workflowde_.ID_RICHIESTA_ACQUISTO, workflowde_.CURRENT_NODE_ID as CURRENT_2_2_, workflowde_.DATA_MOD as DATA_MOD3_2_, workflowde_.ID_WORKFLOW_INSTANCE as ID_WORKF4_2_, workflowde_.WORKFLOW_TOKEN as WORKFLOW5_2_ from RACQUISTI.WORKFLOW_DETAIL workflowde_ where workflowde_.ID_RICHIESTA_ACQUISTO=?
Hibernate: select richiedent_.ID_RICHIESTA_ACQUISTO, richiedent_.COGNOME as COGNOME2_0_, richiedent_.GODIVA_ANAGRAFICA_ID as GODIVA_A3_0_, richiedent_.NOME as NOME4_0_ from RACQUISTI.RICHIEDENTE richiedent_ where richiedent_.ID_RICHIESTA_ACQUISTO=?
Hibernate: insert into RACQUISTI.RICHIESTA_DI_ACQUISTO (ACQUISTO_DI_BENI, CONFERMA_CAPITOLO, CONFERMA_ELEGIBILITA, COSTO_ACQUISTO, DATA_MOD, DESCRIZIONE_ACQUISTO, GARA_NAZIONALE_CATALOGO, ID_ANAG_MOD, LUOGO_DI_CONSEGNA, SIGLA_ESPERIMENTO, STATUS, STRUTTURA_PAGANTE, ID_RICHIESTA_ACQUISTO) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into RACQUISTI.WORKFLOW_DETAIL (CURRENT_NODE_ID, DATA_MOD, ID_WORKFLOW_INSTANCE, WORKFLOW_TOKEN, ID_RICHIESTA_ACQUISTO) values (?, ?, ?, ?, ?)
WARN 2017-04-28 10:14:28,817 [http-nio-8080-exec-6] org.hibernate.engine.jdbc.spi.SqlExceptionHelper - SQL Error: 2291, SQLState: 23000
ERROR 2017-04-28 10:14:28,819 [http-nio-8080-exec-6] org.hibernate.engine.jdbc.spi.SqlExceptionHelper - ORA-02291: integrity constraint (RACQUISTI.WORKFLOW_DETAIL_FK1) violated - parent key not found
Your insert statement is trying to insert a value into column which doesn't exist in the RACQUISTI table
I found a solution. I modify the annotation on main class child (WorkFlowDetail):
#Entity()
#DynamicUpdate()
#Table(schema = "RACQUISTI", name = "RICHIESTA_DI_ACQUISTO")
public class RichiestaDiAcquisto {
#Id
#Column(name = "ID_RICHIESTA_ACQUISTO")
#SequenceGenerator(name="SEQ_GEN",sequenceName="RICHIESTA_DI_ACQUISTO_SEQ", allocationSize=1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="SEQ_GEN")
private int id;
#OneToOne(fetch=FetchType.LAZY, mappedBy = "richiestaDiAcquisto", cascade = {CascadeType.ALL})
private WorkFlowDetail workFlowDetail;
.....
}
Then I modified the child class:
#Entity()
#Table(schema = "RACQUISTI", name = "WORKFLOW_DETAIL")
public class WorkFlowDetail {
#GenericGenerator(name = "generator", strategy = "foreign",
parameters = #org.hibernate.annotations.Parameter(name = "property", value = "richiestaDiAcquisto"))
#Id
#GeneratedValue(generator = "generator")
#Column(name = "ID_RICHIESTA_ACQUISTO", unique = true, nullable = false)
private int idRichiestaAcquisto;
#OneToOne(fetch = FetchType.LAZY)
#PrimaryKeyJoinColumn
private RichiestaDiAcquisto richiestaDiAcquisto;
....
}
Before to save the main class (RichiestaDiAcquisto) I have to put the father on the child class by the following code:
richiestaDiAcquisto.getWorkFlowDetail().setRichiestaDiAcquisto(richiestaDiAcquisto);

Unnecessary updates in #OneToMany

I have model object as follows
Employee.java
#Entity
#Table(name = "EMPLOYEE")
public class Employee {
#Id
#SequenceGenerator(name = "emp_seq", sequenceName = "seq_employee")
#GeneratedValue(generator = "emp_seq")
#Column(name = "EMPLOYEE_ID")
private Integer employeeId;
#Column(name = "EMPLOYEE_NAME")
private String employeeName;
}
Department.java
#Entity
#Table(name = "DEPARTMENT")
public class Department {
#Id
#Column(name = "DEPARTMENT_ID")
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer departmentId;
#Column(name = "DEPARTMENT_NAME")
private String departmentName;
#Column(name = "LOCATION")
private String location;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "DEPARTMENT_ID")
private List<Employee> employees = new ArrayList<>();
}
while saving this it is generating two extra update statements.
Test class
Employee e1 = new Employee();
e1.setEmployeeName("Employee-1");
Employee e2 = new Employee();
e2.setEmployeeName("Employee-2");
Department d = new Department();
d.setDepartmentName("Test");
d.setLocation("Test");
d.getEmployees().add(e1);
d.getEmployees().add(e2);
em.getTransaction().begin();
em.persist(d);
em.getTransaction().commit();
on committing the following statements are generated...
Hibernate: insert into DEPARTMENT (DEPARTMENT_NAME, LOCATION, DEPARTMENT_ID) values (?, ?, ?)
Hibernate: insert into EMPLOYEE (EMPLOYEE_NAME, EMPLOYEE_ID) values (?, ?)
Hibernate: insert into EMPLOYEE (EMPLOYEE_NAME, EMPLOYEE_ID) values (?, ?)
**Hibernate: update EMPLOYEE set DEPARTMENT_ID=? where EMPLOYEE_ID=?
**Hibernate: update EMPLOYEE set DEPARTMENT_ID=? where EMPLOYEE_ID=?
my question here is why 2 extra update(marked by *) statements are needed?
That's the order on which Hibernate does the operations normally. Take a look at this
https://docs.jboss.org/hibernate/orm/4.2/javadocs/org/hibernate/event/internal/AbstractFlushingEventListener.html#performExecutions%28org.hibernate.event.spi.EventSource%29
According to this documentation:
Execute all SQL (and second-level cache updates) in a special order so
that foreign-key constraints cannot be violated:
When you add Employees to a Department, employees must have a Department ID so that's the reason why Hibernate do an extra update.
If you want to avoid it you can create first the department, and then the employees adding manually Department id
Due to the #OneToMany #JoinColumn(name = "DEPARTMENT_ID") that annotates the attribute Department.employees the table
EMPLOYEE has a foreign key to the table DEPARTMENT. When you persiste the new department with the two employees a new row is inserted into the table DEPARTMENT and two rows are inserted into the table EMPLOYEE but the column DEPARTMENT_ID is null. Then two updates are executed to set this column and relate the EMPLOYEE rows with the DEPARTMENT row.
The question is why this is not done in one step, i.e. instead of executing the following:
Hibernate: insert into DEPARTMENT (DEPARTMENT_NAME, LOCATION, DEPARTMENT_ID) values (?, ?, ?)
Hibernate: insert into EMPLOYEE (EMPLOYEE_NAME, EMPLOYEE_ID) values (?, ?)
Hibernate: insert into EMPLOYEE (EMPLOYEE_NAME, EMPLOYEE_ID) values (?, ?)
**Hibernate: update EMPLOYEE set DEPARTMENT_ID=? where EMPLOYEE_ID=?
**Hibernate: update EMPLOYEE set DEPARTMENT_ID=? where EMPLOYEE_ID=?
the following should be executed:
Hibernate: insert into DEPARTMENT (DEPARTMENT_NAME, LOCATION, DEPARTMENT_ID) values (?, ?, ?)
Hibernate: insert into EMPLOYEE (EMPLOYEE_NAME, EMPLOYEE_ID, DEPARTMENT_ID) values (?, ?, ?)
Hibernate: insert into EMPLOYEE (EMPLOYEE_NAME, EMPLOYEE_ID, DEPARTMENT_ID) values (?, ?, ?)

Constraint Violation when persisting One To Many relation

In a spring mvc application using hibernate and MySQL, I am getting the following constraint violation exception:
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException:
Cannot add or update a child row: a foreign key constraint fails
(`mybd`.`hl7_documententity`, CONSTRAINT `hl7_documententity_ibfk_1`
FOREIGN KEY (`ptcode`, `ptcodesystem`)
REFERENCES `hl7_generalcode` (`code`, `codesystem`))
The problem occurs when I try to save a DocumentEntity containing a property of type GeneralCode, both of which are defined below.
I have read many postings and blogs on this error, but none seem to resolve my problem. How can I resolve this error?
Here is the DocumentEntity class:
#Entity
#Table(name = "hl7_documententity")
public class HL7DocumentEntity extends BaseEntity{
//other properties
#ManyToOne
#JoinColumns({ #JoinColumn(name = "ptcode", referencedColumnName = "code"),
#JoinColumn(name = "ptcodesystem", referencedColumnName = "codesystem")
})
private HL7GeneralCode providertype;
//getters and setters
}
Here is the GeneralCode class:
#Entity
#Table(name = "hl7_generalcodes")
public class HL7GeneralCode implements Serializable{
private static final long serialVersionUID = -8620565054475096516L;
#EmbeddedId
private HL7EmbedCodePK codePk;
#OneToMany(mappedBy = "providertype")
private Set<HL7DocumentEntity> documententities;
////////////getters and setters
}
Here is the code from the controller:
HL7GeneralCode authcode = processGeneralCode(grandkid);
HL7GeneralCode testcode = this.clinicService.findGeneralCodeByPK(authcode.getCodePk().getCode(), authcode.getCodePk().getCodesystem());
if(testcode==null){
authcode.addDocumententity(mydent);
this.clinicService.savehl7GeneralCode(authcode);
mydent.setProvidertype(authcode);
//this next line throws the error
this.clinicService.savehl7DocumentEntity(mydent);
}else{
//other stuff
}
Here is the dao method:
#Repository
public class JpaSomethingRepositoryImpl implements SomethingRepository {
#PersistenceContext
private EntityManager em;
#Override
#Transactional
public void savehl7DocumentEntity(HL7DocumentEntity de) {
HL7GeneralCode code = de.getProvidertype();
if(code !=null && code.getCodePk()==null){//HL7GeneralCode is not persistent. We don't support that
throw new IllegalStateException("Cannot persist an adress using a non persistent HL7GeneralCode");
}
System.out.println("=========================== inside jpaCdaRespository.saveDocEntity(de)");
de.setProvidertype(null);
if(code.getDocumententities()!=null){
ArrayList<HL7DocumentEntity> addrList = new ArrayList<HL7DocumentEntity>();
addrList.addAll(code.getDocumententities());
addrList.remove(de);
Set<HL7DocumentEntity> myaddrs = new HashSet<HL7DocumentEntity>(addrList);
code.setDocumententities(myaddrs);
}
code = em.merge(code);
de.setProvidertype(code);
code.addDocumententity(de);
if (de.getId() == null) {
System.out.println("[[[[[[[[[[[[ about to persist de ]]]]]]]]]]]]]]]]]]]]");
em.persist(de);
} else {
System.out.println("]]]]]]]]]]]]]]]]]] about to merge de [[[[[[[[[[[[[[[[[[[[[");
de = em.merge(de);
}
}
}
The executed SQL statement and the actual values that hibernate is trying to insert via the sql are:
[[[[[[[[[[[[ about to persist de ]]]]]]]]]]]]]]]]]]]]
DEBUG SQL - insert into hl7_documententity (author_id, authpar_id, entitytype, id_extension, id_root, ptcode, ptcodesystem, id) values (?, ?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into hl7_documententity (author_id, authpar_id, entitytype, id_extension, id_root, ptcode, ptcodesystem, id) values (?, ?, ?, ?, ?, ?, ?, ?)
TRACE BasicBinder - binding parameter [1] as [INTEGER] - <null>
TRACE BasicBinder - binding parameter [2] as [INTEGER] - <null>
TRACE BasicBinder - binding parameter [3] as [VARCHAR] - <null>
TRACE BasicBinder - binding parameter [4] as [VARCHAR] - NI
TRACE BasicBinder - binding parameter [5] as [VARCHAR] - nullFlavor
TRACE BasicBinder - binding parameter [6] as [VARCHAR] - UNK
TRACE BasicBinder - binding parameter [7] as [VARCHAR] - HL7NullFlavor
TRACE BasicBinder - binding parameter [8] as [INTEGER] - 32787
WARN SqlExceptionHelper - SQL Error: 1452, SQLState: 23000
ERROR SqlExceptionHelper - Cannot add or update a child row: a foreign key constraint fails (`docbd`.`hl7_documententity`, CONSTRAINT `hl7_documententity_ibfk_1` FOREIGN KEY (`ptcode`, `ptcodesystem`) REFERENCES `hl7_generalcode` (`code`, `codesystem`))
INFO AbstractBatchImpl - HHH000010: On release of batch it still contained JDBC statements
WARN warn - Handler execution resulted in exception
You can read the EmbedCodePK class code by clicking on this link.
You can read the entire stack trace by clicking on this link.
Here is a link to the code for the BaseEntity class.
Change this:
#OneToMany(mappedBy = "providertype")
private Set<HL7DocumentEntity> documententities;
To this:
#OneToMany(fetch = FetchType.LAZY)
#JoinTable(name = "Link_Documents", joinColumns = {#JoinColumn(name = "codePk", unique = true)}, inverseJoinColumns = {#JoinColumn(name = "change_this_with_primary_key_variable_name_from_HL7DocumentEntity")})
private Set<HL7DocumentEntity> documententities;
And in HL7DocumentEntity change as follows:
This
#ManyToOne
#JoinColumns({ #JoinColumn(name = "ptcode", referencedColumnName = "code"),
#JoinColumn(name = "ptcodesystem", referencedColumnName = "codesystem")
})
private HL7GeneralCode providertype;
Change to this:
#ManyToOne(fetch = FetchType.LAZY)
#JoinTable(name = "Link_Documents", joinColumns = {#JoinColumn(name = "change_this_with_primary_key_variable_name_from_HL7DocumentEntity")}, inverseJoinColumns = {#JoinColumn(name = "codePk")})
private HL7GeneralCode providertype;
I think you have to change "change_this_with_primary_key_variable_name_from_HL7DocumentEntity" with "id" like it is in BaseEntity but take a look at your sql table, you willsee there the correct name.
I hope you notice How I told JPA to use the same "Link_Documents" table for linking the 2 tables. I think this is were your mistake is. Just make sure to change where I told you with the correct variable name and I think it should work
you know i would start by cleaning up this code.
Though the hibernate documentation might tell you placing those annotations on the field variables is ok,,, its a bad idea as a standard. field level requires hibernate to create a proxy before the field can be access in the case of lazy fetches. so just get use to doing it correctly and you wont have the misfortune of finding issues. move all those annotations to the getters as property level access doesnt require the proxy.
field level variables should be private.
you are casting an Integer to an Integer in there... some of that stuff makes me not want to look at it and truthfully will probably be a silly bit of laziness that ends up being your problem.
while you are at it go ahead an initialize the set with new HashSet(0); Things like that. Still broken update us.

cascade PERSIST during synchronization exception

I have a problem when trying to persist new entities. I'm using Eclipselink 2.4.2 as entity manager. My BaseDao class in the store method flushes and refreshes entity after persisting it as new (persist->flush->refresh). All is happening in a single transaction.
My entities look like this (the part I'm concerned about):
TrustEntity {
#OneToMany(fetch = FetchType.LAZY, mappedBy = "trust", cascade = {CascadeType.ALL})
#PrivateOwned
private List<TrustIncentiveRateEntity> trustIncentiveRates;
}
TrustIncentiveRateEntity {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "TRUST_ID", nullable = false)
private TrustEntity trust;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "trustIncentiveRate", cascade = {CascadeType.ALL})
#PrivateOwned
private List<TrustIncentiveRateValueEntity> trustIncentiveRateValues;
}
TrustIncentiveRateValueEntity {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "TRUST_INCENTIVE_RATE_ID", nullable = false)
private TrustIncentiveRateEntity trustIncentiveRate;
}
I'm creating a new Trust entity, instantiate a TrustIncentiveRateEntity list, create one new element in it, instantiate a TrustIncentiveRateEntity and create one new element in it.
During debugging I could see that all the references, in both ways, are correct.
Now, when I try to persist this here is what happens:
Log from server:
FINE: SELECT SEQ_TRUST.NEXTVAL FROM DUAL
FINE: SELECT SEQ_TRUST_INCENTIVE_RATE.NEXTVAL FROM DUAL
FINE: SELECT SEQ_TRUST_INCENTIVE_RATE_VALUE.NEXTVAL FROM DUAL
FINE: INSERT INTO TRUST (TRUST_ID, ACTION_DATE, ACTION_USER_ID, IS_ACTIVE, CREATION_DATE, CREATION_USER_ID, IS_INCENTIVE_ACTIVE, IS_NO_CREDIT_LIMIT, PROCESS_CURRENT_STATUS, REMARKS, STATUS_INCENTIVE, TRUST_CODE, TRUST_NAME, TRUST_TYPE, UPDATE_DATE, UPDATE_USER_ID, VERSION_OPT_LOCK, STRUCTURAL_ORG_UNIT_SALES_ID, STRUCTURAL_ORG_UNIT_ID, USER_ID) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
bind => [38007, 2013-04-26 09:46:31.582, 1003186, true, 2013-04-26 07:46:34.659, 1003186, true, false, OPEN, null, OPEN, 100058, 741963852, T, null, null, 1, 387, 387, 1003186]
FINE: INSERT INTO TRUST_INCENTIVE_RATE (TRUST_INCENTIVE_RATE_ID, CREATION_DATE, CREATION_USER_ID, EQUIPMENT_SIZE, EXTENDED_EQ_GROUP_ID, RATE_BASIS, UPDATE_DATE, UPDATE_USER_ID, VERSION_OPT_LOCK, TRADE_ID, TRUST_ID) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
bind => [15001, 2013-04-26 07:46:39.862, 1003186, 20, 2, B, null, null, 1, 144001, 38007]
FINE: INSERT INTO TRUST_INCENTIVE_RATE_VALUE (TRUST_INCENTIVE_RATE_VALUE_ID, CREATION_DATE, CREATION_USER_ID, EFFECTIVE_DATE, EXPIRY_DATE, INCENTIVE, STATUS, UPDATE_DATE, UPDATE_USER_ID, VERSION_OPT_LOCK, CURRENCY_CODE, TRUST_INCENTIVE_RATE_ID) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
bind => [14007, 2013-04-26 07:46:39.955, 1003186, 2013-04-26 00:00:00.0, 9999-12-31 00:00:00.0, 12, OPEN, null, null, 1, USD, 15001]
FINE: SELECT TRUST_ID, ACTION_DATE, ACTION_USER_ID, IS_ACTIVE, CREATION_DATE, CREATION_USER_ID, IS_INCENTIVE_ACTIVE, IS_NO_CREDIT_LIMIT, PROCESS_CURRENT_STATUS, REMARKS, STATUS_INCENTIVE, TRUST_CODE, TRUST_NAME, TRUST_TYPE, UPDATE_DATE, UPDATE_USER_ID, VERSION_OPT_LOCK, STRUCTURAL_ORG_UNIT_SALES_ID, STRUCTURAL_ORG_UNIT_ID, USER_ID FROM TRUST WHERE (TRUST_ID = ?)
bind => [38007]
FINE: SELECT TRUST_INCENTIVE_RATE_ID, CREATION_DATE, CREATION_USER_ID, EQUIPMENT_SIZE, EXTENDED_EQ_GROUP_ID, RATE_BASIS, UPDATE_DATE, UPDATE_USER_ID, VERSION_OPT_LOCK, TRADE_ID, TRUST_ID FROM TRUST_INCENTIVE_RATE WHERE (TRUST_ID = ?)
bind => [38007]
FINE: SELECT TRUST_INCENTIVE_RATE_VALUE_ID, CREATION_DATE, CREATION_USER_ID, EFFECTIVE_DATE, EXPIRY_DATE, INCENTIVE, STATUS, UPDATE_DATE, UPDATE_USER_ID, VERSION_OPT_LOCK, CURRENCY_CODE, TRUST_INCENTIVE_RATE_ID FROM TRUST_INCENTIVE_RATE_VALUE WHERE (TRUST_INCENTIVE_RATE_ID = ?)
bind => [15001]
So far so good, but when the transaction is commited by EntityManager I get the following exception:
WARNING: java.lang.IllegalStateException: During synchronization a new object was found through a relationship that was not marked cascade PERSIST: my.package.entity.TrustIncentiveRateEntity#1fa1df7.
at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.discoverUnregisteredNewObjects(RepeatableWriteUnitOfWork.java:303)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.calculateChanges(UnitOfWorkImpl.java:706)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.commitToDatabaseWithChangeSet(UnitOfWorkImpl.java:1498)
at org.eclipse.persistence.internal.sessions.UnitOfWorkImpl.issueSQLbeforeCompletion(UnitOfWorkImpl.java:3151)
at org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork.issueSQLbeforeCompletion(RepeatableWriteUnitOfWork.java:345)
at org.eclipse.persistence.transaction.AbstractSynchronizationListener.beforeCompletion(AbstractSynchronizationListener.java:158)
at org.eclipse.persistence.transaction.JTASynchronizationListener.beforeCompletion(JTASynchronizationListener.java:68)
at com.sun.enterprise.transaction.JavaEETransactionImpl.commit(JavaEETransactionImpl.java:435)
at com.sun.enterprise.transaction.JavaEETransactionManagerSimplified.commit(JavaEETransactionManagerSimplified.java:855)
at com.sun.ejb.containers.BaseContainer.completeNewTx(BaseContainer.java:5136)
at com.sun.ejb.containers.BaseContainer.postInvokeTx(BaseContainer.java:4901)
at com.sun.ejb.containers.BaseContainer.postInvoke(BaseContainer.java:2045)
at com.sun.ejb.containers.BaseContainer.postInvoke(BaseContainer.java:1994)
at com.sun.ejb.containers.EJBLocalObjectInvocationHandler.invoke(EJBLocalObjectInvocationHandler.java:222)
at com.sun.ejb.containers.EJBLocalObjectInvocationHandlerDelegate.invoke(EJBLocalObjectInvocationHandlerDelegate.java:88)
at $Proxy241.save(Unknown Source)
Which for me seems strange, like the EM tries to actually store TrustIncentiveRateValueEntity before TrustIncentiveRateEntity and can't see TrustIncentiveRateEntity.
After looking at similar threads I've added CascadeType.PERSIST to the #ManyToOne annotation over trustIncentiveRate field in TrustIncentiveRateValueEntity class. After that the situation looks like this: EM inserts the entities like before AND THEN it gets nextval from SEQ_TRUST_INCENTIVE_RATE and tries to insert TrustIncentiveRateValueEntity again (with the new id, but the rest of the field values remain the same). It results in constrain violation, as I have a unique constrain on cross-section of some of this table columns. Exception, transaction rolled back, I am still sad.
My store method in the BaseDao class:
#TransactionAttribute(TransactionAttributeType.MANDATORY)
public T_ENTITY store(T_ENTITY entity) {
if (!entity.isNewlyCreated()) {
T_ENTITY mergedEntity = em.merge(entity);
flush();
return mergedEntity;
} else {
try {
em.persist(entity);
flush();
refresh();
} catch (RuntimeException exc) {
entity.resetPersistentFlag();
throw exc;
}
return entity;
}
}
But calling the em.persist(entity) directly, without flush/refresh causes the same problem.
The logic of the service call:
#Override
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public TrustEntity save(TrustEntity dto) {
TrustEntity trust = trustDao.store(trust);
workflowConversation.triggerWorkflow(); // doesn't do anything to any of the entities when they are freshly created
return trust;
}
Anybody could help me in identifying what could be wrong with this?
You cannot have a OneToMany that uses both a mappedby, making it bidirectional, and a joincolumn marking it as unidirectional. They conflict and are causing you issues.
When you mark a relationship as mappedby, you specify that all information and control of the relationship is on the other side - that includes the joincolumn info. try:
TrustEntity {
#OneToMany(fetch = FetchType.LAZY, mappedBy = "trust", cascade = {CascadeType.ALL})
#PrivateOwned
private List<TrustIncentiveRateEntity> trustIncentiveRates;
}
TrustIncentiveRateEntity {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "TRUST_ID", nullable = false)
private TrustEntity trust;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "trustIncentiveRate", cascade = {CascadeType.ALL})
#PrivateOwned
private List<TrustIncentiveRateValueEntity> trustIncentiveRateValues;
}

Categories

Resources