Hibernate Duplicate entry '7090' for key 'PRIMARY' - java

Im getting following error when the application tries to insert a record row in the db.
SQL Error: 1062, SQLState: 23000
ERROR org.hibernate.util.JDBCExceptionReporter - Duplicate entry '7089' for key 'PRIMARY'
ERROR org.hibernate.event.def.AbstractFlushingEventListener - Could not synchronize database state with session
Caused by: java.sql.BatchUpdateException: Duplicate entry '7090' for key 'PRIMARY'
at com.mysql.jdbc.PreparedStatement.executeBatchSerially(PreparedStatement.java:1269)
at com.mysql.jdbc.PreparedStatement.executeBatch(PreparedStatement.java:955)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:48)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:242)
The definition is
#Id
#Column(name = "CST_CUSTOMER_ID_PK")
#GenericGenerator(name = "generator", strategy = "increment")
#GeneratedValue(generator = "generator")
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
The part at which the error occurs is :
savedCustomer = customerDao.save(customer);
mtmrsLogger.debug("saved customer id:" + savedCustomer.getId());
/**
* Updating Customer Trans Table
*/
updateCustomerTransForMakerChecker(customer, customerform.getAuditDetails());
/**
* Updating Customer Audit
*/
updateCustomerAuditForMakerChecker(customer, customerform.getAuditDetails());
//status=1;
//Add customer ewallet account
updateCustomerInWalletBalance(customer, customerform.getAuditDetails());
//send sms to customer
smsManager.sendSMSToCUCustomer(customer.getMobileno(), userBean);
}
mtmrsLogger.exiting("CustomerManagerImpl", "addCustomer");
My log shows, program has reached ' Exiting Class: CustomerManagerImpl Method: addCustomer' this part. Im saving the customer ,im setting the same entry in other two tables. The primary key of customer table is foreigh key in other two tables. Im lost please help.
CREATE TABLE `CST_CUSTOMER_INFO` (
`CST_CUSTOMER_ID_PK` bigint(11) NOT NULL AUTO_INCREMENT,
`CST_MOBILE` varchar(30) DEFAULT NULL,
`CST_FIRST_NAME` varchar(50) DEFAULT NULL,
`CST_LAST_NAME` varchar(150) NOT NULL,
`CST_MIDDLE_NAME` varchar(50) DEFAULT NULL,
PRIMARY KEY (`CST_CUSTOMER_ID_PK`)
) ENGINE=InnoDB AUTO_INCREMENT=4103 DEFAULT CHARSET=latin1
Im getting error occassionally in production , but in local its ok..

As table is being modified by more than one application, use of #GenericGenerator(name = "generator", strategy = "increment") will result in ambiguity.
Explanation
Strategy increment : It generates identifiers of type long, short or
int that are unique only when no other process is inserting data into
the same table. It should not be used in the clustered environment.
So you should rethink what strategy is to be used to generate id's. Using sequence or native strategy can resolve your problem.

Replace
`CST_CUSTOMER_ID_PK` bigint(11) NOT NULL AUTO_INCREMENT,
with
`CST_CUSTOMER_ID_PK` bigint(11) NOT NULL,
You are incrementing the id using hibernate/java, no need to make the DBMS increment it also.

Related

Liquibase not executing for Spring Boot/MySQL app

Spring Boot 1.5.8 and Java 8 here. I've followed all the Spring Boot & Liquibase guides and I can't seem to get Liquibase to work.
Here is a link to a GitHub repo for reproducing the issue exactly, but here's the scoop:
I have the following MySQL v8 database that gets created like so ahead of time (before the app runs):
CREATE DATABASE IF NOT EXISTS troubleshooting_db CHARACTER SET utf8 COLLATE utf8_general_ci;
I have the following src/main/resources/db/changelog files:
db.changelog-master.yaml:
===
databaseChangeLog:
- include:
file: db/changelog/1-setup.sql
1-setup.sql:
===
--liquibase formatted sql
--changeset troubleshooting:1 dbms:mysql
-- LOOKUPS
CREATE TABLE IF NOT EXISTS metric_range_categories (
metric_range_category_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
metric_range_category_ref_id VARCHAR(36) NOT NULL,
metric_range_category_name VARCHAR(250) NOT NULL,
metric_range_category_label VARCHAR(250) NOT NULL,
metric_range_category_description VARCHAR(500) NOT NULL,
CONSTRAINT pk_metric_range_categories PRIMARY KEY (metric_range_category_id),
INDEX idx_metric_range_categories_metric_range_category_ref_id (metric_range_category_ref_id),
INDEX idx_metric_range_categories_metric_range_category_label (metric_range_category_label),
CONSTRAINT uc_metric_range_categories_metric_range_category_ref_id UNIQUE (metric_range_category_ref_id),
CONSTRAINT uc_metric_range_categories_metric_range_category_name UNIQUE (metric_range_category_name),
CONSTRAINT uc_metric_range_categories_metric_range_category_label UNIQUE (metric_range_category_label)
);
-- Lots of other CREATE TABLE statements down here...
And the following JPA-annotated entity:
#Entity
#Table(name = "metric_range_categories")
#AttributeOverrides({
#AttributeOverride(name = "id", column = #Column(name = "metric_range_category_id")),
#AttributeOverride(name = "refId", column = #Column(name = "metric_range_category_ref_id")),
#AttributeOverride(name = "name", column = #Column(name = "metric_range_category_name")),
#AttributeOverride(name = "label", column = #Column(name = "metric_range_category_label")),
#AttributeOverride(name = "description", column = #Column(name = "metric_range_category_description"))
})
public class MetricRangeCategory extends BaseLookup {
public MetricRangeCategory() {
}
public MetricRangeCategory(Long id, String refId, String name, String label, String description) {
super(id, refId, name, label, description);
}
}
At runtime I get the following exception:
Caused by: org.hibernate.tool.schema.spi.SchemaManagementException: Schema-validation: missing table [metric_range_categories]
at org.hibernate.tool.schema.internal.SchemaValidatorImpl.validateTable(SchemaValidatorImpl.java:67)
at org.hibernate.tool.schema.internal.SchemaValidatorImpl.doValidation(SchemaValidatorImpl.java:50)
at org.hibernate.tool.hbm2ddl.SchemaValidator.validate(SchemaValidator.java:91)
at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:475)
at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:444)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:879)
... 29 common frames omitted
So when it starts up, Liquibase does not excute/engage and so Hibernate JPA validation fails because its looking for a table that doesn't exist (because Liquibase never kicked in and did its job!). Any ideas as to where I'm going awry? Why isn't Liquibase kicking in?
There are 2 different problems in the repo:
Wrong location of application.yml. Move it from root to
src/main/resources
Nested property in TroubleshootingConfig.Machine
has null value, because of this bean "authInfo" is not created and context initialization fails. Here is the reference on how Spring Boot Configuration Binding works.

How to insert entity with foreign keys defined as Long in Hibernate?

I'm using Hibernate 3.6.8 and I have a table defined in mysql (5.5) like this:
CREATE TABLE mytable
(
id BIGINT(20) PRIMARY KEY NOT NULL,
version INT(11),
description VARCHAR(60),
scheduled_at DATETIME,
`from` BIGINT(20),
`to` BIGINT(20),
deleted BIT(1) DEFAULT b'0',
completed BIT(1) DEFAULT b'0',
delete_from_after_completion BIT(1) DEFAULT b'0',
CONSTRAINT FK14F71A73C588I54F FOREIGN KEY (`from`) REFERENCES other_table (id),
CONSTRAINT FK14F71C231C45J198 FOREIGN KEY (`to`) REFERENCES other_table (id)
);
CREATE INDEX FK14F71A73C588I54F ON mytable (`from`);
CREATE INDEX FK14F71C231C45J198 ON mytable (`to`);
And a Java entity defined like this:
#Entity
public class MyTable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Version
#Column(name = "version")
private Integer version;
#Column(name = "description")
private String description;
#Column(name = "scheduled_at")
#Temporal(TemporalType.TIMESTAMP)
#DateTimeFormat(style = "SM")
private Date scheduledAt;
#Column(name = "from")
private Long fromId;
#Column(name = "to")
private Long toId;
#Column(name = "deleted")
private boolean deleted;
#Column(name = "completed")
private boolean completed;
#Column(name = "delete_from_after_completion")
private boolean deleteFromEntityAfterCompletion;
...
}
When I try to persist an instance of MyTable with valid values I end up with the following error:
2016-01-12 10:06:33,443 [qtp2139431292-20] WARN org.hibernate.util.JDBCExceptionReporter - SQL Error: 1064, SQLState: 42000
2016-01-12 10:06:33,443 [qtp2139431292-20] ERROR org.hibernate.util.JDBCExceptionReporter - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'from, scheduled_at, to, version) values (0, 1, 0, 'Test', 10, '2016-01-13 00:00:' at line 1
I suspect that the problem occurs since I try to model the foreign key relationships (to and from) as Long instead of an entity (OtherTable). I suspect this because Hibernate can indeed persist this entity if I comment out the to and from fields. Note that the relationship to the to and from entities does indeed exists in the database so that's not the problem.
If I try insert manually using something like this it works:
insert into mytable values(3, 0, "desc", '2016-10-10 00:00:', 10, 11, 0, 0, 0);
You have a column that is a SQL reserved keyword ("from") and Hibernate doesn't bother quoting it for you. Other JPA implementations (e.g DataNucleus JPA) take care of such things for you. You will have to add single quotes around the reserved word in your JPA annotation information
I am pretty sure that the problem is not a matter of persisting Long values.
Your code has something wrong. I bet there is a wrong quotation mark in your insert code.

Error inserting a new entity into the database because it conflicts with a duplicate key value

Im trying to create CRUD test to show my JPA program works but it is giving me strange error when i create a new entity and commit it to the database.
The Table supplier have only one contraint and that is a unique index that is autogenerated.
i will post the classes first then what i tried to do with them
Here is the SQL for the table and contraints
-- DDL for Table SUPPLIER
CREATE TABLE SUPPLIER
( SUPPLIERID INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY,
COMPANYNAME VARCHAR(40),
CONTACTNAME VARCHAR(30),
CONTACTTITLE VARCHAR(30),
ADDRESS VARCHAR(60),
CITY VARCHAR(15),
POSTALCODE VARCHAR(10),
COUNTRY VARCHAR(15),
PHONE VARCHAR(24),
FAX VARCHAR(24)
);
-- Constraints for Table SUPPLIER
ALTER TABLE SUPPLIER ALTER SUPPLIERID NOT NULL;
ALTER TABLE SUPPLIER ALTER COMPANYNAME NOT NULL;
ALTER TABLE SUPPLIER ADD CONSTRAINT PK_SUPPLIER PRIMARY KEY (SUPPLIERID);
This is my suppliers Primary key:
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "SUPPLIERID")
private Integer supplierid;
So first i create the entity and add it to my entitymanager
Supplier sup = new Supplier("Test","Test","Test","Test","Test","Test","Test","Test","Test");
em.persist(p);
Then i save it
public void Save() {
em.getTransaction().begin();
em.getTransaction().commit();
}
And then i get the error below even thought the only constraint that can be broken is 2 identical pk but those are auto generated.
I know that my setup works because my 2 other tests ( inserting other new entities works fine).
I also close and reopen connection between each test.
[EL Warning]: 2014-09-09 17:17:33.67--UnitOfWork(1407675409)--Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.5.1.v20130918-f2b9fc5): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLIntegrityConstraintViolationException: The statement was aborted because it would have caused a duplicate key value in a unique or primary key constraint or unique index identified by 'PK_SUPPLIER' defined on 'SUPPLIER'.
Error Code: -20001
Call: INSERT INTO SUPPLIER (ADDRESS, CITY, COMPANYNAME, CONTACTNAME, CONTACTTITLE, COUNTRY, FAX, PHONE, POSTALCODE) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
bind => [9 parameters bound]
Query: InsertObjectQuery(Peristency.Supplier[ supplierid=null ])
The save method irritates me a little. Like it is written above, it performs an empty transaction, doesn't it? Shouldn't it be something like this:
public void Save() {
...
em.getTransaction().begin();
...
Supplier sup = new Supplier("Test","Test","Test","Test","Test","Test","Test","Test","Test");
em.persist(p);
...
em.getTransaction().commit();
...
}
Could it be, that the coding for the question has been shortened too much, thus scrumbling the picture? Could you provide us please with the complete coding from starting the whole transaction until committing?
I remember once a while, I had a similar problem. Main point was, that I tried to persist twice in one transaction - and that was not possible - complaining about a duplicated key problem. May be that this is a hint for your case.
Okay after alot of Wall to face action i have found the cause of my problem
If you try to commit(entitymanager.getTransaction.commit()) a entity with a null pk to the database it wont work even if the database is supposed to provide the id.
What i had to do everytime i need a new object in a state where it can be saved to the db
public void addEmployee(Employee p) {
em.getTransaction().begin();
em.persist(p);
//Doesn't have an id
System.out.println(p.getEmployeeid());
em.flush();
//now it has an id!
System.out.println(p.getEmployeeid());
em.getTransaction().commit();
}
Then later i can merge and commit at my leasure but i feel like i made a ineffecient solution with the database transaction i have.

MySQLIntegrityConstraintViolationException when updating list of entities mapped as #ManyToMany, #JoinTable and #OrderColumn (with unique constraint)

I have a many-to-many relationship between Prequalification and Company entities (Partnership). The DDL for the three tables is:
CREATE TABLE Prequalifications
(
id INTEGER NOT NULL IDENTITY,
user_id INTEGER NOT NULL,
name VARCHAR(100) NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (user_id) REFERENCES Users (id)
);
CREATE TABLE Partnerships
(
prequalification_id INTEGER NOT NULL,
company_id INTEGER NOT NULL,
ordinal_nbr SMALLINT DEFAULT 0 NOT NULL,
PRIMARY KEY (prequalification_id, company_id),
FOREIGN KEY (prequalification_id) REFERENCES Prequalifications (id),
FOREIGN KEY (company_id) REFERENCES Companies (id),
UNIQUE (prequalification_id, ordinal_nbr)
);
CREATE TABLE Companies
(
id INTEGER NOT NULL,
dnd_type VARCHAR(10) NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (id) REFERENCES Organizations (id) -- just an inherited ID, never mind!
);
Please note the unique constraint on Partnerships's PQ ID and ordinal number: there can be only one position of a company per PQ.
This got mapped in Prequalification as #ManyToMany + #JoinTable including an #OrderColumn for the order of the companies per PQ (Partnerships's order column is ordinal_nbr):
#Entity
#Table(name = "Prequalifications")
public class Prequalification implements Serializable
{
...
#ManyToMany
#JoinTable(name = "Partnerships", joinColumns = #JoinColumn(name = "prequalification_id", referencedColumnName = "id"), inverseJoinColumns = #JoinColumn(name = "company_id", referencedColumnName = "id"))
#OrderColumn(name = "ordinal_nbr", nullable = false)
private List<Company> companies;
...
}
Here's the update method which is called from the GUI. Note, that the list on the GUI only includes external companies and that the internal company is (supposed to be) at index zero always:
#ManagedBean
#ViewScoped
public class PqHome implements DropListener, Serializable
{
...
private Prequalification pq;
private Integer userId;
private List<Company> participatingCompanies; // xetters omitted!
...
public void update()
{
// assign new owner
pq.setUser(userService.findSingleUser(userId));
Company internal = companyManager.getInternalCompany();
// find internal company
int index = participatingCompanies.indexOf(internal);
// if internal company missing or at non-zero index: we need to repair this
if ( index != 0 )
{
// if internal exists at some wrong place, remove it
if ( index > 0 )
{
participatingCompanies.remove(index);
}
// (re-)add at index zero
participatingCompanies.add(0, internal);
}
pq.setCompanies(participatingCompanies);
// update and get *new* merged instance
pq = pqService.update(pq);
participatingCompanies = null;
init(); // some not so important (re-)initialization...
}
...
}
In the client JSF page the field participatingCompanies is used like:
<rich:pickList value="#{pqHome.participatingCompanies}"
var="company"
converter="#{companyConverter}"
orderable="true"
sourceCaption="Available companies"
targetCaption="Selected companies">
<f:selectItems value="#{companyManager.externalCompanies}" />
<rich:column>#{company.name}</rich:column>
</rich:pickList>
Don't be intimidated RichFaces component. The list referencing #{pqHome.participatingCompanies} just contains optional (external) companies.
When I hit the update button (not shown), the update method on the PqHome bean is called. When executing the code on GlassFish 3.1.2 using EclipseLink 2.3.2 the following exception is thrown:
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '2-2' for key 'partnerships_multi_uq'
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:525)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)
at com.mysql.jdbc.Util.getInstance(Util.java:386)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1039)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3609)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3541)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2002)
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2163)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2624)
at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2127)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2427)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2345)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2330)
at com.mysql.jdbc.jdbc2.optional.PreparedStatementWrapper.executeUpdate(PreparedStatementWrapper.java:875)
at com.sun.gjc.spi.base.PreparedStatementWrapper.executeUpdate(PreparedStatementWrapper.java:125)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeDirectNoSelect(DatabaseAccessor.java:831)
... 128 more
This exception is duplicated several times in the log. Other PQ's have the same problem, just involve other unique column combinations, e.g. '1-1'. The server log can be found here: http://www.eclipse.org/forums/index.php/t/310923/
What's wrong with the update code? It works without problems on Hibernate.
EclipseLink updates the #OrderColumn in the join table to maintain the order efficiently, so you cannot have a unique constraint on it. So, remove or defer the unique constraint.
You could log a bug on EclipseLink to have an option added to avoid this restriction.

JPA joined column allow every value

I'm testing JPA, in a simple case File/FileVersions tables (Master/Details), with OneToMany relation, I have this problem: in FileVersions table, the field "file_id" (responsable for the relation with File table) accepts every values, not only values from File table.
How can I use the JPA mapping to limit the input in FileVersion.file_id only for values existing in File.id?
My class are File and FileVersion:
FILE CLASS
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="FILE_ID")
private Long id;
#Column(name="NAME", nullable = false, length = 30)
private String name;
//RELATIONS -------------------------------------------
#OneToMany(mappedBy="file", fetch=FetchType.EAGER)
private Collection <FileVersion> fileVersionsList;
//-----------------------------------------------------
FILEVERSION CLASS
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="VERSION_ID")
private Long id;
#Column(name="FILENAME", nullable = false, length = 255)
private String fileName;
#Column(name="NOTES", nullable = false, length = 200)
private String notes;
//RELATIONS -------------------------------------------
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name="FILE_ID", referencedColumnName="FILE_ID", nullable=false)
private File file;
//-----------------------------------------------------
and this is the FILEVERSION TABLE
CREATE TABLE `JPA-Support`.`FILEVERSION` (
`VERSION_ID` bigint(20) NOT NULL AUTO_INCREMENT,
`FILENAME` varchar(255) NOT NULL,
`NOTES` varchar(200) NOT NULL,
`FILE_ID` bigint(20) NOT NULL,
PRIMARY KEY (`VERSION_ID`),
KEY `FK_FILEVERSION_FILE_ID` (`FILE_ID`)
) ENGINE=MyISAM AUTO_INCREMENT=4 DEFAULT CHARSET=latin1
Thanks for help,
I know the SQL constraint to limit the input, but it is possible to create this SQL costraint using some annotation, without writing by hand the SQL in the database?
I'm new on JPA, I was thinking that using #JoinColumn annotation, JPA could create also the costraint...
Thank you again.
At the Java level, you describe and annotate associations between classes - which and you did - and your mapping looks fine.
At the database level, if you want to restrict the possible values in the file_id column to values that are primary keys in the FILE table, you should use a foreign key constraint. To do so, you will need to use InnoDB tables. Something like that:
CREATE TABLE `JPA-Support`.`FILEVERSION` (
`VERSION_ID` bigint(20) NOT NULL AUTO_INCREMENT,
`FILENAME` varchar(255) NOT NULL,
`NOTES` varchar(200) NOT NULL,
`FILE_ID` bigint(20) NOT NULL,
PRIMARY KEY (`VERSION_ID`),
FOREIGN KEY `FK_FILEVERSION_FILE_ID` (`FILE_ID`) REFERENCES FILE(ID)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1
The table FILE also has to use InnoDB. Actually, use InnoDB tables for the tables for which you want to use referential integrity.

Categories