JPA stores null value in CLOB when setting an empty String - oracle

Let's say I have an entity Entry with a Clob column like:
#Entity
public class Entry {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQUENCE_GENERATOR")
private Long id;
#Lob
#Column(name = "value")
private String data;
...
And in database we have an existing Entry row where VALUE is a clob initialized with Oracle's EMPTY_CLOB() function.
I need to clone the existing to a new entity, but after querying and setting an empty String for simulate the EMPTY_CLOB() from Java side, Oracle's show up a (null) value instead of an empty Clob.
Whe use hibernate as JPA implementation.
Expected after saving entity 2 with JPA:
SELECT * FROM ENTRY
ID VALUE
1 <------------ Returning an empty CLOB here
2 (null)
Actual behaviour
SELECT * FROM ENTRY
ID VALUE
1 <------------ Returning an empty CLOB here
2 <------------ Returning an empty CLOB here

It's because an empty string in Java is not NULL, and then the ORM generates an INSERT with "" for that column and the DEFAULT defined in the DDL is not applied, and eventually the '' is converted to NULL before caste-ed to CLOB.
With Hibernate, you may try to put #org.hibernate.annotations.Entity(dynamicInsert = true) or #DynamicInsert on the entity and don't set the column to empty string: if NULL, dynamic insert should skip it when generating the INSERT statement, so the DEFAULT defined in the DLL should be applied.
(Be aware of https://hibernate.atlassian.net/browse/HHH-7074)
(And you may also need dyanmicUpdate = true)

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

Hibernate Jpa - constraint violation exception on Primary Key(Sequence)

I use Hibernate JPA in my application. I have a Table that has a Primary key(Sequence). Service inserts records to that table.
Version: Oracle 12c
Dialect: org.hibernate.dialect.Oracle10gDialect
Issue :
We face problem(Unique constraint violation on SEQUENCE Key) during the load testing.
Questions :
This issue is not occurring all the time. But only during load test. Can someone please check and help to use thread safe generator?
Is it DB side sequence definition issue or at Java side?
DB Sequence :
CREATE SEQUENCE MY_SEQ
START WITH 1
INCREMENT BY 1
NOMINVALUE
NOMAXVALUE
CACHE 30
NOORDER;
CREATE TABLE MY_TABLE (
MY_PRIMARY_KEY INT default MY_SEQ.nextval NOT NULL,
VALUE_COL VARCHAR2(10) NULL
);
Entity :
public class MyTableEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "MY_PRIMARY_KEY")
#GenericGenerator(
        name = "mySequenceGenerator",
        strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
        parameters = {
                #Parameter(name = "sequence_name", value = "SEQUENCE MY_SEQ"),
                #Parameter(name = "increment_size", value = "1")
        }
)
#GeneratedValue(generator = "mySequenceGenerator")
private long myPrimaryKey;
#Column(name = "VALUE")
private String value;
}
Oracle 10 Dialect
For Oracle10gDialect use this configuration
#Id
#Column(name = "MY_PRIMARY_KEY")
#GeneratedValue(strategy=GenerationType.AUTO)
Long myPrimaryKey;
Hibernate creates a table and a sequence:
create table MY_TABLE (
MY_PRIMARY_KEY number(19,0) not null,
VALUE varchar2(255 char),
primary key (MY_PRIMARY_KEY))
create sequence hibernate_sequence
While storing it first gets the new sequence ID and than passes it in the INSERT statement
select hibernate_sequence.nextval from dual
insert into MY_TABLE (VALUE, MY_PRIMARY_KEY) values (?, ?)
Oracle 12 Dialect
If you use Oracle 12 that natively supports IDENTITY column it is prefered to upgrade to Oracle12cDialect (note that this requires Hibernate 5.3)
Set the strategy to GenerationType.IDENTITY
#Id
#Column(name = "MY_PRIMARY_KEY", updatable = false, nullable = false)
#GeneratedValue(strategy=GenerationType.IDENTITY)
Long myPrimaryKey;
The following table is created - the important part is generated as identity which provides the unique velues.
Note that no explicite sequence is required to be created, it is managed internally .
create table MY_TABLE (
MY_PRIMARY_KEY number(19,0) generated as identity,
VALUE varchar2(255 char),
primary key (MY_PRIMARY_KEY))
While storing no ID is passed in the INSERT, it is assigned by Oracle and returned to the session
insert into MY_TABLE (VALUE) values (?) RETURNING MY_PRIMARY_KEY INTO ?
Note that in contrary to the Oracle 10 you save one round trip to the database.
Change long to Long
Because if you use long, it (by default) is 0. No generator is allowed to change existing values!
https://docs.oracle.com/javaee/7/api/javax/persistence/GeneratedValue.html

Unique constraint with null columns (Hibernate, PostgreSQL)

I have class like Clazz
#Table(
name="tablename",
uniqueConstraints=
#UniqueConstraint(
name= "uniqueColumn_deleted_uk",
columnNames={"myuniquecolumn", "deleted"}
)
)
public class Clazz {
#Column(name = "deleted")
private LocalDateTime deleted;
}
deleted is nullable, PosgreSQL creates unique index like
CREATE UNIQUE INDEX uniqueColumn_date_uk ON public.tablename (short_code_3, deleted);
and it allows insert duplicate myuniquecolumn when deleted is NULL.
How prevent this?
I want have non duplicates when deleted is null.
You should create two partial unique indexes
create unique index on public.tablename (short_code_3, deleted) where deleted is not null;
create unique index on public.tablename (short_code_3) where deleted is null;
(I don't know how to do it in your ORM).
This is not possible because null is never = null.
Read more about null values in SQL https://en.wikipedia.org/wiki/Null_(SQL)
If you want to have the deleted column in the unique index you must provide a default value for that column.
Tow partial indexes like klin provided are best practice up to Postgres 14.
Postgres 15 adds NULLS NOT DISTINCT for this purpose:
CREATE UNIQUE INDEX foo_idx ON public.tbl (short_code_3, deleted) NULLS NOT DISTINCT;
See:
Create unique constraint with null columns

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/

Resources