I want to use an autoincrement ID for a field that is a String, like this:
#Id
#GeneratedValue(strategy = TABLE, generator = "MY_GENERATOR")
#TableGenerator(name = "NAME", table = "MY_TABLE", pkColumnName = "MY_COLUMN", pkColumnValue = "MY_REF", valueColumnName = "SOMETHING", allocationSize = 1)
#Column(name = "COL", unique = true, nullable = false, length = 3)
public String getFcvCval() {
return this.fcvCval;
}
But I get an exception of type:
org.hibernate.id.IdentifierGenerationException: Unknown integral data type for ids : java.lang.String
I cannot change the type of the column (it needs to be String). Is there a way to map the generated Numerical ID to its String value?
Related
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 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
I am using below code to generating the sequence, upon transaction commit it throws an unique constraint error from db and all the time its not happening.
While tracing the logs, the sequence which is used to insert the data was already used by another row(data in table).
Suppose using hbm files for this entity class its inserting correctly.
#Id
#GenericGenerator(name="detailsseq", strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator", parameters =
{ #Parameter(name="sequence_name", value = "DETAILS_SEQ"),
#Parameter(name = "optimizer", value = "hilo"),
#Parameter(name = "increment_size", value = "1"),
#Parameter(name = "allocationSize", value = "1")
})
#GeneratedValue(generator = "detailsseq", strategy = GenerationType.AUTO)
#Column(name = "ID", unique = true, nullable = false, precision = 30, scale = 0)
public BigDecimal getId() {
return this.id;
}
Application using hibernate 4.3 and orcale10g.
Please help me to resolve the issue.
I am using hibernate to create entity. The attributes I used are as below :
#Id
#SequenceGenerator(name = "customer-id-gen", sequenceName = "CUSTOMERS_SEQ", allocationSize = 1)
#GeneratedValue(generator = "customer-id-gen", strategy = GenerationType.SEQUENCE)
#Column(name = "CUSTOMER_ID", length = 4, nullable = false)
private int customerId;
#Column(name = "CUSTOMER_NAME", length = 40, unique = false, nullable = false)
private String customerName;
#Column(name = "PHONE_NO", unique = true, nullable = true, length = 10)
private Long phoneNo;
However as i can see through logs that table created is as following structure :
create table CUSTOMER_ALL (
CUSTOMER_ID number(10,0) not null,
CUSTOMER_NAME varchar2(40 char) not null,
PHONE_NO number(19,0) unique,
primary key (CUSTOMER_ID)
)
I am not able to figure out how the phone_no attribute is converted into 19 size and customer_id to 10 ?
As per JPA, length only applies to String types.
Type 'int' controlled the storage size of CUSTOMER_ID.
Type 'Long' controlled the storage size of PHONE_NO.
Do you really want a phone number to be a Long? Better a String?
i will use sequence for primary key. I use hibernate tools in eclipse to generate ORMs. i couldn't identify an option to mention sequence for ID in java bean.
like
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "G1")
#SequenceGenerator(name = "G1", sequenceName = "LOG_SEQ")
#Column(name = "ID", unique = true, nullable = false, precision = 22, scale = 0)
public int getId() {
return this.id;
}
Is there a way to mention it and generate bean class with it?
In the definition below LOG_SEQ is the name of the sequence in the database that that ID should use to generate the values
#SequenceGenerator(name = "G1", sequenceName = "LOG_SEQ")
Have
java.lang.String sequenceName
(Optional) The name of the database sequence object from which to obtain primary key values.
At the end you should substitue sequenceName for the one that is in your database schema consider using
schema property of the annotation, take a look here