Hibernate insert with id generation on insert trigger with sequence - oracle

Here my problem, I have a sequence in my oracle database and a trigger on insert to fetch the next value from the sequence and put it as id. With a tool like sql developer, it works perfectly.
My id is defined at this
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "MY_SEQUENCE")
#SequenceGenerator(sequenceName = "MY_SEQUENCE", allocationSize = 1, name = "MY_SEQUENCE")
private BigInteger jobId;
The problem is hibernate firstly read the next value of the sequence, set it as the id and then persist it. Then my database update my id with the next value of the sequence but that new id isn't "updated" in my code after my .save(entity).
I read that I should use the GenerationType.IDENTITY but I would like to do batch inserts and I also read that with IDENTITY the batch inserts is not possible.
If possible, I would like to keep my trigger so like that hibernate doesn't have to call the database each time I insert and be able to do batch inserts.
Edit: I'll probably need to insert near a million of rows

Related

Cassandra + SpringBoot: Configure Table to automatically add INSERT timestamp

Small question regarding Cassandra, with the context of a SpringBoot application please.
I am interested in adding onto the table the timestamp of when a row gets inserted onto the table.
Therefore, when creating the table, I do this:
create table mytable (someprimarykey text PRIMARY KEY, timestamp long, someotherfield text);
I can see a column timestamp, happy.
Then, the web application:
#Table
public class MyTable {
#PrimaryKey
private final String somePrimaryKey;
private final long timestamp;
private final String someOtherField;
//constructor + getters + setters
And when I insert, I will do the usual:
MyTable myTable = new MyTable(somePK, System.currentTimeMillis(), "foo");
myTableRepository.save(myTable);
This works fine, I can see in the table my record, with the time of the insert, happy.
Problem:
Now, for the hundreds of POJOs I am interested to insert into Cassandra, all of them are carrying this timestamp long field. Somehow, on the web application layer, I am dealing with a database concept, the timestamp of the write.
Question:
May I ask if it is possible to delegate this back to the database? Some kind of:
create table mytable (someprimarykey text PRIMARY KEY, hey-cassandra-please-automatically-add-the-time-when-this-row-is-written long, someotherfield text);
or
create table mytable (someprimarykey text PRIMARY KEY, someotherfield text WITH default timestamp-insert-time-column);
And the web app can have the abstraction creating and inserting POJOs without carrying this timestamp field?
Thank you
It isn't necessary to store the insert time of each row separately since Cassandra already stores this for all writes in the metadata.
There is a built-in CQL function WRITETIME() which returns the date/time (encoded in microseconds) when a column was written to the database.
In your case, you can query your table with:
SELECT WRITETIME(someotherfield) FROM mytable WHERE someprimarykey = ?
For details, see the CQL doc on retrieving the write time. Cheers!

How to migrate [DDL] from generation type identity to generation type sequence for postgres db

As of now I am generating id using
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
the sequence generated in DB is like:
CREATE SEQUENCE public.table_name_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.table_name_id_seq OWNED BY public.table_name.id;
ALTER TABLE ONLY public.table_name ALTER COLUMN id SET DEFAULT``nextval('public.table_name_id_seq'::regclass);
or at some places in definition itself:
CREATE TABLE "public.table_name" (
"id" int8 NOT NULL DEFAULT nextval('table_name_id_seq'::regclass),
"some_column" varchar(255),
PRIMARY KEY ("id")
);
I am using hibernate as JPA provider, I needed to enable insert and update batching due to which I want to change the generation type to SEQUENCE from IDENTITY.
After changing to generation type sequence [in hibernate] and using older sequence [of postgres] i see weird id being generated. This is not the expected behavior.
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "table_name_id_seq ")
private Long id;
The ids which are highlighted in yellow is created after using generation type sequence. I was expecting id after 23 to be 24 and not -22.
How can I fix this? How can I smoothly shift from IDENTITY to SEQUENCE? What is the proper DDL?
Answering to this part of your question:
How can I smoothly shift from IDENTITY to SEQUENCE? What is the proper DDL?
You should do the following things:
Drop the default value from the table_name.id column:
alter table only table_name
alter column id drop default;
Correct your mapping for the id field:
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "my_generator")
#SequenceGenerator(name = "my_generator", sequenceName = "table_name_id_seq", allocationSize = 1)
private Long id;
Please note that an allocationSize should be equal to the INCREMENT BY of your sequence definition.

Spring Data/JPA PostgreSQL SequenceGenerator and duplicate primary keys on concurrent access to the same database

In my Spring Data/JPA project I use ProstgreSQL database.
I use a following mapping for my JPA entity PK on cards table:
#Id
#SequenceGenerator(name = "cards_id_seq", sequenceName = "cards_id_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.AUTO, generator = "cards_id_seq")
private Long id;
Everything works fine until some other applications or persons do not insert manually new records into this table. Once it happens my application fails with the following error:
Caused by: org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "cards_pkey"
Detail: Key (id)=(42) already exists.
when tries to insert records because of out of sync between PostgreSQL sequence object and actual PK IDs in the database.
What am I doing wrong and how to solve this situation in order to assign the correct IDs to new records inserted via my application ?

Cannot retrieve the id of the last inserted row in Hibernate using Oracle

I'm using Hibernate Tools 3.2.1.GA with the Spring version 3.0.2. I'm trying to retrieve the id of the last inserted row into the Oracle(10g) database as follows.
Session session=NewHibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
Country c=new Country();
c.setCountryId(new BigDecimal(0));
c.setCountryName(request.getParameter("txtCountryName"));
c.setCountryCode(request.getParameter("txtCountryCode"));
Zone z=(Zone) session.get(Zone.class, new BigDecimal(request.getParameter("zoneId")));
c.setZone(z);
session.save(c);
session.flush();
System.out.println(c.getCountryId());
session.getTransaction().commit();
This statement System.out.println(c.getCountryId()); is expected to return the currently inserted id after the data is serialized to the database and before the transaction is committed but it doesn't because of the following line in the preceding code snippet (as it presumably appears to me).
c.setCountryId(new BigDecimal(0));
I'm not sure why this statement is required in my case (while inserting). I saw this statement nowhere. Omission of this line causes the following exception to be thrown.
org.hibernate.id.IdentifierGenerationException: ids for this class
must be manually assigned before calling save(): model.Country
Is this statement c.setCountryId(new BigDecimal(0)); really required during insertion? It's a sequence generated primary key in the Oracle database and because of that line, this statement System.out.println(c.getCountryId()); always returns 0 which is actually expected to return the currently inserted id in the current session.
So, how can I get the last generated id in this case? Am I following a wrong way, is there a different way?
EDIT:
CREATE TABLE "COUNTRY"
(
"COUNTRY_ID" NUMBER(35,0) NOT NULL ENABLE,
"COUNTRY_CODE" VARCHAR2(10),
"COUNTRY_NAME" VARCHAR2(50),
"ZONE_ID" NUMBER(35,0),
CONSTRAINT "COUNTRY_PK" PRIMARY KEY ("COUNTRY_ID") ENABLE,
CONSTRAINT "COUNTRY_FK" FOREIGN KEY ("ZONE_ID")
REFERENCES "ZONE" ("ZONE_ID") ON DELETE CASCADE ENABLE
)
/
CREATE OR REPLACE TRIGGER "BI_COUNTRY"
before insert on "COUNTRY"
for each row
begin
select "COUNTRY_SEQ".nextval into :NEW.COUNTRY_ID from dual;
end;
/
ALTER TRIGGER "BI_COUNTRY" ENABLE
/
The exception 'ids for this class must be manually assigned before calling save()' means that you are using the identifier generation strategy of 'Assigned'.
assigned
lets the application assign an identifier to the object before save() is called. This is the default strategy if no element is specified.
If you do not define any strategy, hibernate defaults to 'assigned'. 'assigned' strategy implies that hibernate expects that the application supplies it's own ids.
If you want to use a sequence id generator in Oracle, you can do so with the following configuration -
If you are using xml -
<id name="countryId" type="java.lang.Integer">
<column name="Country_Id" />
<generator class="sequence">
<param name="sequence">Country_Id_Seq</param>
</generator>
</id>
If you are using annotations -
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="Country_Id_Seq")
#SequenceGenerator(name="Country_Id_Seq", sequenceName="Country_Id_Seq" )
private Integer sequence;
And your code should look like so -
Country c=new Country();
c.setCountryName(request.getParameter("txtCountryName"));
c.setCountryCode(request.getParameter("txtCountryCode"));
Zone z=(Zone) session.get(Zone.class, new BigDecimal(request.getParameter("zoneId")));
c.setZone(z);
session.save(c);
session.flush();
System.out.println(c.getCountryId());
When 'session.save(c)' executes, hibernate makes the following sql call to Oracle, retrieves the id and sets it in Country object.
select Country_Id_Seq.nextVal from dual;
Problem with trigger
Since you are using a trigger to increment the id when a row is inserted, this will cause a problem with hibernate sequence. Hibernate is using the sequence to generate an id and the database is using the trigger to increment the id. This is resulting in the id being incremented twice.
You have a three options to resolve this.
Delete the trigger because it's not necessary.
If you still need the trigger because the table could be updated outside the application, you could update the trigger such that the id is generated only if the id is not set in the insert statement
HIbernate issue with Oracle Trigger for generating id from a sequence
Create a custom id generator that uses the trigger to set the id in the data before it is saved to db. Check out the following link - https://forum.hibernate.org/viewtopic.php?t=973262
If the values into an ID column generated by a sequence, then you should associate that sequence with your ID column in the entity definition so that the attribute is filled in with the ID value by Hibernate during insertion.
Using annotations:
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "CountryIdSequence")
#SequenceGenerator(name = "CountryIdSequence", sequenceName = "COUNTRY_ID_SEQUENCE")
#Column(name = "COUNTRY_ID")
private BigDecimal countryId;
Using hbm:
<id name="countryId" type="big_decimal">
<column name="COUNTRY_ID" />
<generator class=""sequence">
<param name="sequence">COUNTRY_ID_SEQUENCE</param>
</generator>
</id>
Then, it will be available after the save.
Any changes made to the entity at the database layer are not reflected in the hibernate entity layer until you refresh the object.
session.save(c);
session.flush();
// Refresh the object for columns modified in the DB by IDENTITY / SEQUENCE / Triggers.
session.refresh(c);
System.out.println(c.getCountryId());

Get the latest inserted PK after submitting a linq insert stored procedure

I have a stored procedure that updates a table using linq, eg: (this is just example code by way)
using (DataContext db = new DataContext())
{
d.sp_Insert_Client( textboxName.Text, textBoxSurname.Text);
}
What I would like to know is how to retrieve (if possible) newly generated primary key of the above inserted row, as I need this primary key as a foreign key to complete another insert.
You have to modify your stored procedure to return that value from database and then regenerate your Linq mapping to update that change in your ORM files. After that your sp_Insert_Client method will return an integer.
The other way to do that is to add another parameter into the query and mark it as output one.
To get last inserted I'd inside your SP use SCOPE_IDENTITY: http://msdn.microsoft.com/pl-pl/library/ms190315.aspx
I think you need to retrieve value by using the output parameter that you can check over here : Handling stored procedure output parameters A Scott Gu post which explain that easily
Procedure
For you
create procdeudre nameofprocedure
#id int output
as
begin
insert in your table statement
--retrieve identity value
select #id = scope_identity();
end
Code

Resources