Spring Boot 2.4.0, DB is MySql 8.
Data is fetched every 15 seconds from remote with REST and storing it to MySql DB with saveAll().
Which call the save() method for all the given entities.
All data has set ID.
And I am expecting that if there is no such id at DB - it will be inserted.
If such ID is already presented at DB - it will be updated.
Here is snipped from the console:
Hibernate:
insert
into
iot_entity
(controller_ref, description, device_id, device_ref, entity_type_ref, hw_address, hw_serial, image_ref, inventory_nr, ip6address1, ip6address2, ip_address1, ip_address2, latlng, location, mac_address, name, params, status, tenant, type, id)
values
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
...
2020-12-05 23:18:28.269 ERROR 15752 --- [ restartedMain] o.h.e.jdbc.batch.internal.BatchingBatch : HHH000315: Exception executing batch [java.sql.BatchUpdateException: Duplicate entry '1' for key 'iot_entity.PRIMARY'], SQL: insert into iot_entity (controller_ref, description, device_id, device_ref, entity_type_ref, hw_address, hw_serial, image_ref, inventory_nr, ip6address1, ip6address2, ip_address1, ip_address2, latlng, location, mac_address, name, params, status, tenant, type, id) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2020-12-05 23:18:28.269 WARN 15752 --- [ restartedMain] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 1062, SQLState: 23000
2020-12-05 23:18:28.269 ERROR 15752 --- [ restartedMain] o.h.engine.jdbc.spi.SqlExceptionHelper : Duplicate entry '1' for key 'iot_entity.PRIMARY'
2020-12-05 23:18:28.269 DEBUG 15752 --- [ restartedMain] o.s.orm.jpa.JpaTransactionManager : Initiating transaction rollback after commit exception
org.springframework.dao.DataIntegrityViolationException: could not execute batch; SQL [insert into iot_entity (controller_ref, description, device_id, device_ref, entity_type_ref, hw_address, hw_serial, image_ref, inventory_nr, ip6address1, ip6address2, ip_address1, ip_address2, latlng, location, mac_address, name, params, status, tenant, type, id) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)]; constraint [iot_entity.PRIMARY]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute batch
Here is how to fetch and to save look like:
#Override
#SneakyThrows
#Scheduled(fixedDelay = 15_000)
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void fetchAndStoreData() {
IotEntity[] entities = restTemplate.getForObject(properties.getIotEntitiesUrl(), IotEntity[].class);
log.debug("ENTITIES:\n{}", mapper.writerWithDefaultPrettyPrinter().writeValueAsString(entities));
if (entities != null && entities.length > 0) {
entityRepository.saveAll(List.of(entities));
} else {
log.warn("NO entities data FETCHED !!!");
}
}
This method runs every 15 seconds.
Entity:
#Data
#Entity
#NoArgsConstructor
#EqualsAndHashCode(of = {"id"})
#ToString(of = {"id", "deviceId", "entityTypeRef", "ipAddress1"})
public class IotEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private Integer id;
// other fields
and Repository:
public interface EntityRepository extends JpaRepository<IotEntity, Integer> {
}
Here is snipped for iot entity at JSON format:
2020-12-05 23:18:44.261 DEBUG 15752 --- [pool-3-thread-1] EntityService : ENTITIES:
[ {
"id" : 1,
"controllerRef" : null,
"name" : "Local Controller Unterföhring",
"description" : "",
"deviceId" : "",
...
So ID is definitely set.
Also, batching is enabled for a project. It shouldn't have any impact on saving.
I could not understand why it tries to insert a new entity instead of update the existing one?
Why it couldn't distinguish the difference between the old and new entities?
UPDATE:
Implemented Persistable for Entity:
#Data
#Entity
#NoArgsConstructor
#EqualsAndHashCode(of = {"id"})
#ToString(of = {"id", "deviceId", "entityTypeRef", "ipAddress1"})
public class IotEntity implements Serializable, Persistable<Integer> {
private static final long serialVersionUID = 1L;
#Id
private Integer id;
#Override
public boolean isNew() {
return false;
}
#Override
public Integer getId() {
return this.id;
}
However, it fails with the same exception - Duplicate entry '1' for key 'iot_entity.PRIMARY'
If I will add #GeneratedValue like the following:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
It wouldn't fail. However, it will update the ID value by itself.
For example, it fetched with id = 15:
[ {
"id" : 15,
"carParkRef" : 15,
"name" : "UF Haus 1/2",
And should be saved like following:
In fact it has id = 2 instead:
And it is incorrect.
Tried to add to storing service:
private final EntityManager entityManager;
...
List.of(carParks).forEach(entityManager::merge);
Fails with the same exception (with or without implementing Persistable). It tries to insert the value - insert into ... Duplicate entry '15' for key '... .PRIMARY'
Snippet from application.yml:
spring:
# ===============================
# = DATA SOURCE
# ===============================
datasource:
url: jdbc:mysql://localhost:3306/demo_db
username: root
password: root
initialization-mode: always
# ===============================
# = JPA / HIBERNATE
# ===============================
jpa:
show-sql: true
generate-ddl: true
hibernate:
ddl-auto: update
properties:
hibernate:
format_sql: true
generate_statistics: true
Here you could see pom file content.
How to fix this issue?
The problem is likely that, since the #Id is not marked with #GeneratedValue, Spring Data assumes all detached (transient) entities passed to save()/saveAll() should have EntityManager.persist() invoked on them.
Try making IotEntity implement Persistable and returning false from isNew(). This will tell Spring Data to always use EntityManager.merge() instead, which should have the desired effect (i.e. inserting nonexistent entities and updating existing ones).
Looks like I found the root of this behaviour.
Main App launcher look like:
#AllArgsConstructor
#SpringBootApplication
public class Application implements CommandLineRunner {
private final DataService dataService;
private final QrReaderServer qrReaderServer;
private final MonitoringService monitoringService;
#Override
public void run(String... args) {
dataService.fetchAndStoreData();
monitoringService.launchMonitoring();
qrReaderServer.launchServer();
}
All 3 steps have strict execution sequence. And the first one has to repeat for updating data locally if it is needed. Two other just servers which work with stored data only.
Where the first method look like:
#Scheduled(fixedDelay = 15_000)
public void fetchAndStoreData() {
log.debug("START_DATA_FETCH");
carParkService.fetchAndStoreData();
entityService.fetchAndStoreData();
assignmentService.fetchAndStoreData();
permissionService.fetchAndStoreData();
capacityService.fetchAndStoreData();
log.debug("END_DATA_FETCH");
}
Also, this execution is scheduled as well.
When the app starts it tried to execute this fetching twice:
2020-12-14 14:00:46.208 DEBUG 16656 --- [pool-3-thread-1] c.s.s.s.data.impl.DataServiceImpl : START_DATA_FETCH
2020-12-14 14:00:46.208 DEBUG 16656 --- [ restartedMain] c.s.s.s.data.impl.DataServiceImpl : START_DATA_FETCH
2 threads run at the same catch and store in parallel - trying to insert data. (tables are recreated at every start).
All later fetches are fine, they are executed only by #Sceduled thread.
If comment #Sceduled - it will work fine without any Exceptions.
SOLUTION:
Added additional boolean property to service class:
#Getter
private static final AtomicBoolean ifDataNotFetched = new AtomicBoolean(true);
#Override
#Scheduled(fixedDelay = 15_000)
#Order(value = Ordered.HIGHEST_PRECEDENCE)
public void fetchAndStoreData() {
ifDataNotFetched.set(true);
log.debug("START_DATA_FETCH");
// fetch and store data with `saveAll()`
log.debug("END_DATA_FETCH");
ifDataNotFetched.set(false);
}
And control the value after the application is started:
#Value("${sharepark.remote-data-fetch-timeout}")
private int dataFetchTimeout;
private static int fetchCounter;
#Override
public void run(String... args) {
waitRemoteDataStoring();
monitoringService.launchMonitoring();
qrReaderServer.launchServer();
}
private void waitRemoteDataStoring() {
do {
try {
if (fetchCounter == dataFetchTimeout) {
log.warn("Data fetch timeout reached: {}", dataFetchTimeout);
}
Thread.sleep(1_000);
++fetchCounter;
log.debug("{} Wait for data fetch one more second...", fetchCounter);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
} while (DataServiceImpl.getIfDataNotFetched().get() && fetchCounter <= dataFetchTimeout);
}
Spring Data JPA uses combination of #version #Id field to decide the whether to merge or insert.
null #id and null #version would mean new record hence insert
if #id is present #version field is used to decide whether to merge or insert.
Update is only invoked when (update .... where id = xxx and version = 0)
Beacuse you have #id and #version missing, its trying to insert, because underlysing system decided this is new record and when run sql u get error.
Can you pls try with #GeneratedValue(strategy = GenerationType.AUTO)
This worked for me.
Related
For some reason with this setup, when saving the transaction with a nested ticket. It will create both and then connect the two. However when I use the transaction repository to find the transaction it will have an attached ticket however when I use the ticket repository and find the ticket, it doesn't have the attached transaction. I generated this relationship with jhipster jdl, not sure what is going wrong here.
Transaction.java
...
#JsonIgnoreProperties(value = { "person", "event", "transaction", "nameTags" }, allowSetters = true)
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(unique = true)
private Ticket tickets;
public Ticket getTickets() {
return this.tickets;
}
public Transaction tickets(Ticket ticket) {
this.setTickets(ticket);
return this;
}
public void setTickets(Ticket ticket) {
this.tickets = ticket;
}
...
Ticket.java
...
#JsonIgnoreProperties(value = { "tickets", "membershipLevel", "person", "event" }, allowSetters = true)
#OneToOne(mappedBy = "tickets")
private Transaction transaction;
public Transaction getTransaction() {
return this.transaction;
}
public Ticket transaction(Transaction transaction) {
this.setTransaction(transaction);
return this;
}
public void setTransaction(Transaction transaction) {
if (this.transaction != null) {
this.transaction.setTickets(null);
}
if (transaction != null) {
transaction.setTickets(this);
}
this.transaction = transaction;
}
...
Edit:
Here is the generated SQL output, it is showing here just a normal select wihtout joins from both but transaction has the ticket_id in the transaction table while ticket is doing the same but has no reference and would need to do a join but is not.
Hibernate: insert into ticket (cost_per_ticket, count, event_id, person_id, picked_up, id) values (?, ?, ?, ?, ?, ?)
Hibernate: insert into transaction (cost_sub_items_purchased, date, donation, event_id, event_donation, generic_sub_items_purchased, membership_level_id, notes, number_of_memberships, person_id, tickets_id, total_amount, id) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
Hibernate: select transactio0_.id as id1_23_, transactio0_.cost_sub_items_purchased as cost_sub2_23_, transactio0_.date as date3_23_, transactio0_.donation as donation4_23_, transactio0_.event_id as event_i10_23_, transactio0_.event_donation as event_do5_23_, transactio0_.generic_sub_items_purchased as generic_6_23_, transactio0_.membership_level_id as members11_23_, transactio0_.notes as notes7_23_, transactio0_.number_of_memberships as number_o8_23_, transactio0_.person_id as person_12_23_, transactio0_.tickets_id as tickets13_23_, transactio0_.total_amount as total_am9_23_ from transaction transactio0_
Hibernate: select ticket0_.id as id1_22_, ticket0_.cost_per_ticket as cost_per2_22_, ticket0_.count as count3_22_, ticket0_.event_id as event_id5_22_, ticket0_.person_id as person_i6_22_, ticket0_.picked_up as picked_u4_22_ from ticket ticket0_
I am trying to insert data of user actions using Spring JPA Single table type inheritance. Below are my classes:
RmlAction.java - The super class
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "entityType")
#Getter
#Setter
#NoArgsConstructor
#Table(name = "rml_actions")
public abstract class RmlAction extends RmlBase {
//attributes
}
RmlProjectAction.java
#Entity
#Getter
#Setter
#DiscriminatorValue(value = "PROJECT")
#NoArgsConstructor
public class RmlProjectAction extends RmlAction {
//constructor
}
RmlEfsAction.java
#Entity
#Getter
#Setter
#DiscriminatorValue(value = "EFS")
#NoArgsConstructor
public class RmlEfsAction extends RmlAction {
//constructor
}
When I try to insert data using ActionRepository, it gives the below error
Hibernate: insert into rml_user (active, creation_date, last_modified_date, object_version, activated, directory_role, efs_id, efs_stack_id, email, login_id, organization, id) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into rml_efs_action (active, creation_date, last_modified_date, object_version, action_params, action_status, action_type, entity_id, job_id, message, published, user, id) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2020-02-03 15:53:43,767 WARN [http-nio-5000-exec-1] org.hibernate.engine.jdbc.spi.SqlExceptionHelper: SQL Error: 1146, SQLState: 42S02
2020-02-03 15:53:43,768 ERROR [http-nio-5000-exec-1] org.hibernate.engine.jdbc.spi.SqlExceptionHelper: Table 'rmldb.rml_efs_action' doesn't exist
Notice here that it is trying to insert into rml_efs_action table and I don't understand the reason. I tried multiple things but nothing works.
I used JBOSS Hibernate tools to reverse engineer the MS SQL Server to get my data model objects.
I then wrote a small test program in Java to open a session, use the configuration, instantiate a Customer object, persist it into the database.
The sample runs writing one record to the database and closing the session.
What is weird, I have never seen this before is the 'oriental' characters being saved into the database columns.
Database:
SQL Server (Express) 10.50.1600 on Windows 2012 R2 Standard
Columns customerCode and customerName are nvarchar, null
Java App: Eclipse Neon, Maven, Hibernate 4.3, Java 1.8
NOTE: I am a newbie to Hibernate.
What else can I try or refactor?
Things I tried
Do a SQL Insert with IBM data studio (success)
No Success
Add the UTF-8 in connection url
Add the UTF-8 in hibernate xml
Create a SqlServerDialectWithNvarchar Class and enter it into hibernate xml.
public class SqlServerDialectWithNvarchar extends org.hibernate.dialect.SQLServerDialect{
public SqlServerDialectWithNvarchar(){
registerHibernateType(Types.NVARCHAR, 4000, "string");
registerColumnType(Types.BIGINT, "bigint");
registerColumnType(Types.BIT, "bit");
registerColumnType(Types.CHAR, "nchar(1)");
registerColumnType(Types.VARCHAR, 4000, "nvarchar($l)");
registerColumnType(Types.VARCHAR, "nvarchar(max)");
registerColumnType(Types.VARBINARY, 4000, "varbinary($1)");
registerColumnType(Types.VARBINARY, "varbinary(max)");
registerColumnType(Types.BLOB, "varbinary(max)");
registerColumnType(Types.CLOB, "nvarchar(max)");
}
}
Main Program:
public static void main(String[] args) {
String btnName = "OrderTrack.java ";
System.out.println(btnName + " started.");
System.out.println(btnName + " hibernate.enh-tas.cfg.xml");
Configuration cfg1 = new Configuration();
cfg1.configure("/hibernate.enh-tas.cfg.xml");
SessionFactory sf1 = cfg1.configure().buildSessionFactory();
System.out.println(btnName + " open hibernate session.");
Session session = sf1.openSession();
session.beginTransaction();
TblCustomers customer = new TblCustomers();
customer.setCustomerName("TEST XXX");
customer.setCustomerCode("DELETE ME");
session.persist(customer);
session.getTransaction().commit();
System.out.println(btnName + " closing hibernate session.");
session.close();
System.out.println(btnName + " session closed.");
}
SELECT Query using IBM Data Studio after main program runs
customerId customerCode customerName
---------- -------------- ---------------
30 Ԁt䐉䱅呅⁅䕍 Ԁt合卅⁔塘X
Hibernate XML
hibernate-configuration>
<session-factory>
<property name="hibernate.connection.useUnicode">true</property>
<property name="hibernate.connection.characterEncoding">utf-8</property>
<property name="hibernate.bytecode.use_reflection_optimizer">false</property>
<property name="hibernate.connection.driver_class">com.microsoft.sqlserver.jdbc.SQLServerDriver</property>
<property name="hibernate.connection.password">pwd</property>
<property name="hibernate.connection.url">jdbc:sqlserver://serverhost:1433;characterEncoding=UTF-8;</property>
<property name="hibernate.connection.username">tms</property>
<property name="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</property>
<property name="hibernate.encrypt">false</property>
<property name="hibernate.search.autoregister_listeners">true</property>
<property name="hibernate.show_sql">false</property>
<property name="hibernate.validator.apply_to_ddl">false</property>
Customer Class
/**
* TblCustomers generated by hbm2java
*/
#Entity
#Table(name = "tblCustomers", schema = "dbo", catalog = "TAS", uniqueConstraints = #UniqueConstraint(columnNames = {
"customerCode", "customerContact" }))
public class TblCustomers implements java.io.Serializable {
private Integer customerId;
private TblFrequency tblFrequency;
private Boolean customerLocked;
private Boolean customerDeleted;
private Serializable customerCode;
private Serializable customerName;
private Boolean customerIsOperator;
private Boolean customerIsSupplier;
private Boolean customerIsOwner;
private Boolean customerIsDrawer;
private Serializable customerContact;
private Serializable customerContactPosition;
private Serializable customerAddress1;
private Serializable customerAddress2;
private Serializable customerTown;
private Serializable customerCounty;
private Serializable customerPostCode;
private Serializable customerCountry;
private Serializable customerTelNo;
private Serializable customerFaxNo;
private Serializable customerEmail1;
private Serializable customerEmail2;
private Serializable customerEmail3;
private Serializable customerEmail4;
private Serializable customerEmail5;
private Serializable customerInvoiceName;
private Serializable customerInvoiceAddress1;
private Serializable customerInvoiceAddress2;
private Serializable customerInvoiceTown;
private Serializable customerInvoiceCounty;
private Serializable customerInvoicePostCode;
private Serializable customerInvoiceCountry;
private Serializable customerDefermentAccountNo;
private Double customerBond;
private Double customerBondTrigger;
private Double customerBondRemaining;
private Serializable customerNotes;
private Integer customerCustomerGroupId;
private Serializable customerRdcocode;
private Serializable customerVatregNo;
private Boolean customerIsHoldingAccount;
private Set<TblContacts> tblContactses = new HashSet(0);
private Set<TblMovementCustomers> tblMovementCustomerses = new HashSet(0);
private Set<TblTas2orderConfig> tblTas2orderConfigsForTas2ocCarrierNumber = new HashSet(0);
private Set<TblJournalCustomers> tblJournalCustomerses = new HashSet(0);
private Set<TblTas2orderConfig> tblTas2orderConfigsForTas2ocCustomerNumber = new HashSet(0);
private Set<TblSites> tblSiteses = new HashSet(0);
private Set<TblWomovements> tblWomovementses = new HashSet(0);
private Set<TblTas2destinations> tblTas2destinationses = new HashSet(0);
private Set<TblTas2orderConfig> tblTas2orderConfigsForTas2ocSupplierNumber = new HashSet(0);
private Set<TblTas2orderProduct> tblTas2orderProducts = new HashSet(0);
private Set<TblCustomerStocks> tblCustomerStockses = new HashSet(0);
public TblCustomers() {
}
public TblCustomers(TblFrequency tblFrequency, Boolean customerLocked, Boolean customerDeleted,
Serializable customerCode, Serializable customerName, Boolean customerIsOperator,
Boolean customerIsSupplier, Boolean customerIsOwner, Boolean customerIsDrawer, Serializable customerContact,
Serializable customerContactPosition, Serializable customerAddress1, Serializable customerAddress2,
Serializable customerTown, Serializable customerCounty, Serializable customerPostCode,
Serializable customerCountry, Serializable customerTelNo, Serializable customerFaxNo,
Serializable customerEmail1, Serializable customerEmail2, Serializable customerEmail3,
Serializable customerEmail4, Serializable customerEmail5, Serializable customerInvoiceName,
Serializable customerInvoiceAddress1, Serializable customerInvoiceAddress2,
Serializable customerInvoiceTown, Serializable customerInvoiceCounty, Serializable customerInvoicePostCode,
Serializable customerInvoiceCountry, Serializable customerDefermentAccountNo, Double customerBond,
Double customerBondTrigger, Double customerBondRemaining, Serializable customerNotes,
Integer customerCustomerGroupId, Serializable customerRdcocode, Serializable customerVatregNo,
Boolean customerIsHoldingAccount, Set tblContactses, Set tblMovementCustomerses,
Set tblTas2orderConfigsForTas2ocCarrierNumber, Set tblJournalCustomerses,
Set tblTas2orderConfigsForTas2ocCustomerNumber, Set tblSiteses, Set tblWomovementses,
Set tblTas2destinationses, Set tblTas2orderConfigsForTas2ocSupplierNumber, Set tblTas2orderProducts,
Set tblCustomerStockses) {
this.tblFrequency = tblFrequency;
this.customerLocked = customerLocked;
this.customerDeleted = customerDeleted;
this.customerCode = customerCode;
this.customerName = customerName;
this.customerIsOperator = customerIsOperator;
this.customerIsSupplier = customerIsSupplier;
this.customerIsOwner = customerIsOwner;
this.customerIsDrawer = customerIsDrawer;
this.customerContact = customerContact;
this.customerContactPosition = customerContactPosition;
this.customerAddress1 = customerAddress1;
this.customerAddress2 = customerAddress2;
this.customerTown = customerTown;
this.customerCounty = customerCounty;
this.customerPostCode = customerPostCode;
this.customerCountry = customerCountry;
this.customerTelNo = customerTelNo;
this.customerFaxNo = customerFaxNo;
this.customerEmail1 = customerEmail1;
this.customerEmail2 = customerEmail2;
this.customerEmail3 = customerEmail3;
this.customerEmail4 = customerEmail4;
this.customerEmail5 = customerEmail5;
this.customerInvoiceName = customerInvoiceName;
this.customerInvoiceAddress1 = customerInvoiceAddress1;
this.customerInvoiceAddress2 = customerInvoiceAddress2;
this.customerInvoiceTown = customerInvoiceTown;
this.customerInvoiceCounty = customerInvoiceCounty;
this.customerInvoicePostCode = customerInvoicePostCode;
this.customerInvoiceCountry = customerInvoiceCountry;
this.customerDefermentAccountNo = customerDefermentAccountNo;
this.customerBond = customerBond;
this.customerBondTrigger = customerBondTrigger;
this.customerBondRemaining = customerBondRemaining;
this.customerNotes = customerNotes;
this.customerCustomerGroupId = customerCustomerGroupId;
this.customerRdcocode = customerRdcocode;
this.customerVatregNo = customerVatregNo;
this.customerIsHoldingAccount = customerIsHoldingAccount;
this.tblContactses = tblContactses;
this.tblMovementCustomerses = tblMovementCustomerses;
this.tblTas2orderConfigsForTas2ocCarrierNumber = tblTas2orderConfigsForTas2ocCarrierNumber;
this.tblJournalCustomerses = tblJournalCustomerses;
this.tblTas2orderConfigsForTas2ocCustomerNumber = tblTas2orderConfigsForTas2ocCustomerNumber;
this.tblSiteses = tblSiteses;
this.tblWomovementses = tblWomovementses;
this.tblTas2destinationses = tblTas2destinationses;
this.tblTas2orderConfigsForTas2ocSupplierNumber = tblTas2orderConfigsForTas2ocSupplierNumber;
this.tblTas2orderProducts = tblTas2orderProducts;
this.tblCustomerStockses = tblCustomerStockses;
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "customerId", unique = true, nullable = false)
public Integer getCustomerId() {
return this.customerId;
}
public void setCustomerId(Integer customerId) {
this.customerId = customerId;
}
#Column(name = "customerCode")
public Serializable getCustomerCode() {
return this.customerCode;
}
public void setCustomerCode(Serializable customerCode) {
this.customerCode = customerCode;
}
#Column(name = "customerName")
public Serializable getCustomerName() {
return this.customerName;
}
public void setCustomerName(Serializable customerName) {
this.customerName = customerName;
}
Console Output (hibernate.show_sql = true)
DEBUG - begin
DEBUG - Obtaining JDBC connection
DEBUG - Obtained JDBC connection
DEBUG - initial autocommit status: false
DEBUG - Executing identity-insert immediately
DEBUG - insert into TAS.dbo.tblCustomers (customerAddress1, customerAddress2, customerBond, customerBondRemaining, customerBondTrigger, customerCode, customerContact, customerContactPosition, customerCountry, customerCounty, customer_customerGroupId, customerDefermentAccountNo, customerDeleted, customerEmail1, customerEmail2, customerEmail3, customerEmail4, customerEmail5, customerFaxNo, customerInvoiceAddress1, customerInvoiceAddress2, customerInvoiceCountry, customerInvoiceCounty, customerInvoiceName, customerInvoicePostCode, customerInvoiceTown, customerIsDrawer, customerIsHoldingAccount, customerIsOperator, customerIsOwner, customerIsSupplier, customerLocked, customerName, customerNotes, customerPostCode, customerRDCOCode, customerTelNo, customerTown, customerVATRegNo, customerFrequencyId) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
Hibernate: insert into TAS.dbo.tblCustomers (customerAddress1, customerAddress2, customerBond, customerBondRemaining, customerBondTrigger, customerCode, customerContact, customerContactPosition, customerCountry, customerCounty, customer_customerGroupId, customerDefermentAccountNo, customerDeleted, customerEmail1, customerEmail2, customerEmail3, customerEmail4, customerEmail5, customerFaxNo, customerInvoiceAddress1, customerInvoiceAddress2, customerInvoiceCountry, customerInvoiceCounty, customerInvoiceName, customerInvoicePostCode, customerInvoiceTown, customerIsDrawer, customerIsHoldingAccount, customerIsOperator, customerIsOwner, customerIsSupplier, customerLocked, customerName, customerNotes, customerPostCode, customerRDCOCode, customerTelNo, customerTown, customerVATRegNo, customerFrequencyId) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
DEBUG - Natively generated identity: 37
DEBUG - committing
DEBUG - Processing flush-time cascades
DEBUG - Dirty checking collections
DEBUG - Collection found: [com.xpagesbeast.data.entities.enh.TblCustomers.tblContactses#37], was: [<unreferenced>] (initialized)
DEBUG - Collection found: [com.xpagesbeast.data.entities.enh.TblCustomers.tblCustomerStockses#37], was: [<unreferenced>] (initialized)
DEBUG - Collection found: [com.xpagesbeast.data.entities.enh.TblCustomers.tblJournalCustomerses#37], was: [<unreferenced>] (initialized)
DEBUG - Collection found: [com.xpagesbeast.data.entities.enh.TblCustomers.tblMovementCustomerses#37], was: [<unreferenced>] (initialized)
DEBUG - Collection found: [com.xpagesbeast.data.entities.enh.TblCustomers.tblSiteses#37], was: [<unreferenced>] (initialized)
DEBUG - Collection found: [com.xpagesbeast.data.entities.enh.TblCustomers.tblTas2destinationses#37], was: [<unreferenced>] (initialized)
DEBUG - Collection found: [com.xpagesbeast.data.entities.enh.TblCustomers.tblTas2orderConfigsForTas2ocCarrierNumber#37], was: [<unreferenced>] (initialized)
DEBUG - Collection found: [com.xpagesbeast.data.entities.enh.TblCustomers.tblTas2orderConfigsForTas2ocCustomerNumber#37], was: [<unreferenced>] (initialized)
DEBUG - Collection found: [com.xpagesbeast.data.entities.enh.TblCustomers.tblTas2orderConfigsForTas2ocSupplierNumber#37], was: [<unreferenced>] (initialized)
DEBUG - Collection found: [com.xpagesbeast.data.entities.enh.TblCustomers.tblTas2orderProducts#37], was: [<unreferenced>] (initialized)
DEBUG - Collection found: [com.xpagesbeast.data.entities.enh.TblCustomers.tblWomovementses#37], was: [<unreferenced>] (initialized)
DEBUG - Flushed: 0 insertions, 0 updates, 0 deletions to 1 objects
DEBUG - Flushed: 11 (re)creations, 0 updates, 0 removals to 11 collections
DEBUG - Listing entities:
DEBUG - com.xpagesbeast.data.entities.enh.TblCustomers{customerInvoicePostCode=null, customerContactPosition=null, customerDefermentAccountNo=null, tblCustomerStockses=[], tblTas2destinationses=[], customerIsDrawer=null, customerInvoiceCountry=null, tblContactses=[], customerCustomerGroupId=null, tblWomovementses=[],
customerName=TEST XXX, customerDeleted=null,customerBondTrigger=null, customerTown=null, customerVatregNo=null, tblMovementCustomerses=[], tblSiteses=[], tblTas2orderConfigsForTas2ocCarrierNumber=[], customerInvoiceName=null,
customerCode=DELETE ME, customerEmail1=null, customerNotes=null, customerEmail3=null, customerEmail2=null, customerCountry=null, customerEmail5=null, customerInvoiceAddress1=null, tblTas2orderConfigsForTas2ocCustomerNumber=[], customerEmail4=null, customerInvoiceAddress2=null, customerIsOwner=null, tblTas2orderProducts=[], customerBondRemaining=null, customerFaxNo=null, customerIsSupplier=null, customerContact=null, customerPostCode=null, customerId=37, customerCounty=null, customerRdcocode=null, customerLocked=null, customerBond=null, tblTas2orderConfigsForTas2ocSupplierNumber=[], customerTelNo=null, customerInvoiceCounty=null, customerIsHoldingAccount=null, tblJournalCustomerses=[], tblFrequency=null, customerIsOperator=null, customerAddress1=null, customerAddress2=null, customerInvoiceTown=null}
DEBUG - Inserting collection: [com.xpagesbeast.data.entities.enh.TblCustomers.tblContactses#37]
DEBUG - Collection was empty
DEBUG - committed JDBC Connection
main.java closing hibernate session.
DEBUG - Releasing JDBC connection
DEBUG - Released JDBC connection
main.java session closed.
DEBUG - Connection pool now considered primed; min-size will be maintained
Change the fields in your entity class TblCustomers, whose type are serializable to String.
For eg: change
private Serializable customerCode;
to
private String customerCode;
Change the corresponding setter and getter methods as well.
Then try running your application.
I'm using eclipselink in my web application (i use vaadin with oracle as DATABASE) but i'm facing a problem with the Id values.
I have this simple schema :
encaiss (#MappedSuperclass contains just Id)
|
|
Encaissement (it extends encaiss so as to have the ID)
/ \
/ \
Encaissement_Technique (extends Encaissement) Encaissement_espece
/ \
/ \
Encaissement_Cheque Encaissement_Virement (both extend Encaissement_Technique to have the ID)
When i create an instance of Encaissement_virement for example, the Id generated is random eventhough i set allocationSize = 1, initialValue = 1.
Those are the entities :
====================Entity encaiss (contains just the ID)==============
#MappedSuperclass
public abstract class encaiss {
#Id
#GeneratedValue(strategy=GenerationType.AUTO, generator="encaiss_seq_gen")
#SequenceGenerator(name="encaiss_seq_gen", sequenceName="ENCAISSEMENT_SEQ", allocationSize = 1, initialValue = 1)
protected long id_encaissement;
public long getId_encaissement() {
return id_encaissement;
}
public void setId_encaissement(long id_encaissement) {
this.id_encaissement = id_encaissement;
}
}
============================entity Encaissement=======================
#Entity
#Inheritance(strategy=InheritanceType.JOINED)
#DiscriminatorColumn(name="ENCAISS_TYPE")
#Table(name="ENCAISSEMENT")
public class Encaissement extends encaiss implements Serializable{
#Column(name="LIBELLE")
protected String libelle;
#Column(name="PIECE_JOINTE")
protected String piece_jointe;
#Temporal(TemporalType.DATE)
#Column(name="DATE_ENCAISSEMENT")
protected Date date_encaissement;
#Embedded
protected Avis_Recette avis_recette;
public Encaissement(String libelle, String piece_jointe, Date date_encaissement){
this.libelle=libelle;
this.piece_jointe=piece_jointe;
this.date_encaissement=date_encaissement;
}
public Encaissement(){
}
}
====================entity Encaissement_Technique======================
#Entity
#DiscriminatorValue("Technique")
#Inheritance(strategy=InheritanceType.JOINED)
#DiscriminatorColumn(name="ENCAISS_TECHNIQUE_TYPE")
#Table(name="ENCAISSEMENT_TECHNIQUE")
public class Encaissement_Technique extends Encaissement implements Serializable{
public Encaissement_Technique(){
}
}
====================entity Encaissement_Cheque========================
#Entity
#DiscriminatorValue("C")
#Table(name="ENCAISSEMENT_CHEQUE")
public class Encaissement_Cheque extends Encaissement_Technique{
#Column(name="ETAT_AVISCREDIT")
private String etat_avisCredit;
#Embedded
private Cheque cheque;
public Encaissement_Cheque(String etat_avisCredit, Date date_encaissement){
this.etat_avisCredit=etat_avisCredit;
this.date_encaissement=date_encaissement;
}
public Encaissement_Cheque(){
}
}
====================entity Encaissement_Virement=======================
#Entity
#DiscriminatorValue("V")
#Table(name="ENCAISSEMENT_VIREMENT")
public class Encaissement_Virement extends Encaissement_Technique{
#Temporal(TemporalType.DATE)
#Column(name="DATE_VIREMENT")
private Date date_virement;
public Encaissement_Virement(Date date_virement, String libelle, String piece_jointe, Float primCoass, Date date_encaissement){
this.date_virement=date_virement;
this.primeCoass=primCoass;
this.piece_jointe=piece_jointe;
this.libelle=libelle;
this.date_encaissement=date_encaissement;
}
public Encaissement_Virement(){
}
}
I created from eclipse an instance of Encaissement_virement, the generated id is 71 then when i create another it gives it 75, after that it jumps to 91 !!! then it's adding 3 after each insertion (i mean id=103 then 106 then 109) !!!!!
PS : When i excute
Encaissement_virement ev=new Encaissement_virement();
//ev.getters and setters
ev.getId_Encaissement(); doesn't return the real Id instead it returns 0 whether it is called before or after the commit.
This problem is driving me crazy and i can't figure out the problem. (i think that the inheritance id or the id type (which is long) are not working).
I would appreciate your help. Thanks.
Edit :
After using sql developer i'm convinced that the problem is in the inheritance.
This is what i get when i try to insert an Encaissement_virement (which is logical because it extends another entity) :
Can anyone help me fix the inheritance problem in jpa eclipselikn. Thanks again.
Edit2
I create a new instance of Encaissement_virement using this code :
JPAContainer<Encaissement_Espece> ee=JPAContainerFactory.make(Encaissement_Espece.class, PERSISTANCE_UNIT);
Encaissement_Espece encaissEspece;
JPAContainer<Encaissement_Cheque> ec=JPAContainerFactory.make(Encaissement_Cheque.class, PERSISTANCE_UNIT);
Encaissement_Cheque encaissCheque;
JPAContainer<Encaissement_Virement> ev=JPAContainerFactory.make(Encaissement_Virement.class, PERSISTANCE_UNIT);
Encaissement_Virement encaissVirement;
//Some code here.
switch (typeEncaissement) {
case 0:
encaissEspece=new Encaissement_Espece();
encaissEspece.setCaisse(caisseEnc);
encaissEspece.setDate_encaissement(dateEnc);
encaissEspece.setLibelle(libelleEnc);
encaissEspece.setMontant(montantEncEspece);
encaissEspece.setPiece_jointe(pieceJointesEnc);
encaissEspece.setClient(clientEnc);
//UUID uuid = (UUID)ee.addEntity(encaissEspece);
//EntityItem<Encaissement_Espece> eeEntityItem = ee.getItem(uuid);
IdEnc=(Long)ee.addEntity(encaissEspece);
ee.commit();
break;
case 1:
encaissCheque=new Encaissement_Cheque();
encaissCheque.setBanque(banqueEnc);
encaissCheque.setCheque(chequeEnc);
encaissCheque.setClient(clientEnc);
encaissCheque.setDate_encaissement(dateEnc);
encaissCheque.setLibelle(libelleEnc);
encaissCheque.setPiece_jointe(pieceJointesEnc);
encaissCheque.setPrimeCoass(primeCoassEnc);
IdEnc=(Long)ec.addEntity(encaissCheque);
ec.commit();
break;
case 2:
encaissVirement=new Encaissement_Virement();
encaissVirement.setBanque(banqueEnc);
encaissVirement.setClient(clientEnc);
encaissVirement.setDate_encaissement(dateEnc);
encaissVirement.setLibelle(libelleEnc);
encaissVirement.setPiece_jointe(pieceJointesEnc);
encaissVirement.setPrimeCoass(primeCoassEnc);
encaissVirement.setDate_virement(dateVir);
IdEnc=(Long)ev.addEntity(encaissVirement);
ev.commit();
break;
default:
break;
this is the result (a new anstance with 66 as id) :
[EL Fine]: sql: 2015-04-01 17:25:45.301--ServerSession(5981709)--Connection(27335918)--Thread(Thread[http-bio-8080-exec-5,5,main])--SELECT ENCAISSEMENT_SEQ.NEXTVAL FROM DUAL
[EL Fine]: sql: 2015-04-01 17:25:45.316--ClientSession(26722029)--Connection(27335918)--Thread(Thread[http-bio-8080-exec-5,5,main])--INSERT INTO ENCAISSEMENT (ID_ENCAISSEMENT, DATE_ENCAISSEMENT, LIBELLE, PIECE_JOINTE, AGENCE, DATE_AVIS_RECETTE, ENDROIT, PROVENANCE, UNITE, ID_CLIENT, ENCAISS_TYPE) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
bind => [66, 2015-04-01, titre, jointes, null, null, null, null, null, 2, V]
[EL Fine]: sql: 2015-04-01 17:25:45.795--ClientSession(26722029)--Connection(27335918)--Thread(Thread[http-bio-8080-exec-5,5,main])--INSERT INTO ENCAISSEMENT_TECHNIQUE (PRIMECOASS, ID_BANQUE, ID_ENCAISSEMENT) VALUES (?, ?, ?)
bind => [123.0, 1, 66]
[EL Fine]: sql: 2015-04-01 17:25:45.936--ClientSession(26722029)--Connection(27335918)--Thread(Thread[http-bio-8080-exec-5,5,main])--INSERT INTO ENCAISSEMENT_VIREMENT (DATE_VIREMENT, ID_ENCAISSEMENT) VALUES (?, ?)
bind => [2015-04-01, 66]
Then i drop this row and create another instance with the same code, and i get :
[EL Fine]: sql: 2015-04-01 17:30:42.119--ServerSession(27444543)--Connection(6011948)--Thread(Thread[http-bio-8080-exec-15,5,main])--SELECT ENCAISSEMENT_SEQ.NEXTVAL FROM DUAL
[EL Fine]: sql: 2015-04-01 17:30:42.13--ClientSession(1011455)--Connection(6011948)--Thread(Thread[http-bio-8080-exec-15,5,main])--INSERT INTO ENCAISSEMENT (ID_ENCAISSEMENT, DATE_ENCAISSEMENT, LIBELLE, PIECE_JOINTE, AGENCE, DATE_AVIS_RECETTE, ENDROIT, PROVENANCE, UNITE, ID_CLIENT, ENCAISS_TYPE) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
bind => [69, 2015-04-01, titre, j, null, null, null, null, null, 4, V]
[EL Fine]: sql: 2015-04-01 17:30:42.195--ClientSession(1011455)--Connection(6011948)--Thread(Thread[http-bio-8080-exec-15,5,main])--INSERT INTO ENCAISSEMENT_TECHNIQUE (PRIMECOASS, ID_BANQUE, ID_ENCAISSEMENT) VALUES (?, ?, ?)
bind => [123.0, 1, 69]
[EL Fine]: sql: 2015-04-01 17:30:42.211--ClientSession(1011455)--Connection(6011948)--Thread(Thread[http-bio-8080-exec-15,5,main])--INSERT INTO ENCAISSEMENT_VIREMENT (DATE_VIREMENT, ID_ENCAISSEMENT) VALUES (?, ?)
bind => [2015-04-01, 69]
You can notice that instead of getting 67 as Id i get 69 ??
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;
}