This question already has answers here:
How to set a default entity property value with Hibernate
(18 answers)
Closed 26 days ago.
I am using hbm2java, which is part of Hibernate Tools, to reverse engineer a database into JPA entity classes.
I run the tool via ./mvnw clean generate-sources and the entity classes are generated and saved to target/generated-sources.
In the UserAccount database table, the Created column is defined like this. Note the default value:
Created TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
When hbm2java reverse engineers that column, the default value is not included:
...
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "CREATED", nullable = false, length = 29)
public Timestamp getCreated() {
return this.created;
}
public void setCreated(Timestamp created) {
this.created = created;
}
...
As a result, a DataIntegrityViolationException is thrown when trying to save a UserAccount entity to the database:
org.springframework.dao.DataIntegrityViolationException: not-null
property references a null or transient value :
com.example.UserAccount.created
Really hoping there is a way around this as I have quite a few database columns with default values, the most complex being:
DEFAULT 'User' + CAST(NEXT VALUE FOR SeqUserAccountUsername as VARCHAR(19))
...that just generates a string such as User13.
I'm still learning Spring Boot and Hibernate and could use some advice on the best approach to solving this problem.
My current research:
The same question was asked back in 2007 in the Hibernate Forums but a solution was not provided.
This documentation talks about using the "default-value" attribute to set the "Default initialization value for a field". Is that the correct approach?
I believe that the following mapping should work in all recent-ish versions of Hibernate:
#Generated(INSERT)
#ColumnDefault("CURRENT_TIMESTAMP")
#Column(nullable = false)
public Timestamp getCreated() {
return this.created;
}
If that doesn't work, let me know.
(It's certainly true that the reverse engineering tool doesn't know anything about default values.)
At first, you need to find someone who will able to clarify how that database works. The problem is SQL column definition like TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL means following:
you never select null
you may not insert/set null
you may insert/set not null
you may either omit column in DML statement or specify DEFAULT, in that case DB generates value according to default expression (CURRENT_TIMESTAMP in your case)
The simplest option to get exactly the same functionality/capabilities in Hibernate, is to use JPA Callbacks, smth. like:
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "CREATED", nullable = false, /* updatable = false, */ length = 29)
public Timestamp getCreated() {
return this.created;
}
#PrePersist
protected void onPrePersist() {
if (this.created == null) {
this.created = Timestamp.from(Instant.now());
}
}
that allows you to specify arbitrary created timestamp, and only if it is null Hibernate will use current time - that is exactly what DB allows you to do.
Other options do something similar, but not the same, however some of them may suit you.
#CreationTimestamp:
#CreationTimestamp
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "CREATED", nullable = false, length = 29)
public Timestamp getCreated() {
return this.created;
}
it ignores value of this.created and inserts current time, calculated on Java side, into DB.
#Generated(GenerationTime.INSERT)
#Generated(GenerationTime.INSERT)
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "CREATED", nullable = false, length = 29)
public Timestamp getCreated() {
return this.created;
}
it ignores value of this.created and delegates generation of value to DB, basically it omits corresponding column in INSERT statement (well... "generate" is not correct definition here).
if you choose to use either #CreationTimestamp or #Generated(GenerationTime.INSERT) remove corresponding setter void setCreated(Timestamp created) in order to avoid any confusion about mutability of created field.
Related
I have problem with Hibernate Envers.
I have classes like:
#Entity
#Table(name = "REVINFO")
#RevisionEntity(MyRevisionEntityListener.class)
public class RevEntity {
#Id
#RevisionNumber
#Column(name = "REV", nullable = false, updatable = false)
private Integer id;
#RevisionTimestamp
#Column(name = "REVTSTMP", nullable = false, updatable = false)
private Date timestamp;
#Column(name = "MODIFIED_BY", length = 100)
private String modifiedBy;
#Column(name = "COMMENT", length = 100)
private String comment;
public class MyRevisionEntityListener implements RevisionListener {
#Override
public void newRevision(Object revisionEntity) {
RevEntity a = (RevEntity) revisionEntity;
a.setComment("Some value");
}
}
How can i select every change for entity ID and their "REVINFO" object?
I've got something like this:
List resultList = AuditReaderFactory.get(entityManager)
.createQuery()
.forRevisionsOfEntityWithChanges(ClientType.class, true)
.add(AuditEntity.id().eq(entityId))
.getResultList();
And it's almost work good. I received every "change" but REVINFO looks strange. All fields are null - and there are 1 more object $$_hibernate_interceptor which actually hold "information" but i cannot acces it via code (or i dont know how). See example at the image.
So my question is:
1 - How can i get REVINFO values ?
2 - Do i realy have to use entityManager, or can it be achived with different approach ?
Edit 2:
Correct me if i am wrong, but does forRevisionsOfEntityWithChanges works as Lazy Initialization? I mean, if i try to receive for example modifiedBy field i actually get my data. Debugger log make me confused.
The call to forRevisionsOfEntityWithChanges returns an object array that contains:
Entity instance
Revision Entity
Revision Type
Property names that were changed.
How can i get REVINFO values ? 2 - Do i realy have to use entityManager, or can it be achived with different approach ?
So in your code, to get the revision info attributes, you would do the following. Note that in this code, the type of the revision-info object will depend on your configuration or if you're using a custom revision-info entity class in your deployment. Just be sure to cast it to the proper type.
for (Object entry : resultList) {
final Object[] row = (Object[]) entry;
final TheRevisionEntityClassType revisionInfo = row[1];
// now you can get the revision entity attributes from revisionInfo using getters
}
Correct me if i am wrong, but does forRevisionsOfEntityWithChanges works as Lazy Initialization? I mean, if i try to receive for example modifiedBy field i actually get my data. Debugger log make me confused.
Depending on the query, yes Hibernate may use proxies and its important to understand that in this case, the visual representation you get in the debugger may or may not be accurate depending if the object's internal state gets initialized by the debugger window or not.
I have a Table in MySQL which has it's column definitions as below:
CREATE TABLE APPOINTMENT(
CD_APPOINTMENT BIGINT NOT NULL,
-- omitted for brevity
APPOINT_DATE DATE NOT NULL
);
My JPA entity is defined as:
#Entity
#Table(name = "APPOINTMENT")
public class Appointment {
protected Long id;
protected Date date = new Date();
// other atributes omitted for brevity
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "CD_APPOINTMENT")
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
#Temporal(TemporalType.DATE)
#Column(name = "APPOINT_DATE", columnDefinition = "DATE")
public Date getDate() {
return date;
}
}
As I'm using Spring, I have benefits of Spring Data JPA. Following that line, I'm using Spring Data JPA Repositories.
I'm testing in 2019-07-12 (at my timezone [UTC-3]).
When I run:
appointmentRepository.save(appointment);
the Appointment is successfully (more or less) saved.
Fine! The column APPOINT_DATE has the value of 2019-07-12, yes? Well, it's seems ok.
When I run:
SELECT * FROM APPOINTMENT;
the retrieved rows looks as expected:
CD_APPOINTMENT|APPOINT_DATE
--------------|------------
1| 2019-07-12
The strange part appears when I try to filter BETWEEN dates.
If I run my JPQL:
SELECT ap FROM Appointment AS ap WHERE ap.date BETWEEN :startDate AND :endDate
startDate and endDate are parameters received in a #Param annotation in Spring and both of them have the value of 2019-07-12
I get 0 rows, but I was expecting to get one (the above inserted Appointment). Firstly, I thought it was a problem with the JPQL, but it's not. If I execute the same JPQL with a different RDBMS (like H2, for an example), the query works perfectly.
And if I run the same JPQL but in SQL, directly on the MySQL database:
SELECT * FROM APPOINTMENT where APPOINT_DATE BETWEEN '2019-07-12' AND '2019-07-12'
just like the JPQL it returns 0 rows.
If I run the now(); command at MySQL database, it return the CORRECT date time.
How can I fix it?
Has anybody seen something like that already? Because I have not.
BETWEEN '2019-07-12' AND '2019-07-13'
It is best not to use between for date/times. One reason is because there might be a time component that throws off the comparison.
I would suggest:
SELECT *
FROM APPOINTMENT
WHERE APPOINT_DATE >= '2019-07-12' AND
APPOINT_DATE < '2019-07-13'
This logic works with an without a time component. And it can take advantage of an index on the date column.
My MySQL instance is from Amazon RDS.
Their default Time Zone is UTC. Switched from UTC to Brazil/East and now it's working as expected.
I use Spring Boot and Spring Data for my project. In one of the tables I have a column called "itemStatus". Now I want to add another column with a timeStamp something like "itemStatus_LastModifiedDate" to keep the date of the latest update to the itemStatus column.
Is there a way to do this cleanly with the help of some annotations like:
#LastModifiedDate
#UpdateTimestamp
private Date itemStatusLastModifiedDate
Because as far I know these above only work when there is an update to the entity and not to a specific column.
1 You can not use annotation #UpdateTimestamp to track the update of a single field.
2 You can do a custom update using #SqlUpdate annotation with a custom update query.
Something like this (I didn't check this code)
#SqlUpdate(update item set item_status = :itemStatus, itemStatusModDate = case when (item_status = :itemStatus) then sysdate() else item_status_mod_date where id = :id)
public class Item {
#GeneratedValue
private Long id;
private String itemStatus;
private Date itemStatusModDate;
..................................................
}
Is there anyway to insert a new record into a PostgreSQL database with Jooq straight from a POJO which extends a general identity class that has an id field without including the id in the insert statement?
An example POJO:
#Data
public abstract class PersistenceIdentity {
#Id
#Column(name = "id", unique = true, nullable = false, precision = 7, insertable = false)
private Integer id;
#Column(name = "created_date")
private LocalDateTime createdDate;
public abstract Table<R> getJooqTable();
}
#Data
public class SocialNetwork extends PersistenceIdentity {
#Column(name = "name")
private String name;
#Override
public Table<SocialNetworkRecord> getJooqTable() {
return Tables.SOCIAL_NETWORK;
}
}
The PostgreSQL schema is:
CREATE TABLE "social_network" (
id SERIAL NOT NULL PRIMARY KEY,
created_date TIMESTAMP DEFAULT now(),
name TEXT NOT NULL
);
My code to persist the POJO:
public <T extends PersistenceIdentity> T insertRecord(T record) {
Record newRecord = db.newRecord(record.getJooqTable(), record);
if (newRecord instanceof UpdatableRecord) {
((UpdatableRecord) newRecord).store();
}
return newRecord.into(record);
}
I realize I'm probably doing what Jooq really wasn't meant for (i.e. using generic types), however that (appears) to work just fine.
The problem is, Jooq includes the id in the insert statement and I then, of course, get a null value constraint. I don't want it inserted when it's a new record, however I do want it included when it returns the record (after inserting), when updating and also in select statements.
I can't simply exclude the id because I need it later on to easily get around some of the #OneToMany / #ManyToOne limitations.
And I would rather not have to insert the specific values for each POJO (that's why we annotated with #Column).
Does Jooq not honor the #Id or the insertable = false parameter in #Column?
Can anyone shed some light on this?
EDIT 1
Per request, below is the relevant snippet from the jOOQ generated table object. I'm not sure if this is correct or not for what I'm trying to do (i.e. allow the database to generate the ID), but I would think nextval('social_network_id_seq'::regclass) would accomplish that.
#Generated(
value = {
"http://www.jooq.org",
"jOOQ version:3.9.1"
},
comments = "This class is generated by jOOQ"
)
#SuppressWarnings({ "all", "unchecked", "rawtypes" })
public class SocialNetwork extends TableImpl<SocialNetworkRecord> {
/**
* The column <code>public.social_network.id</code>.
*/
public final TableField<SocialNetworkRecord, Integer> ID = createField("id", org.jooq.impl.SQLDataType.INTEGER.defaultValue(org.jooq.impl.DSL.field("nextval('social_network_id_seq'::regclass)", org.jooq.impl.SQLDataType.INTEGER)), this, "");
}
Also, we use the mvn jooq-codegen:generate -Djooq.generator.name=org.jooq.util.XMLGenerator to generate the XML schema and then generate the the jOOQ table objects from that XML config. The thinking is we can push the XML config to github and all builds can simply regenerate the table objects from that.
Here is the XML:
<column>
<table_catalog></table_catalog>
<table_schema>public</table_schema>
<table_name>social_network</table_name>
<column_name>id</column_name>
<data_type>integer</data_type>
<character_maximum_length>0</character_maximum_length>
<numeric_precision>32</numeric_precision>
<numeric_scale>0</numeric_scale>
<ordinal_position>1</ordinal_position>
<column_default>nextval('social_network_id_seq'::regclass)</column_default>
</column>
<table_constraint>
<constraint_catalog></constraint_catalog>
<constraint_schema>public</constraint_schema>
<constraint_name>social_network_pkey</constraint_name>
<constraint_type>PRIMARY KEY</constraint_type>
<table_catalog></table_catalog>
<table_schema>public</table_schema>
<table_name>social_network</table_name>
</table_constraint>
<table_constraint>
<constraint_catalog></constraint_catalog>
<constraint_schema>public</constraint_schema>
<constraint_name>2200_17431_1_not_null</constraint_name>
<constraint_type>CHECK</constraint_type>
<table_catalog></table_catalog>
<table_schema>public</table_schema>
<table_name>social_network</table_name>
</table_constraint>
<table_constraint>
<constraint_catalog></constraint_catalog>
<constraint_schema>public</constraint_schema>
<constraint_name>2200_17431_3_not_null</constraint_name>
<constraint_type>CHECK</constraint_type>
<table_catalog></table_catalog>
<table_schema>public</table_schema>
<table_name>social_network</table_name>
</table_constraint>
<key_column_usage>
<column_name>id</column_name>
<constraint_catalog></constraint_catalog>
<constraint_schema>public</constraint_schema>
<constraint_name>social_network_pkey</constraint_name>
<ordinal_position>0</ordinal_position>
<table_catalog></table_catalog>
<table_schema>public</table_schema>
<table_name>social_network</table_name>
</key_column_usage>
EDIT 2
My SocialNetwork jOOQ-generated table object does not have a getIdentity() method, however it does have a getPrimaryKey() method and if it helps, my SocialNetworkRecord class has two Constructors:
public SocialNetworkRecord() {
super(SocialNetwork.SOCIAL_NETWORK);
}
/**
* Create a detached, initialised SocialNetworkRecord
*/
public SocialNetworkRecord(Integer id, Timestamp createdDate, String name) {
super(SocialNetwork.SOCIAL_NETWORK);
set(0, id);
set(1, createdDate);
set(2, name);
}
The way jOOQ works, there are two elements worth explaining:
Step 1: Record.from(Object):
Record newRecord = db.newRecord(record.getJooqTable(), record);
This call is convenience for this:
Record newRecord = db.newRecord(record.getJooqTable());
newRecord.from(record);
And the Record.from(Object) will copy all values from the record to the newRecord by using Record.set(Field, Object), which again sets the record's internal Record.changed(Field) flag.
Step 2: UpdatableRecord.store()
Your call to:
((UpdatableRecord) newRecord).store();
Will take all changed() fields into consideration for the relevant INSERT or UPDATE statement that is executed. The rationale here is that people sometimes want to set the primary key value explicitly, and not let an identity generate the value for them. Even if an identity is present on the primary key, it may sometimes be desireable to override its value. SQL standard databases (e.g. Oracle 12c) thus support two ways of specifying an identity:
-- This can be overridden
GENERATED BY DEFAULT AS IDENTITY
-- This can never be overridden
GENERATED ALWAYS AS IDENTITY
(MySQL's AUTO_INCREMENT or PostgreSQL's SERIAL type work the same way)
jOOQ assumes GENERATED BY DEFAULT AS IDENTITY here. The only exception to the above behaviour is when the identity column is NOT NULL and the Record value for the identity is null and jOOQ's meta model is aware of both:
- `NOT NULL` constraint
- `GENERATED BY DEFAULT AS IDENTITY`
Then, jOOQ will omit considering the identity value for insertion / update.
Bug in 3.9.2 and less:
Note that up until jOOQ version 3.9.2, there was a bug / missing feature in the XMLGenerator that produces the XML file you're importing: https://github.com/jOOQ/jOOQ/issues/6141. This bug resulted in no identity information being generated.
Workaround 1: If you cannot influence the jOOQ meta model
If, for some reason, you cannot get the jOOQ meta model to reflect your NOT NULL constraint and your DEFAULT clause, you could work around this limitation by resetting the value of the identity right after your Record.from(Object) call using Record.reset(Field):
Record newRecord = db.newRecord(record.getJooqTable(), record);
newRecord.reset(identityColumn);
((UpdatableRecord) newRecord).store();
Workaround 2: Generate a synthetic identity
The code generator has a feature to generate synthetic identities. For instance, if all your identity columns are called ID, you could write this:
<!-- fully qualified -->
<syntheticIdentities>.*?\.ID</syntheticIdentities>
Or this:
<!-- unqualified -->
<syntheticIdentities>ID</syntheticIdentities>
I'm creating a table using Hibernate for MySQL database in Java.
One of the columns is of Date type.
#Temporal( TemporalType.TIMESTAMP )
#Column( name = "event_start_time", nullable = false, length = 19 )
public Date getEventStartTime()
{
return eventStartTime;
}
I know that it's possible to specify the precision of TIMESAMP in MySQL using
TIMESTAMP(6)
However, how do you do that in Hibernate annotation mapping? I've tried
length = 19, precision = 38, scale = 20
none of which seem to store the time in milliseconds past the second aka
1374839856000
as opposed to
1374839855789
Anyone know a solution to this? Thanks for the help!
Subclass the class you configured in property hibernate.dialect and then change the used data type there. Set the property hibernate.dialect to your new class.
This is what worked for me (since MySQL 5.6.4):
public class MySQL564InnoDBDialect extends MySQL5InnoDBDialect {
protected void registerColumnType(int code, String name) {
if (code == Types.TIMESTAMP) {
super.registerColumnType(code, "TIMESTAMP(6)");
} else {
super.registerColumnType(code, name);
}
}
}