I have two tables, AccountData and Relations.
They have one to many relation, to one account can belong more relations.
I try to write a test for its repository to test the saving functionality and I get:
Caused by: org.h2.jdbc.JdbcSQLException: Unique index or primary key violation: "PRIMARY KEY ON PUBLIC.ACC_DATA(ID)"; SQL statement:
insert into ACC_DATA (ACC_CURRENCY, STATUS, CREATED_AT, CODE, ACC_NBR, OWNER_ID, EP_ID, SUBTYPE_ID, TYPE_ID, id) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) [23505-197]
I define the primary key as follows:
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_ACC_DATA")
#SequenceGenerator(name = "SEQ_ACC_DATA", sequenceName = "SEQ_ACC_DATA", allocationSize = 50)
private Long id;
Any idea?
Thank yoou in advance!
Code seems to be correct.
Compare the value of the SEQ_ACC_DATA Sequence in the DB and the Max value for the ID column of the ACC_DATA table.
If the ID column has value greater than the sequence, you'll need to increment the value of the Sequence so that new inserts can take place through code.
Related
I have a simple use case:
I want to insert an entity that doesn't exist.
Here is the entity class, it has combined unique columns:
#Entity
#Table(uniqueConstraints = {
#UniqueConstraint(columnNames = {
"firstName", "lastName"
})
})
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
And here is the simple insertion logic:
Optional<Customer> customer = customerRepository.findByFirstNameAndLastName("John", "Doe");
if (!customer.isPresent()) {
customerRepository.save(new Customer("John", "Doe"));
}
when I call this via concurrent threads, I get this ConstraintViolationException and that's obvious
insert into customer (first_name, last_name, id) values (?, ?, ?) [23505-200]]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement] with root cause
org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException: Unique index or primary key violation: "PUBLIC.UKKIYY7M3FWM4VO5NIL9IBP5846_INDEX_5 ON PUBLIC.CUSTOMER(FIRST_NAME, LAST_NAME) VALUES 8"; SQL statement:
insert into customer (first_name, last_name, id) values (?, ?, ?) [23505-200]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:459) ~[h2-1.4.200.jar:1.4.200]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:429) ~[h2-1.4.200.jar:1.4.200]
I can catch this exception, but ConstraintViolationException can occur in any scenario other than the unique index.
I also tried JPA's Optimistic locking but it seems to work only in the update and delete operations but not in insertion.
I don't want native query (like ON DUPLICATE KEY in mysql, ON CONFLICT in PostgresSQL) because I want to make operations to be handled in JPA itself.
Also, I cannot make it threadsafe because the application will be running on multiple JVMs.
Is there any way somehow I can handle this, like locking on insert or atleast update on same keys?
I had an existed entity (TableEntity.java), the table existed in db, and also the data
Here is how the column id already delcared in TableEntity.java
#Id
#SequenceGenerator(name = "table_name_id_seq", sequenceName = "table_name_id_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "table_name_id_seq")
#Column(name = "id", nullable = false, updateable = false)
private int id;
In the database, I look at the properties of the table, for id column, the data type is int and set as Not NULL
The previous code, for inserting data to db, is only doing "repository.save()"
But now, I want to use PreparedStatement to insert data to db per batch
Here is how I create the query
String query = "INSERT INTO table_name (column1, column2) values (?, ?)";
the problem is, when the query executed, its violates null constraint for the id column. How to make the database can handle my column id? Since, when I just use repository.save() is work fine, but when I use that (could I say it a native query?) query, that exception appear. How to solve this? or any reference to solve this?
Set a default value for the column:
alter table table_name
alter column id set default nextval('table_name_id_seq');
Now if an INSERT statement does not supply a value for the id column (by not mentioning it), the next sequence value is used to populate the column.
I have a NOTIFICATION table who contains one ManyToMany association :
#Entity
#Table(name="NOTIFICATION")
#NamedQuery(name="Notification.findAll", query="SELECT f FROM Notification f")
public class Notification {
/** SOME COLUMN DEFINITION NOT IMPORTANT FOR MY CASE
COD, DATE, ID_THEME, ID_TYP, IC_ARCH, ID_CLIENT, INFOS, NAME, TITRE_NOT, ID_NOT
**/
#ManyToMany
#JoinTable(
name="PJ_PAR_NOTIF"
, joinColumns={
#JoinColumn(name="ID_NOTIF")
}
, inverseJoinColumns={
#JoinColumn(name="ID_PJ_GEN")
}
)
private List<PiecesJointesGen> piecesJointesGens;
}
So, I have an association table called PJ_PAR_NOTIF.
I try to persist a Notification entity. Here is piecesJointesGens initialisation, from a Value Object :
#PersistenceContext(unitName="pu/middle")
private EntityManager entityMgr;
FoaNotification lFoaNotification = new FoaNotification();
for(PieceJointeGenVO lPJGenVO : pNotificationVO.getPiecesJointes()){
PiecesJointesGen lPiecesJointesGen = new PiecesJointesGen();
lPiecesJointesGen.setLienPjGen(lPJGenVO.getLienPieceJointeGen());
lPiecesJointesGen.setIdPjGen(lPJGenVO.getIdPieceJointeGen());
lNotification.getFoaPiecesJointesGens().add(lFoaPiecesJointesGen);
}
entityMgr.persist(pNotification);
The persist doesn't work. JPA generate a first insert for my Notification object, that is ok :
insert
into
NOTIFICATION
(COD, DATE, ID_THEME, ID_TYP, IC_ARCH, ID_CLIENT, INFOS, NAME, TITRE_NOT, ID_NOT)
values
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
Then, JPA try to insert values in my association table, but piecesJointesGen doesn't exists for the moment :
insert
into
PJ_PAR_NOTIF
(ID_NOTIF, ID_PJ_GEN)
values
(?, ?)
So, I have this error :
GRAVE: EJB Exception: : java.lang.IllegalStateException: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.entities.PiecesJointesGen
Is there a way to tell JPA to insert piecesJointesGen before the PJ_PAR_NOTIF insert ?
Modify piecesJointesGens mapping to #ManyToMany(cascade = CascadeType.PERSIST).
I have written an application that parses an xml document using jaxb and then inserts into a database using JPA.
I have three JPA entities.
1.ItemEntity
2.PromotionEntity
3.SellPriceEntity
The item entity has a one to one relationship with the PromotionEntity and a one to one relationship with the SellPrice Entitiy.
When try to insert into the db using only the ItemEntity my application works and the item record is inserted into the db. However when I try to insert into the db using the ItemEntity, PromotionEntity and the SellPriceEntity I start to get errors.
org.apache.openjpa.persistence.PersistenceException: Incorrect integer value: '\xAC\xED\x00\x05sr\x00,org.apache.camel.example.etl.PromotionEntity$\x0C\xF5\xF1\x08\x0B\xA2\x81\x02\x00\x05L\x00\x02idt\x00\x10' for column 'PROMOTION_ID' at row 1 {prepstmnt 1554452939 INSERT INTO item (id, ATTRIBUTE_1, ATTRIBUTE_3, ATTRIBUTE_2, BRAND_LOGO_FILE_NAME, BRAND_NAME, CLASS_NO, DEFAULT_MARGIN, DESCRIPTION, EXTENDED_DESCRIPTION, EXTENDED_DESCRIPTION_2, GST_CODE, IMAGE_FILE_NAME, ITEM_NO, OUT_OF_STOCK_IND, PACK_QTY, PROMOTION_ID, SELL_PRICE_ID, SELLING_UNIT, SIZE_APPLICABLE, STOCK_AVAILABLE, SPPLR_NO, VOLUME, WEB_AGE_GROUP, WEB_COLOR_DESCRIPTION, WEB_DESCRIPTION, WEB_SIZE_DESCRIPTION, WEIGHT) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)} [code=1366, state=HY000]
FailedObject: org.apache.camel.example.etl.ItemEntity#333f8b4c
Here is how I have set my entity beans up...
#Entity(name = "item")
public class ItemEntity implements java.io.Serializable {
private static final long serialVersionUID = -9063279672222036437L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "ITEM_NO")
private String itemNo;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name="PROMOTION_ID")
private PromotionEntity promotion;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name="SELL_PRICE_ID")
private SellPriceEntity sellPrice;
gets and sets...
Promotion Entity
#Entity(name = "promotion")
public class PromotionEntity implements java.io.Serializable {
private static final long serialVersionUID = 2597721500656837249L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "PROMOTION_ID")
private String promotionId;
#Column(name = "PROMOTION_PRICE")
private String promotionPrice;
gets and sets...
Sell Price Entity
#Entity(name = "sell_price")
public class SellPriceEntity implements java.io.Serializable {
private static final long serialVersionUID = -205334787672950850L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "SELL_PRICE_EFFECTIVE_DATE_1")
private String sellPriceEffectiveDateOne;
#Column(name = "SELL_PRICE_1")
private String sellPriceOne;
gets and sets...
I believe that I have defined the relationship fields properly so not sure what is going wrong. On trying to debug through the JDBCStoreManager class I can see that the sql being executed is....
com.mysql.jdbc.JDBC4PreparedStatement#6d66cc49:
INSERT INTO item (
id,
ATTRIBUTE_1,
ATTRIBUTE_3,
ATTRIBUTE_2,
BRAND_LOGO_FILE_NAME,
BRAND_NAME,
CLASS_NO,
DEFAULT_MARGIN,
DESCRIPTION,
EXTENDED_DESCRIPTION,
EXTENDED_DESCRIPTION_2,
GST_CODE, I
MAGE_FILE_NAME,
ITEM_NO,
OUT_OF_STOCK_IND,
PACK_QTY,
PROMOTION_ID,
SELL_PRICE_ID,
SELLING_UNIT,
SIZE_APPLICABLE,
STOCK_AVAILABLE,
SPPLR_NO,
VOLUME,
WEB_AGE_GROUP,
WEB_COLOR_DESCRIPTION,
WEB_DESCRIPTION,
WEB_SIZE_DESCRIPTION,
WEIGHT)
VALUES (701, '', '', '', '', '', '350', '.00', 'KHOMBU APFOOTA KOKO HIGH', '', '', '1', '', '93501250080', 'Y', '0', ** STREAM DATA **, ** STREAM DATA **, 'Each', 'Y', '0', 'KHOMBU', '.0000', '', 'Black', '', '8', '.00')
Where I should be inserting promotion_id and sell_price_id it is telling me ** STREAM DATA **. Is that right? Maybe this is why I'm getting a data type error.
thanks
It is not clear what you are after from the description, which also conflicts with the model you posted. A joincolumn is used to specify the foreign key field name as well as the primary key it references in the target entity. But JPA requires this to be the primary key in the referenced entity, so your mappings end up using "ITEM"."PROMOTION_ID" to reference "PROMOTION"."ID". You get the exception because on of the "PROMOTION_ID" fields doesn't exist on one of the tables - either the item or promotion tables.
Decide which table will have the foreign key to the parents table and go from there. I'm not sure it makes sense to have a single auto generated pk value reused for the 3 entities involved in the model shown, as it means the other two entitities can never exist with out the one who gets the id set. For example, there would be no way to create a new promotion and keep the old one around. That said, it would be possible in JPA 2.0 to set a relationship as the #ID. So you could remove the id from item like this:
#Column(name = "ITEM_NO")
private String itemNo;
#Id
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name="PROMOTION_ID")
private PromotionEntity promotion;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name="SELL_PRICE_ID")
private SellPriceEntity sellPrice;
This would have item use the generated long id value from promotion as its id as well, and store it in the "PROMOTION_Id" field. You could do something similar to SellPriceEntity so it has a relationship to item or promotion that it could use to get its id set.
I've mapped my class as follow (omitted other fields as only ID matters):
#Entity
#Table(name = "MODEL_GROUP")
#Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
public class SettlementModelGroup implements Serializable
{
#Id
#GeneratedValue(generator = "MODEL_GROUP_SEQ", strategy = GenerationType.SEQUENCE)
#GenericGenerator(name = "MODEL_GROUP_SEQ",
strategy = "sequence",
parameters = #Parameter(name = "sequence", value = "SEQ_MODEL_GROUP_MODEL_GROUP_ID"))
#Column(name = "MODEL_GROUP_ID", nullable = false)
private Integer modelId;
}
when I'm saving new object:
Integer modelGroupId = sessionFactory.getCurrentSession().save( modelGroup );
System.out.println( modelGroupId );
ID is set as for example 23, but when I look at the database it is actually 24. This is leading to many problems, as I'm using this ID later on. Any idea why it is making this gap?
SQL logs show that everything is fine (I thinks so):
Hibernate:
select
SEQ_MODEL_GROUP_MODEL_GROUP_ID.nextval
from
dual
Hibernate:
insert
into
MODEL_GROUP
(DOMAIN_ID, DESCRIPTION, NAME, PERIOD_TYPE_ID, MODEL_GROUP_TYPE_ID, STATUS_ID, OWNER_ID, MODEL_GROUP_ID)
values
(?, ?, ?, ?, ?, ?, ?, ?)
Trigger and Sequence:
CREATE SEQUENCE "SEQ_MODEL_GROUP_MODEL_GROUP_ID"
INCREMENT BY 1
START WITH 1
NOMAXVALUE
MINVALUE 1
NOCYCLE
NOCACHE
NOORDER
;
CREATE OR REPLACE TRIGGER "TRG_MODEL_GROUP_MODEL_GROUP_ID"
BEFORE INSERT
ON "MODEL_GROUP"
FOR EACH ROW
WHEN (NEW."MODEL_GROUP_ID" is NULL)
BEGIN
SELECT "SEQ_MODEL_GROUP_MODEL_GROUP_ID".NEXTVAL
INTO :NEW."MODEL_GROUP_ID"
FROM DUAL;
END;
Apparently, when Hibernate ask your database for nextValue of ID, it fires also Trigger. So when I ask for ID, I've got number 23 but when actually saving to database by commiting transaction, it is increased again so I've got 24. Solution is described here:
HIbernate issue with Oracle Trigger for generating id from a sequence
To make it work correctly, I changed Trigger:
CREATE OR REPLACE TRIGGER "TRG_MODEL_GROUP_MODEL_GROUP_ID"
BEFORE INSERT
ON "MODEL_GROUP"
FOR EACH ROW
WHEN (NEW."MODEL_GROUP_ID" is NULL)
BEGIN
SELECT "SEQ_MODEL_GROUP_MODEL_GROUP_ID".NEXTVAL
INTO :NEW."MODEL_GROUP_ID"
FROM DUAL;
END;