Cannot retrieve the id of the last inserted row in Hibernate using Oracle - 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());

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!

Hibernate insert with id generation on insert trigger with sequence

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

How to set default value to version column for existing data?

I am trying to use optimistic locking.
I am adding the version column to my table how do I set the default value to the version column for existing data or this is sufficient on entity?
#Version
#Column(name = "VERSION")
private Long version = 0L;
The most easiest way it to do this in the database.
Of course you need to add the version column anyway: something like:
alter table MyEntity add column version INT(11); //no not null constraint here!
and then just add the first value to all entities:
update MyEntity set 'version' = 1;
now you can also add the not null constraint
alter table MyEntity modify version INT(11) NOT NULL;
(I expect that you stop the application while you add the version column).
In case of Oracle as a database - use with values option for nullable columns
alter table MyEntity add column version INT(11) default 0 with values
for not-null columns - DB will updates to default value for existing rows
alter table MyEntity add column version INT(11) not null default 0
From Oracle-11g onwards, default values are retrieved from metadata
for null values on modified field, Oracle does not perform update on each row to fill default values.
see - https://chandlerdba.com/2014/10/30/adding-not-null-columns-with-default-values/

Way to get GORM/Hibernate to work with trigger that sets primary key

I have an existing Oracle database that sets the primary key for an insert via a trigger.
TRIGGER SET_schedtemplate_id_template
BEFORE INSERT
ON schedtemplate
FOR EACH ROW
BEGIN
SELECT schedtemplate_id_template_SEQ.NEXTVAL
INTO :NEW.id_template
FROM DUAL;
END;
We have other applications that depend on this approach for this database
I want to be able to map this database in GORM in my domain object
static mapping = {
autoTimestamp true
table 'schedtemplate'
version false
id column: 'id_template', generator: 'sequence', params: [sequence: 'SCHEDTEMPLATE_ID_TEMPLATE_SEQ']
}
The problem with this approach is that GORM increments the sequence to say 12 but then on insert the sequence gets incremented again to 13. This means other objects in the object graph violate foreign key constraints as they are using GORM's 12 instead of the trigger's 13.
It appears the hibernate setting hibernate.jdbc.use_get_generated_keys = true was developed for this purpose.
How do I configure GORM/Grails to use this setting?
The trigger assigned identity column in Hibernate was discussed here hibernate and DB triggers
Now there is a question, how to configure it in GORM.
Try to use the custom identity generator described above like this :
static mapping = {
...
id column: 'id_template', generator: 'jpl.hibernate.util.TriggerAssignedIdentityGenerator'
}

Auto Generated Id Using Fluent NHibernate with Oracle 11g

I am having issues inserting records using Fluent NHibernate. The code is trying to get a number from a sequence that is non-existent for the KEY field.
{"could not insert: [Class Name ][SQL: INSERT INTO Schema.TableName (KEY, ID) VALUES (hibernate_sequence.nextval, ?) returning KEY into :nhIdOutParam]"}
Of course the hibernate_sequence sequence doesn’t exist in the database. If I do an insert using SQL Developer say:
INSERT INTO Schema.TableName (ID) VALUES (90); this works and my primary key (KEY) is auto-generated.
I know you can use a sequence to auto generate this value using GenerateBY.Sequence() but is there a way to insert the record using the SQL statment about using Fluent Nhibernate?
I have my class mapped to the primary key in my class for ex.
Id(x => x.Id, "KEY");
Use this. It will insert all of the fields except for the Id which will let Oracle set it for you.
Id(x => x.Id, "KEY").GeneratedBy.Increment();

Resources