Unique constraint with null columns (Hibernate, PostgreSQL) - spring

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

Related

JPA stores null value in CLOB when setting an empty String

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)

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 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/

Conditional unique constraint in oracle db

I have a situation where I need to enforce a unique constraint on a column[attribute] depending on another column value.
So for example, I have a table like Table(ID, EID, Name, ISDeleted)
ISDeleted can only have a value null or 'y' (active or deleted), and i want to create a unique constraint on EID, ISDeleted only when ISDeleted = null, since I dont care if there are multiple deleted records with the same id. Please note that, EID can have null value.
I am using Oracle DB for this.
You can't create a constraint. But you can create a unique function-based index. This takes advantage of the fact that Oracle does not index NULL values-- any rows where isDeleted is NOT NULL will not be included in the index so the unique constraint won't apply to them.
CREATE UNIQUE INDEX one_not_deleted
ON table_name( (CASE WHEN isDeleted IS NULL
THEN eid
ELSE null
END) );

Creating multi-column constraints depending on column value

I'm trying to create a constraint on an oracle database that says the following:
If column1 == someValue then the combination of column2 and column3 has to be unique for all entries with column1 == someValue
I'm familiar with the concepts of unique and check constraints, and I've tried expressing the constraint with those constructs. However, I can't seem to find a way to include the condition. Which is why I'm wondering if it is even possible.
The table I want to create constraint for is created by Hibernate mapping the following class hierarchy (most of the attributes ommited for brevity):
class MyClass {
String name;
MyClass parent;
}
class MySubClass extends MyClass {
String businessValue;
}
The classes are mapped using a single table strategy and using different discriminator values for each type. It's a customer requirement that for all instances of MySubClass the combination of name and parent has to be unique (column1 would be the discriminator value). It would be easy to enforce such a constraint on the parent class through a table constraint. However, that constraint must only apply to MySubClass.
There is the possibility of validating the data before entering it into the the database with frameworks such as Hibernate Validator. But since the validation would need database access anyway, a database constraint seems the more performance saving way to do it.
You can't do this with a constraint, but you can do it using a "function-based index" (FBI) like this:
create unique index mytable_idx on mytable
( case when column1 = 'somevalue' then column2 end
, case when column1 = 'somevalue' then column3 end
);
This only creates unique index entries for rows with column1 = 'somevalue', so other rows can contain duplicates but these cannot.

Resources