Create a custom JPA temporal annotation - java

I want some of mycoulmns in the JPA entity to take values from database during inserts/updates, i.e from Oracle sys_context, I believe JPA temporal annotation includes database generated timestamps during insert/updates, Is there any way I could create a custom annotation somewhat simailar to this or provide some default values during inserts/updates, can someone give few pointers if you faced this situation.

I want some of mycoulmns in the JPA entity to take values from database during inserts/updates
Configure the #Column to be not insertable and updatable and annotate the field with #Generated (Hibernate specific annotation). By doing so, Hibernate will fetch the value from the database after an insert, update. From the Reference Documentation:
2.4.3.5. Generated properties
Some properties are generated at
insert or update time by your
database. Hibernate can deal with such
properties and triggers a subsequent
select to read these properties.
#Entity
public class Antenna {
#Id public Integer id;
#Generated(GenerationTime.ALWAYS)
#Column(insertable = false, updatable = false)
public String longitude;
#Generated(GenerationTime.INSERT) #Column(insertable = false)
public String latitude;
}
Annotate your property as #Generated
You have to make sure your
insertability or updatability does not
conflict with the generation strategy
you have chosen. When
GenerationTime.INSERT is chosen, the
property must not contains insertable
columns, when GenerationTime.ALWAYS
is chosen, the property must not
contains insertable nor updatable
columns.
#Version properties cannot be
#Generated(INSERT) by design, it has
to be either NEVER or ALWAYS.

Related

How do we use Integer or Long type of objects as Foreign key mapping in JPA/Hibernate?

I tried below sample code which does not seem to work, while trying to fetch the object from table, hibernate tries to set the User Object on top of Long objects and fails when tried to load the Parent entity which has this createdBy field
any help?
#JoinColumn(name = "CREATED_BY", referencedColumnName = "USER_ID", updatable = false, nullable = false)
#OneToOne(targetEntity = User.class)
private Long createdBy;
I want to use Long/Integer as type of the field and want to make sure its a valid Foreign key to User table which has Primary key USER_ID
Please note that i DO NOT want to use User as type of my object, for example , i do not want below declaration in my class
private User createdBy;
Edit:
Reason for such requirement:
Ahh well!!, i'll try to keep it short which is basically one of the problem with ORM's like Hibernate. I have a Super Class AuditLogModel which is extended by each and every entity class in my software (100+ entities). This AuditLogModel class has CreatedBy & ModifiedBy field. If i keep the types of this fields as User, then every entity in my software tries to create a join with user table twice on operations like getResultsList/Merge/refresh, where Merge and Refresh calls on entity manger cannot be controlled by us, its all eager loading in one select query. Since my entities have child entities and they have further childs and so on, this creates more than 61 joins and sometime 100+ joins and causes query performance issues. These createdBy and modified by columns are updated with every insert/update but not required with any query when any entity is loaded, and hence I want to avoid the unneccessari joins here. Hope its understood
Ah OneToOne mapping is used for Entities like this:
#JoinColumn(name = "CREATED_BY", referencedColumnName = "USER_ID", updatable = false, nullable = false)
#OneToOne
private User createdBy;
If you want to use just the database value then you must remove the OneToOne mapping:
#Column(updatable = false, nullable = false)
private Long createdBy;

Is Hibernate #JoinColumn(nullable = false) redundant if not used for DDL generation?

Some of our entities declare OneToMany / OneToOne Relationships with nullable = false
#OneToMany
#JoinColumn(name = "FK_ID", referencedColumnName = "ID", nullable = false)
private List<Things> manyThings;
We use Flyway for DDLs. I have read in many sources that the JoinColumn property nullable is only used for DDL generation - so do I understand correctly that when our Flyway Scripts generate Tables with Constraints on the DB directly, the nullable = false property on our Entities is redundant? What exactly is meant by DDL-Generation and what does Hibernate offer in this regard?
I came across this problem because on some tables it turned out that the constraint on the DB Columns were Non-Null = true whereas on the Java Entities the property nullable = false was set. This didn't seem to have any effect on the application at runtime; it would happily insert null values. However, when running a SpringBootTest it would fail with a constraint violation.
It's not only for DDL generation, but also for Hibernate validation annotations.
When you say nullable = false, JPA generates database constraints (not-null) and ALSO generate a JSR 303 Bean Validation that don't let you persist the entity if the nullable = false column is null in Runtime. So, when you write nullable = false, JPA generates those two features for the price of one. #Column(nullable = false) is the JPA way of declaring a column to be not-null.
With nullable = false, the following save shall fail:
ObjectThatContainsListThings a = new ObjectThatContainsListThings();
a.setListOfThings(null)
hibernate.persist(a);
DataIntegrityViolationException: not-null property references a null or transient value
JPA will throw this exception instead of letting the database find the problem.

UUID as foriegn key in #ManyToOne hibernate

I got error:
org.hibernate.AnnotationException: #OneToOne or #ManyToOne on xxx.yyy.zz.myentity.xxx_id references an unknown entity: java.util.UUID
code
#ManyToOne
#JoinColumn(name = "xxx_id", nullable = false, updatable = false, referencedColumnName = "xxx_id", insertable
= false)
private UUID xxxId;
I got this when i change from AnotherEntity to UUID. I did this because i dont want to hold an object in my entity but only key to it(key type in AnotherEntity is UUID). I found that this error is cause when Object isnt annotated with
#Entity annotation. Is it true? How can i fix this?
You can represent an Entity as a SQL table and fields (properties) from this Entity as the columns from this table.
That's the easiest way to use Hibernate (or any other JPA implementation).
When you define a relationship (OneToOne, OneToMany, ManyToOne or ManyToMany) you are linking SQL tables so, with JPA, you are linking Entities.
As you can imagine, you can't define a relationship between a table and a column.
So:
your xxxId is supposed to be an object (Entity/Table) mapped in your database and Hibernate must know the column mapping.
that's the purpose of the #Entity annotation and all #Column, #JoinColumn you can use on an Entity class.
so yes, you can't use an object in a ManyToOne relationship that is not annotated with #Entity (it would be considerate by Hibernate like a column)
therefore, you cannot use an object from the JDK (because they are not annotated with any JPA annotation)
more specifically, java.util.UUID is not a class you can change. so you have no way to tell hibernate how it can map it to a Table in your DB.
so the only way I can imagine in your case is to use an entity you created to wrappe the UUID. For example:
#Entity
#Table(name="UUID")
public class UUIDWrapperEntity {
#Id
#Column(name = "UUID")
private UUID uuid;
}

How to define database generated column values as readonly fields in JPA and Hibernate?

With MariaDB 10.2 it's possible to define default value for Datetime e.g. created and lastModified.
How I should access this columns as readonly field? Because this values should only be under control of the database and should not be modified from code, but I want read access to this property in code.
It's simple. Just set the insertable and updatable attributes to false.
#Column(
name = "created_on",
insertable = false,
updatable = false
)
private Timestamp createdOn;
You can use:
#Column(updatable=false, insertable=false)
private YourType field;
Where the #Column is used to specify the mapped column for a persistent property or field. In particular it is javax.persistence.Column.

Spring, hibernate entity fields depending on profile

i have something like this:
#Entity
#Table(name = "schedules")
public class ScheduleDO {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false)
private Long id;
#Column(name = "begin_time", columnDefinition = "TIMESTAMP WITH TIME ZONE",
nullable = false)
private OffsetDateTime beginTime;
}
And i want to be able to change the "beginTime" variable anotation based on active profile. Something like this:
#Profile("dev")
#Column(name = "begin_time_dev", columnDefinition = "TIMESTAMP WITH TIME ZONE",
nullable = false)
#Profile("test")
#Column(name = "begin_time_test", columnDefinition = "TIMESTAMP WITH TIME ZONE",
nullable = false)
private OffsetDateTime beginTime;
is something like that possible?
Actually JPA/Hibernate know nothing about spring so profiles is out of their scope. Moreover entities are not beans so spring don't use them
The only solution I can see is to define a placeholder {profile_begin_time_test} and add an interceptor (see the example).
In the method
public String onPrepareStatement(String sql)
In the sql generated by hibernate replace the {profile_begin_time_test} placeholder with desired real column name. The placeholder replacement can be configured to use value based on spring profiles.
You could do this by creating a custom Hibernate UserType and registering it on your beginTime field via the Hibernate #Type annotation.
In your new UserType, you could then get the current profile from the Spring Environment to determine the target column name. You'll have to statically register the Environment in your application somehow first, as your UserType will have been instantiated by Hibernate and won't know about your Spring application context. All doable though!
I think that you could possibly :
1)Create two different fields and different setters.
2)Add all JPA annotations on method level rather than on fields.
3)Since spring 4.1 you can profile methods, so use profiles on setters.
The #Profile annotation may be used in any of the following ways: as a type-level annotation on any class directly or indirectly annotated with #Component, including #Configuration classes. As a meta-annotation, for the purpose of composing custom stereotype annotations. As a method-level annotation on any #Bean method
PROFILE DOCS

Categories

Resources