I am using Hibernate 4.1.3 (JPA) on the Play! framework. The database is PostgreSQL 8.4.2. The schema was generated using hibernate.hbm2ddl.auto="update".
Short version: I have a class that has an #Id field that is a #GeneratedValue. Sometimes, when persisting it, I get a null-column violation, why?
More details:
I have a really simple class that I want to save to the database, that looks like this:
#Entity
class MyObject {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public Long id;
#NotNull
public String email;
public Integer total;
}
I usually create an instance of MyObject, I assign a value to email and total fields while id is null and I save it via EntityManager.persist(). Hibernate gets an id for the new object and saves it to the DB.
However sometimes, I get the following stacktrace:
2012-05-19 00:45:16,335 - [ERROR] - from org.hibernate.engine.jdbc.spi.SqlExceptionHelper [SqlExceptionHelper.java:144] in play-akka.actor.actions-dispatcher-6
ERROR: null value in column "id" violates not-null constraint
2012-05-19 00:45:16,350 - [ERROR] - from application in play-akka.actor.actions-dispatcher-6
! #6ad7j3p8p - Internal server error, for request [POST /method] ->
play.core.ActionInvoker$$anonfun$receive$1$$anon$1: Execution exception [[PersistenceException: org.hibernate.exception.ConstraintViolationException: ERROR: null value in column "id" violates not-null constraint]]
How is this possible? How can I track down the problem?
Here's the relevant DDL generated by Hibernate:
CREATE TABLE myobject (
id bigint NOT NULL,
email character varying(255) NOT NULL,
physical integer
);
CREATE SEQUENCE hibernate_sequence
START WITH 1
INCREMENT BY 1
NO MAXVALUE
NO MINVALUE
CACHE 1;
ALTER TABLE ONLY dailydetailedscore
ADD CONSTRAINT dailydetailedscore_pkey PRIMARY KEY (id);
Try the annotation #org.hibernate.annotations.GenericGenerator(name = “test-hilo-strategy”, strategy = “hilo”):
#Id
#org.hibernate.annotations.GenericGenerator(name=“hilo-strategy”, strategy = “hilo”)
#GeneratedValue(generator = ”hilo-strategy”)
As someone noted above, AUTO does not do what you think. It uses the underlying DB to determine how to generate values. It may pick sequences (for oracle), identity column (for mssql), or something else that is db specific.
The approach here uses an internal strategy that Hibernate supplies called "hilo".
See chapter 5 of the Hibernate reference manual dealing with "Generator" for a full description of what each of the supplied ones does.
Neither the OP solution nor Matt's solution worked with my PostgreSQL 9.3.
But this one works:
#SequenceGenerator(name="identifier", sequenceName="mytable_id_seq", allocationSize=1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="identifier")
Replace mytable_id_seq with the name of the sequence that generates your id.
Use Hibernate method:- save(String entityName, Object object)
Persist the given transient instance, first assigning a generated identifier.
Do not use :- #GeneratedValue(strategy=GenerationType.IDENTITY) for primary key if you want to persist user define Id.
For detail:-
http://docs.jboss.org/hibernate/core/3.6/javadocs/org/hibernate/Session.html#save(java.lang.String
In mycase i was using Identity generation strategy and i have set the wrong data type in Postgres. Following steps i performed to debug the problem.
set
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true in application.properties and Drop the tables.
By this hibernate will automatically create the schema for you on the basis of your data-type.I noticed change in the datatype of id field.
Now when i tried any post requests, everything worked fine.
Related
I have Migrated the Springboot version from 1.4.3.RELEASE to 2.1.0.RELEASE in my project. After that CrudRepository.save() always throws org.hibernate.exception.ConstraintViolationException: could not execute statement.
This is what I can see in the Logs :
o.h.e.j.s.SqlExceptionHelper[m: SQL Error: 1062, SQLState: 23000
o.h.e.j.s.SqlExceptionHelper[m: Duplicate entry '11' for key 'PRIMARY'
o.h.i.ExceptionMapperStandardImpl[m: HHH000346: Error during managed flush [org.hibernate.exception.ConstraintViolationException: could not execute statement
This is the entity I am trying to save.
#Getter
#Setter
#Entity
#Table(name = "project_m")
public class Project {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id", nullable = false)
private Long id;
#Column(name = "name" , nullable = false)
private String name;
//other fields
}
What has changed from Springboot 1.4.3.RELEASE to 2.1.0.RELEASE is the internal Hibernate version from 5.0 to 5.3.
And what has changed in this is the way the SequenceGenerator works, which is used in case the strategy is GenerationType.AUTO (as is in your case).
Link to hibernate migration doc here.
More details on hibernate generation strategy here.
My guess is there are 2 parallel sessions inserting into this table, and both of them now share a local copy of the sequence number, which creates this collision. Not sure though!
My suggestion would be to change the strategy to GenerationType.SEQUENCE and try.
ConstraintViolationException occurred due to the fact that your primary key constraint for SQL database violates
In SQL the primary key is a unique key to identify the record, the database will throw an exception when you trying to insert a duplicate value to a primary column.
which in turn got by the hibernate and passed to your code and is the reason for this exception.
From Springboot 1.4.3.RELEASE to 2.1.0.RELEASE Hibernate version is updated from 5.0 to 5.3.
The way Hibernate interprets AUTO generation type has changed starting with Hibernate version 5.0
If you use strategy="AUTO", Hibernate will generate a table called hibernate_sequence to provide the next number for the ID sequence. You may have forgotten to add the AutoIncrement feature to your table's PK.
Another way to fix is use following annotations with strategy="AUTO"
#Id
#GeneratedValue(
strategy= GenerationType.AUTO,
generator="native"
)
#GenericGenerator(
name = "native",
strategy = "native"
)
private Long id;
You may use generation strategy strategy="IDENTITY" to enforce using the AutoIncrement feature available in SQL and avoid creating a table.
Pls check here to get some more insights
I got a workaround for this issue by setting a large value in hibernate_sequence table. I saw that the primary key value from the duplicate primary key error is generated from a table named hibernate_sequence.
When we set GenerationType.AUTO in the entity,Hibernate selects the generation strategy based on the Hibernate dialect. In older versions , Hibernate selected the GenerationType.IDENTITY as default for MySQL databases.
It now selects the GenerationType.TABLE which uses a database table to generate primary keys
I am working on application where three environments are there.
test, int and production db.
I have a table, if I observe the generated SQL in SQL developer for primary key
ID" NUMBER GENERATED BY DEFAULT AS IDENTITY MINVALUE 1 MAXVALUE
9999999999999999999999999999 INCREMENT BY 1 START WITH 1 CACHE 20
NOORDER NOCYCLE NOT NULL ENABLE,
If I see on table it looks like...
"SCHEMA"."ISEQ$$_97103".nextval
on JPA/eclipselink entity I used this ISEQ$$_97103 sequence generator it worked on test environment.
But, when I executed this on INT environment it throws error ISEQ$$_97103 sequence does not exist. I observed that, it's a different name in INT database.
As they system generated names they would be different.
I tried with the following link but no use
https://www.thoughts-on-java.org/hibernate-tips-use-auto-incremented-column-primary-key/
How to specify in JPA use system generated identity names from table? is there any way? or is is mandatory that the name should be same in three environments.
Here is my entity class:
#Entity
#Table(name = "T_MY_TABLE")
public class MyDataEntity implements Serializable {
#Id
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// other colums, getters/setters
}
Error I am getting
Internal Exception: java.sql.SQLException: ORA-02289: sequence does not exist
Error Code: 2289
Call: SELECT SEQ_GEN_IDENTITY.NEXTVAL FROM DUAL
Query: ValueReadQuery(sql="SELECT SEQ_GEN_IDENTITY.NEXTVAL FROM DUAL
I am really wondering, I haven't mentioned anything related to Sequence but why it's reading the value from sequence.
I have a weird behavior of Hibernate (3.6.1.Final) in my application and I am pretty desperate at the moment. The behavior occurs on both MariaDB 10.1 and RDS on Amazon.
After a certain amount of successful persists (always around ~5k) Hibernate fires a:
SQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails
From what I see in the stacktrace Hibernate tries to fill a foreign key in a entity with a negative value that violates the FK constraint (the target table of the FK does not have such primary key value).
The strange thing is:
It happens only after around ~5k successful persists
After submitting such number of persist operation, the error occurs even if I restart the application (as well as the DB). Only solution is to wipe out the DB and repeat.
The error occurs independently from the load (continuous, in batches, single or multithread).
The following is the FK of the entity that is violated during the insert:
#Entity
#Cacheable
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Charge extends Entry {
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "totalId")
private Amount total;
//....
And this is how the primary key is defined in the Amount entity:
#GeneratedValue
#Id
#Column(nullable = false)
private Integer pk;
//....
My questions are:
Why a non-valid negative value occurs in the FK ? (the primary key targeted by the FK is set as auto increment, starting from one and all the preceeding inserts worked perfectly accordingly to this)
Why does the error occurs only after ~5k persists ?
Why not even a restart of the application fix the problem? DB is fine because a manual insert outside the application works (with valid FK values)
My suspicion is Hibernate and how it manages the auto_increment on the DB. The are many posts on the topic but none of them fit my specific case (error occurring only after a certain usage).
Last note: On the DB auto_increment is initially set to 1. All the initial persists (before the error appears) of the Amount entity have a incremental PK that start by 1: (1,2,3, ...). So again why after a while Hibernate comes up with a negative (incompatible to the FK)?
Thank you so much in advance for your help.
Best
G.
Maybe is the limite of type int ?
Try this :
#Id
#GeneratedValue (strategy = GenerationType.AUTO)
#Column (name = "id")
private long id;
we had exact the same issue and it was due to old mariadb jdbc driver. We were using version 1.4.2
After switching to the version 1.5.5 of mariadb jdbc driver the problem were gone
I have a table with a simple int id column with Identity auto increment in SQL Server.
The entity's Id is annotated with #Id and #GeneratedValue
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id", length = 4, precision = 10, nullable = false)
private Integer id;
In SQL Server the column is properly set as Identity with Seed and Increment equals to 1.
When I try to persist an instance of this entity, Hibernate tries to query the hibernate_sequence table to obtain the ID value. Since I haven't created that table in my schema I'm getting an error:
could not read a hi value: com.microsoft.sqlserver.jdbc.SQLServerException: Invalid object name 'MySchema.hibernate_sequence'
If I change the generation type to IDENTITY everything works as expected
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", length = 4, precision = 10, nullable = false)
private Integer id;
I cannot change it this way, since my App will run both on MS SQL and ORACLE, and the latter does not support auto incremented columns.
As far as I know the AUTO type should use the auto increment behaviour if the underlying database has support to it, so I don't know why is not working.
UPDATE:
It took me some time but I was able to understand exactly what is going on.
I am working with legacy databases with the following behaviours:
MSSQL: id generation uses table IDENTITY
ORACLE: id generation uses a trigger. The trigger queries and updates a custom table where all the "next ids" are stored. This table is called SEQ.
Here is the outcome of using some id generation strategies:
AUTO: does not work in MSSQL, as explained above
IDENTITY: works in MSSQL but is not supported by Oracle
"native": works in MSSQL but fails in ORACLE. It fails because Hibernate activates its default sequence strategy, which uses hibernate_sequences.nextval. Since this is a legacy application the values from the SEQ table (mentioned above) and the hibernate_sequences are not synchronized (SEQ's value for that particular table is at 6120, and hibernate_sequences' is at 1, which is expected since it was not used until now).
So what I need to figure out is a way to configure that entity to:
Use MSSQL Identity feature
OR
When using Oracle, do not automatically set any value to the ID variable and leave everything up to the pre-existing trigger
This can cause me serious issues on Oracle when I need to insert entities that depend on the main entity (via foreign key), because Hibernate won't know which ID value was generated by the "external" trigger.
I had a similar problem and found this information (deeper explained in here).
Adding this property into my persistence.xml file fixed the issue:
<property name="hibernate.id.new_generator_mappings" value="false" />
Orcale 12c supports IDENTITY and SQL SERVER 2012 supports SEQUENCES. I believe a SEQUENCE is always a better choice than an IDENTITY. IDENTITY disables batching and SEQUENCES allow you to provide optimizers, such as the pooled-lo optimization strategy.
This is how the actual identifier generator is chosen for the configured GenerationType value:
switch ( generatorEnum ) {
case IDENTITY:
return "identity";
case AUTO:
return useNewGeneratorMappings
? org.hibernate.id.enhanced.SequenceStyleGenerator.class.getName()
: "native";
case TABLE:
return useNewGeneratorMappings
? org.hibernate.id.enhanced.TableGenerator.class.getName()
: MultipleHiLoPerTableGenerator.class.getName();
case SEQUENCE:
return useNewGeneratorMappings
? org.hibernate.id.enhanced.SequenceStyleGenerator.class.getName()
: "seqhilo";
}
If you use the new identifier generators:
properties.put("hibernate.id.new_generator_mappings", "true");
The AUTO will actually use a SequenceStyleGenerator and where the database doesn't support sequences, you end up using a TABLE generator instead (which is a portable solution but it's less efficient than IDENTITY or SEQUENCE).
If you use the legacy identifier generators, you then end up with the "native" generation strategy, meaning:
public Class getNativeIdentifierGeneratorClass() {
if ( supportsIdentityColumns() ) {
return IdentityGenerator.class;
}
else if ( supportsSequences() ) {
return SequenceGenerator.class;
}
else {
return TableHiLoGenerator.class;
}
}
If a new Oracle12gDialect is going to be added and it will support IDENTITY, then AUTO might switch to IDENTITY rather than SEQUENCE, possibly breaking your current expectations. Currently there is no such dialect available so on Oracle you have SEQUENCE and in MSSQL you have IDENTITY.
Conclusion:
Try it like this:
#Id
#GenericGenerator(name = "native_generator", strategy = "native")
#GeneratedValue(generator = "native_generator")
private Long id;
make the id a Long instead of Integer, and you can let the HBMDDL handle the primary key column type.
force Hibernate to use the "native" generator
If your legacy system uses a table for generating sequence values and there was no hilo optimization ever used you can use a table identifier generator:
#Id
#GeneratedValue(generator = "table", strategy=GenerationType.TABLE)
#TableGenerator(name = "table", allocationSize = 1
)
private Long id;
You can also use the JPA table generator, just make sure you configure the right optimizer. For more info check my Hibernate tutorial
because
#GeneratedValue(strategy = GenerationType.AUTO)
use SequenceStyleGenerator by default in earlier versions
you have to look at this https://hibernate.atlassian.net/browse/HHH-11014
In my spring project, the tables in database are created automatically by Hibernate using my entity classes as base, but I insert some default values in the table manually (using pgAdmin3).
Because that, I am facing now this problem: when I try insert a value via Java code in one of the tables which already have values, I receive a error message, saying the primary key already exists in the database.
Anyone knows how to solve this problem?
UPDATE
That's how I declare my primary key in my class:
#Id
#Column(name = "id")
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
Call this SQL query once per table to set the sequence to the next free number:
SELECT setval('tblname_id_seq', max(id)) FROM tblname;
tblname being the actual name of the table.
Hibernate may use a different naming convention, or the sequence may have been renamed. If you can't find the sequence behind the serial column, check with (per documentation):
SELECT pg_get_serial_sequence(tblname, column_name)
More details:
Modify Django AutoField start value
How to import a CSV to postgresql that already has ID's assigned?
The problem here might be that you declare the id as a primitive instead of a wrapper.
So instead of:
private int id;
You should have:
private Integer id;
When you create the entity with the id is initialized as 0, instead of NULL.
That's why you get duplicate id constraint violation exceptions.
Only when the id is NULL the AUTO generation strategy will delegate the id assignment to the database.