JPA EclipseLink: handle default value - postgresql

I have the following column definition:
#Column(name="active", nullable=false, columnDefinition="BOOLEAN DEFAULT FALSE"
private Boolean active;
In the Postgres database the column active is defined as BOOLEAN NOT NULL DEFAULT FALSE
But when I insert a new record, without setting active, EclipseLink generates a null value for this field and the insert statement obviously fails because Postgres does not allow a null value for the NOT NULL column.
What am I doing wrong?
Well, if I define my field as
private boolean active;
then I will indirectly set the field to false. But I cannot use this trick with Date fields. So, I am looking for a solution which will work for all column types.

The columnDefinition is only used for the DDL creation and can be database specific. You may have problems switching the DB if you use it.
Using primitive types ensures that the value is not null, and without explicitly defining a value, the primitive's default is used. However, you can also define default values for non-primitive values by assigning them: private Boolean active = false;
That will also work with Date.
When you need complex default values you can use the PrePersist annotation:
#PrePersist
public void setDefaultValues() {
if (active == null) {
active = false;
}
if (value == null) {
//do complex stuff
}
}

If you NEVER are going to set the attribute, you can use #Column (insertable = false), if you set a value, this will be ignored anyway.
This is a very common setting on Date like
#Column(nullable = false, insertable = false, updatable = false, columnDefinition = "timestamp not null default CURRENT_TIMESTAMP")
#Temporal(TemporalType.TIMESTAMP)
private Date systemDate;

Related

JPA Criteria on PostgreSQL boolean type creates invalid query

I have a regular JPA Entity which has a boolean field:
#Entity("UserAccount")
public class UserAccount {
...
#Column(name = "isActive")
private boolean isActive = false;
...
}
This is backed by a table in PostgreSQL 15. In the table definition, the field is of type BOOLEAN (not integer!):
CREATE TABLE UserAccount(
...
isActive BOOLEAN NOT NULL,
...
)
When I attempt to use the JPA Criteria API with it:
criteriaBuilder.isTrue(root.get("isActive"))
... then it will be rendered as = 1 in the SQL query which is sent to PostgreSQL:
SELECT ...
FROM UserAccount
WHERE isActive = 1
PostgreSQL rejects this query with the following error (which is pretty clear):
PSQLException: ERROR: operator does not exist: boolean = integer
Hint: No operator matches the given name and argument types. You might need to add explicit type casts.
My question is: how can I tell Hibernate that I'm using an actual PostgreSQL-native BOOLEAN type in my table instead of encoding the boolean as a number?

Adding a jpa record / bean with defaults [duplicate]

In my SQL Server 2000 database, I have a timestamp (in function not in data type) column of type DATETIME named lastTouched set to getdate() as its default value/binding.
I am using the Netbeans 6.5 generated JPA entity classes, and have this in my code
#Basic(optional = false)
#Column(name = "LastTouched")
#Temporal(TemporalType.TIMESTAMP)
private Date lastTouched;
However when I try to put the object into the database I get,
javax.persistence.PersistenceException: org.hibernate.PropertyValueException: not-null property references a null or transient value: com.generic.Stuff.lastTouched
I've tried setting the #Basic to (optional = true), but that throws an exception saying the database doesn't allow null values for the TIMESTAMP column, which it doesn't by design.
ERROR JDBCExceptionReporter - Cannot insert the value NULL into column 'LastTouched', table 'DatabaseName.dbo.Stuff'; column does not allow nulls. INSERT fails.
I previously got this to work in pure Hibernate, but I have since switched over to JPA and have no idea how to tell it that this column is supposed to be generated on the database side. Note that I am still using Hibernate as my JPA persistence layer.
I fixed the issue by changing the code to
#Basic(optional = false)
#Column(name = "LastTouched", insertable = false, updatable = false)
#Temporal(TemporalType.TIMESTAMP)
private Date lastTouched;
So the timestamp column is ignored when generating SQL inserts. Not sure if this is the best way to go about this. Feedback is welcome.
I realize this is a bit late, but I've had success with annotating a timestamp column with
#Column(name="timestamp", columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
This should also work with CURRENT_DATE and CURRENT_TIME. I'm using JPA/Hibernate with Oracle, so YMMV.
#Column(nullable = false, updatable = false)
#CreationTimestamp
private Date created_at;
this worked for me.
more info
Add the #CreationTimestamp annotation:
#CreationTimestamp
#Column(name="timestamp", nullable = false, updatable = false, insertable = false)
private Timestamp timestamp;
If you are doing development in Java 8 and Hibernate 5 Or Spring Boot JPA then use following annotation directly
in your Entity class. Hibernate gets the current timestamp from the VM and will insert date and time in database.
public class YourEntity {
#Id
#GeneratedValue
private Long id;
private String name;
#CreationTimestamp
private LocalDateTime createdDateTime;
#UpdateTimestamp
private LocalDateTime updatedDateTime;
…
}
I do not think that every database has auto-update timestamps (e.g. Postgres). So I've decided to update this field manually everywhere in my code. This will work with every database:
thingy.setLastTouched(new Date());
HibernateUtil.save(thingy);
There are reasons to use triggers, but for most projects, this is not one of them. Triggers dig you even deeper into a specific database implementation.
MySQL 5.6.28 (Ubuntu 15.10, OpenJDK 64-Bit 1.8.0_66) seems to be very forgiving, not requiring anything beyond
#Column(name="LastTouched")
MySQL 5.7.9 (CentOS 6, OpenJDK 64-Bit 1.8.0_72) only works with
#Column(name="LastTouched", insertable=false, updatable=false)
not:
FAILED: removing #Temporal
FAILED: #Column(name="LastTouched", nullable=true)
FAILED: #Column(name="LastTouched", columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")
My other system info (identical in both environments)
hibernate-entitymanager 5.0.2
hibernate-validator 5.2.2
mysql-connector-java 5.1.38
I have this working well using JPA2.0 and MySQL 5.5.10, for cases where I only care about the last time the row was modified. MySQL will create a timestamp on first insertion, and every time UPDATE is called on the row. (NOTE: this will be problematic if I cared whether or not the UPDATE actually made a change).
The "timestamp" column in this example is like a "last-touched" column.x`
The code below uses a separate column "version" for optimistic locking.
private long version;
private Date timeStamp
#Version
public long getVersion() {
return version;
}
public void setVersion(long version) {
this.version = version;
}
// columnDefinition could simply be = "TIMESTAMP", as the other settings are the MySQL default
#Column(name="timeStamp", columnDefinition="TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")
#Temporal(TemporalType.TIMESTAMP)
public Date getTimeStamp() {
return timeStamp;
}
public void setTimeStamp(Date timeStamp) {
this.timeStamp = timeStamp;
}
(NOTE: #Version doesn't work on a MySQL "DATETIME" column, where the attribute type is "Date" in the Entity class. This was because Date was generating a value down to the millisecond, however MySQL was not storing the millisecond, so when it did a comparison between what was in the database, and the "attached" entity, it thought they had different version numbers)
From the MySQL manual regarding TIMESTAMP :
With neither DEFAULT nor ON UPDATE clauses, it is the same as DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP.
If you mark your entity with #DynamicInsert e.g.
#Entity
#DynamicInsert
#Table(name = "TABLE_NAME")
public class ClassName implements Serializable {
Hibernate will generate SQL without null values. Then the database will insert its own default value. This does have performance implications See [Dynamic Insert][1].
This also works for me:-
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "CREATE_DATE_TIME", nullable = false, updatable = false, insertable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
public Date getCreateDateTime() {
return createDateTime;
}
public void setCreateDateTime(Date createDateTime) {
this.createDateTime = createDateTime;
}
I'm posting this for people searching for an answer when using MySQL and Java Spring Boot JPA, like #immanuelRocha says, only have too #CreationTimeStamp to the #Column in Spring, and in MySQL set the default value to "CURRENT_TIMESTAMP".
In Spring add just the line :
#Column(name = "insert_date")
#CreationTimestamp
private Timestamp insert_date;
#Column(name = "LastTouched", insertable = false, updatable = false, columnDefinition = "TIMESTAMP default getdate()")
#Temporal(TemporalType.TIMESTAMP)
private Date LastTouched;`enter code here`
This worked for me:
#Column(name = "transactionCreatedDate", nullable = false, updatable = false, insertable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP")

JPA generate partial unique key

I have a table with 3 columns
UUID - A UUID that is the primary key of the table
ID - A human readable ID of the resource (for a new resource, the ID should be automatically generated by a sequence)
Version - A version number
I am using JPA.
The table can contain multiple records with the same "human readable" ID and different versions.
I would like to be able to insert a new record without specifying the ID: the database should generate the ID automatically.
At the same time, when I need to insert a new version of the same resource, I would like to be able to insert a new row specifying the ID.
I have created a table where the UUID is the primary key, ID is defined as "integer generated by default as identity" and version is just an integer.
Using SQL query I can do what I want, but I do not know how to do it using JPA.
If I define the column as:
#Column(name="ID", insertable = false, updatable = false, nullable = false)
I can insert new records but the ID is always generated as new even if the resource already has one because the insert does not include the column.
If I define the column as:
#Column(name="ID", insertable = true, updatable = false, nullable = false)
The insert include the column and I am able to insert new rows specifying the ID but I cannot insert a row without the ID because the SQL generated is passing a null value for that column.
UPDATE
I have modified the configuration adding the annotation #Generated:
#Generated(value = GenerationTime.INSERT)
#Column(name="ID", updatable = false, nullable = false)
private Integer id;
With this, I am having the same problem: if I pass a value for id, the database is still generating a new one.
You can try to use #DynamicInsert annotation.
Assuming that you have the following table:
create table TST_MY_DATA
(
dt_id uuid,
dt_auto_id integer generated by default as identity,
dt_version integer,
primary key(dt_id)
);
Appropriate entity will look like this:
#Entity
#Table(name = "TST_MY_DATA")
#DynamicInsert
public class TestData
{
#Id
#GeneratedValue
#Column(name = "dt_id")
private UUID id;
// Unfortunately you cannot use #Generated annotation here,
// otherwise this column will be always absent in hibernate generated insert query
// #Generated(value = GenerationTime.INSERT)
#Column(name = "dt_auto_id")
private Long humanReadableId;
#Version
#Column(name = "dt_version")
private Long version;
// getters/setters
}
and then you can persist entities:
TestData test1 = new TestData();
session.persist(test1);
TestData test2 = new TestData();
test2.setHumanReadableId(27L);
session.persist(test2);
session.flush();
// here test1.getHumanReadableId() is null
/*
* You can use session.refresh(entity) only after session.flush() otherwise you will have:
* org.hibernate.UnresolvableObjectException: No row with the given identifier exists:
* [this instance does not yet exist as a row in the database#ff09c202-cd17-4d4a-baea-057e475fabb9]
**/
session.refresh(test1);
// here you can use the test1.getHumanReadableId() value fetched from DB

How to store Boolean type data into a column in a PostgreSQL db table with data type "bit" using HIbernate?

I have a variable of type Boolean in my entity class by name "isActive". It to mapped to a column by name "is_active" with data type as bit.
#Column(name = "is_active")
private boolean isActive;
But when ever I try to save isActive attribute of the object, I get an error:
column "is_active" is of type bit but expression is of type character
varying Hint: You will need to rewrite or cast the expression.
How do I store the values the values of isActive? I want to store "1" in the database when value of "isActive" is true and "0" when "isActive" is false.
Thank you!
You can use Hibernate's NumericBooleanType like so:
#Type(type = "numeric_boolean")
#Column(name = "is_active")
private boolean isActive;
Add below annotation above the field name, if using hibernate
#Type(type = "org.hibernate.type.NumericBooleanType")
#Column(name = "is_active")
private boolean isActive;

Hydrate non required string properties from null to empty string

If I have a model class
public class Foo
{
public string Property1 { get; set; } = string.Empty;
... other properties omitted for brevity
}
It gets save to the database as null since it is not a required property. But now when I retrieve the entity that property is null.
I have a lot of not required string properties on this entity and I don't want to have to do a lot of null checking on those properties, I just want them to be rehydrated as empty strings if they are null in the database. I don't mind them being stored in the db as null but when the entity is retrieved from the db I would like to have it mapped back to empty string if it is null in the db.
I'm thinking this must be a fairly common scenario and was wondering if I'm missing some simple way to do it with a configuration setting or something. I am fairly new to using EF.
In my OnModelCreating for this property I have:
.HasMaxLength(50)
.HasDefaultValue(string.Empty)
but it still gets stored as null and rehydrated as null. Again I don't mind it being stored as null but I want it hydrated as empty string if it is null in the db.
I tried modifying the model class like this:
private string property1 = string.empty;
public string Property1
{
get { return property1; }
set { property1 = value ?? string.Empty; }
}
thinking that EF must have to use the setter but this still resulted in a null property. So far the only way I've been able to solve it by making the property like this:
private string property1 = string.empty;
public string Property1
{
get { return property1 ?? string.empty; }
set { property1 = value ?? string.Empty; }
}
I'd really rather not have to make all my properties like that.
Can anyone suggest a cleaner way to do this or correct me if I'm doing something wrong or unusual or thinking about it wrong. Am I missing some easier way to achieve this?
I don't want to make the property required since empty string would not satisfy that case either.
The question's original Entity Framework version was EF7, the first ef-core version that was renamed to EF-core 1 later. The described behavior considerably differs from the current EF-core version (2.1.1).
Let me mention the two key points:
1
It gets save to the database as null since it is not a required property. But now when I retrieve the entity that property is null.
That's not what happens currently.
Take a simple class:
public class Item
{
public int ID { get; set; }
public string Name { get; set; }
public string Code { get; set; } = string.Empty;
}
When adding an Item of which only Name is set, the following SQL is executed:
exec sp_executesql N'SET NOCOUNT ON;
INSERT INTO [Items] ([Code], [Name])
VALUES (#p0, #p1);
SELECT [ID]
FROM [Items]
WHERE ##ROWCOUNT = 1 AND [ID] = scope_identity();
',N'#p0 nvarchar(4000),#p1 nvarchar(50)',#p0=N'',#p1=N'Item1'
The empty string is part of the insert statement. Also, when retrieving the item from the database, its Code is an empty string.
2
In my OnModelCreating for this property I have:
.HasMaxLength(50)
.HasDefaultValue(string.Empty)
but it still gets stored as null and rehydrated as null.
That, too, is different now. Take the same class but now with:
public string Code { get; set; } // removed "= string.Empty;"
...and mapping for Code:
modelBuilder.Entity<Item>().Property(p => p.Code).HasMaxLength(50).HasDefaultValue(string.Empty);
...then this is the resulting table:
CREATE TABLE [Items] (
[ID] int NOT NULL IDENTITY,
[Name] nvarchar(50) NOT NULL,
[Code] nvarchar(50) NULL DEFAULT N'', -- Default constraint
CONSTRAINT [PK_Items] PRIMARY KEY ([ID])
);
As you see, the mapping instruction is translated into a database default.
When adding an Item of which only Name is set, the following SQL is executed:
exec sp_executesql N'SET NOCOUNT ON;
INSERT INTO [Items] ([Name])
VALUES (#p0);
SELECT [ID], [Code]
FROM [Items]
WHERE ##ROWCOUNT = 1 AND [ID] = scope_identity();
',N'#p0 nvarchar(50)',#p0=N'Item1'
So EF inserts the item and reads back the generated default value in order to keep the tracked entity in sync with the database. Likewise, an item read from the database later has an empty string in Code.
These findings confirm that EF7 was a very immature version of EF-core (although I didn't confirm that it really displayed the described behavior). There have been more, and more profound, breaking changes since it. I hope we will soon forget about these early EF-core versions. Since version 2.0, EF-core is finally developing into a production-ready ORM.