I try to autogenerate value which is not PK, when I do save in DB.
I created Entity with value:
class Entity {
// Other values
#NaturalId
#SequenceGenerator(name = "number_sequence", sequenceName =
"number_sequence")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "number_sequence")
private Long number;
}
And script for sequence:
CREATE SEQUENCE schema.number_sequence AS BIGINT
INCREMENT 1
START 1
OWNED BY table_name.number;
But when I build Entity without number and save it to DB I have an error:
org.postgresql.util.PSQLException: ERROR: null value in column "number" violates not-null
constraint
Detail: Failing row contains (e925b4fb-5147-4754-b949-08d79a6ad764, 2020-06-04
14:31:50.49584+03, null, bd765ef29c3211e98b6b019787d6f1ee,
1e100b1da97b11e98b6b511f0c71b787).
Where I wrong?
Thanks to Kayaman, and SternK. What have I done:
#NaturalId
#Generated(value = GenerationTime.INSERT)
#Column(insertable = false, updatable = false)
private Long number;
on my Entity and:
CREATE SEQUENCE number_sequence AS BIGINT
INCREMENT 1
START 1
OWNED BY table.number;
ALTER TABLE table
ALTER COLUMN number SET DEFAULT nextval('number_sequence');
In my entity. You can add schema.number_seq or schema.table if query above doesn't work
Related
In liquibase I added a script for creating a database and also use in Java / Spring Entity with GeneratedValue and my SequenceGenerator. In the case of the script in liquibase, do I have to add the generation of the identifier with the sequence to it, or is it enough that I have it from the level of my entity?
#SequenceGenerator(name = "TEST_DATA_SEQ", sequenceName = "TEST_DATA", initialValue = 1, allocationSize = 1)
public class TestPPData {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "TEST_DATA_SEQ")
#Column(name = "ID", nullable = false)
private Long id;
CREATE TABLE TEST_DATA
(
ID NUMBER(10) PRIMARY KEY,
....
);
COMMENT ON COLUMN TEST_DATA.ID IS 'Primary key';
And my LB
- changeSet:
id: test
author: test
changes:
- sqlFile:
dbms: oracle
encoding: utf8
path: scripts/test.sql
relativeToChangelogFile: true
splitStatements: true
stripComments: false
or I must need add ?
createSequence:
incrementBy: 1
sequenceName: TEST_DATA_SEQ
startValue: 1
I have recursive parent and child relation for Graph. When I have a large Graph with 50 or more nodes on a Single hibernate session, I get an error message "A different object with the same identifier value was already associated with the session". This is due to the fact the default allocation Size in Spring JPA is 50. I have overcome this error by setting allocationSize to 100 and increment by 100. But that does not solve the root of the problem. I can have any arbitrary # nodes in ONE session. I use saveAndFlush(NodeEntity) which throws this error message.
My question is How do I force Hibernate to fetch the primary key from DB after allocation size is limit is reached and be able to generate a new set primary key in a single session?
Hibernate version: hibernate-core-5.4.30.Final.jar
Error:
A different object with the same identifier value was already associated with the session : [graph.entity.NodeEntity#53]; nested exception is javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [graph.entity.NodeEntity#53]
//GRAPH DATABASE.
CREATE TABLE IF NOT EXISTS node
(
id SERIAL NOT NULL,
name character varying(255) NOT NULL,
parent_id int,
CONSTRAINT node_pkey PRIMARY KEY (id),
CONSTRAINT node_parent_id_fk FOREIGN KEY (parent_id)
REFERENCES node (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
);
CREATE TABLE IF NOT EXISTS graph
(
id SERIAL NOT NULL,
name character varying(255) NOT NULL,
node_id int,
CONSTRAINT graph_pkey PRIMARY KEY (id),
CONSTRAINT graph_node_fk FOREIGN KEY (node_id)
REFERENCES node (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT graph_node_id_uk UNIQUE (node_id)
);
public class NodeEntity {
#Id
#SequenceGenerator(name = "node_id_seq", sequenceName = "node_id_seq",allocationSize = 50)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "node_id_seq")
#Column(name = "id")
int id;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER,orphanRemoval=true)
#JoinColumn(name = "parent_id")
private List<NodeEntity> children = new LinkedList<NodeEntity>();
}
public class GraphEntity{
#Id
#SequenceGenerator(name = "graph_id_seq", sequenceName = "graph_id_seq")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "graph_id_seq")
#Column(name = "id")
int id;
#OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "NODE_ID", unique = true, nullable = true, insertable = true, updatable = true)
private NodeEntity rootNode;
}
// Way to reproduce this.
void generate150deepChild(int count,NodeEntity node){
if(count == 100){
return
}else{
NodeEntity newNode = new NodeEntity("Child " +count)
node.getChildren().add(newNode);
cout++;
generate150deepChild(count,newNode);
}
}
NodeEntity rootNode = new NodeEntity("ROOT");
// PLEASE NOT if # NodeEntity < 50 everything works fine.
generate150deepChild(0,rootNode);
// PLEASE NOT all ids are zero so they are new node.
GraphEntity graph = new GraphEntity("TEST");
graph.setRootNode(rootNode);
graphRepository.saveAndFlush(graph);
// THIS WILL GENERATE Duplicate Primary key for NodeEntity.
Actual stack trace:
Caused by: javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [graph.entity.NodeEntity#131605]
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:123)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:181)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:188)
at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:823)
at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:786)
at org.hibernate.engine.spi.CascadingActions$6.cascade(CascadingActions.java:261)
at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:499)
at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:423)
at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:220)
at org.hibernate.engine.internal.Cascade.cascadeCollectionElements(Cascade.java:532)
at org.hibernate.engine.internal.Cascade.cascadeCollection(Cascade.java:463)
at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:426)
at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:220)
at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:153)
at org.hibernate.event.internal.AbstractSaveEventListener.cascadeAfterSave(AbstractSaveEventListener.java:459)
at org.hibernate.event.internal.DefaultMergeEventListener.entityIsTransient(DefaultMergeEventListener.java:247)
at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:175)
at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:104)
at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:813)
at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:786)
at org.hibernate.engine.spi.CascadingActions$6.cascade(CascadingActions.java:261)
at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:499)
at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:423)
Change to use GenerationType.SEQUENCE strategy and it should handle for you automatically :
#Id
#SequenceGenerator(name = "node_id_seq", sequenceName = "node_id_seq",allocationSize = 50)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "node_id_seq")
#Column(name = "id")
int id;
And you just need to make sure the followings:
Do not set the ID for each node manually.
The related sequence in DB (i.e. the sequence specified in sequenceName in #SequenceGenerator) is really configured and aligned with what you configure in #SequenceGenerator. In case of PostgreSQL , you can do :
alter sequence node_id_seq increment by 50;
Tip:
According to this, you can change to use the pooled or pooled-lo algorithm to reduce the DB roundtrip to get the ID by configuring the following settings:
<property name="hibernate.id.optimizer.pooled.preferred" value="pooled-lo" />
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 application in which I have autoincrement for PK Id. I had to add other autoincremented column and I used liquibase to make it work - liquibase created sequence for auto increment. When I make insert from query tool or when i do not map this field in entity and make persist autoincrement works. But when I add :
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "next_value", nullable=false, unique=true)
private Long nextValue;
I get an ugly error:
Caused by: org.postgresql.util.PSQLException: ERROR: empty value in the "next_value" column violates the limit of the required value
What is wrong here?
EDIT:
My liquibase changeset to add this column and make it autoincrement:
<addColumn tableName="table">
<column name="next_value" type="number"/>
</addColumn>
<addAutoIncrement
columnDataType="number"
columnName="next_value"
incrementBy="1"
startWith="1"
tableName="table"/>
<sql>select setval('table_next_value_seq', (select cast(current_value+1 as bigint) from gapless_sequence), false)</sql>
setval was used to start it not from 1
You have to disable insertable and updatable false
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "next_value", unique = true, nullable = false, insertable = false,updatable = false)
i guess it should change table in db
ALTER TABLE tblName(
nextValue LONG NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),
....
I am new to hibernate and I want to insert primary number in my table for unique identification. I am using Oracle as my database so do I need to create sequence in oracle to get auto increment generation number ?
I am using below code but it is not working. I have not created any sequence yet.
#Id
#Column(name = "id" )
#GeneratedValue ( strategy = GenerationType.TABLE)
I have used AUTO, SEQUENCE and IDENTITY but nothing works for me.
this is one way of using Oracle sequence in a JPA mapped entity:
#Id
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQUENCE_NAME")
#SequenceGenerator(name = "SEQUENCE_NAME", sequenceName = "SEQUENCE_NAME", allocationSize = 1, initialValue = 1)
In this way your persist() method will ask for the next value of the sequence in order to use it as ID for your entry.
You can ue this #GeneratedValue(strategy=GenerationType.AUTO)
#Id
#Column(name = "id" )
#GeneratedValue(strategy=GenerationType.AUTO)
In GenerationType.TABLE option, ID value will be filled with the column of other table.
If you are using strategy=GenerationType.TABLE you will require to mention the Table from where your ID will be filled.
For example,
#GeneratedValue(strategy=GenerationType.TABLE, generator="course")
#TableGenerator(
name="course",
table="GENERATOR_TABLE",
pkColumnName = "key",
valueColumnName = "next",
pkColumnValue="course",
allocationSize=30
)
And for other option, you can use GenerationType.AUTO option, and let hibernate decide which option to choose according to databse.
#Id
#Column(name = "id" )
#GeneratedValue (strategy = GenerationType.AUTO)
And make sure that you properly configured hibernate.cfg.xml file.