I have an entity that has a field annotated with #CreationTimestamp:
#Entity
#Table(name = "foo")
class Foo {
...
#Column
#CreationTimestamp
private Instant createdAt;
}
And now I have integration tests where I need to create a few entries with different createdAt timestamps timestamp. But when I set the value to one that I need and save it's getting overridden with the creating timestamp of the VM:
Foo foo = new Foo();
foo.setCreatedAt(Instant.MIN);
foo = fooRepo.save(foo);
foo.getCreatedAt(); // equals to the creation time
How can I insert the desired values for this field in my test env?
I don't think it is possible to disable this behavior in an integration test as you need here.
However you can achieve the same using a workaround by setting the creation date after saving the entity, then save the entity once more to persist the new desired creation time.
This will work since #CreationTimestamp will only be triggered when saving the entity for the first time.
Instead of using #CreationTimestamp annotation, you could remove it from the field and manage that date with a #PrePersist:
#PrePersist
void onCreate() {
this.setCreatedAt(Instant.now());
}
Of course, you can check whatever you want in there. You could still have an updatable=false on the field.
Related
I have an entity which belongs to a customer entity.
I would like the entity including all associations to be kind of read-only.
public class Foo{
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name = "customer_id")
private Customer customer;
#Basic
#Column(name = "firstname")
private String firstName;
// getters for both fields ....
}
Here is what I want:
Calls to setters of foo should not be persisted.
Calls to myFoo.getCustomer() should return a readonly customer so that calls to setters like myFoo.getCustomer().setSomething("should not be persisted") should not work.
Example:
List<Foo> list = fooDAO.getList();
for (Foo foo : liust) {
String f1 = foo.getCustomer().getSomeField(); // should work
foo.getCustomer.setSomeField("change that should not be persisted"); // calling this setter should not have an effect or should even throw an UnsupportedOperationException() or something
foo.setFirstName("change should not be persisted"); // also this should not be persisted.
}
Currently my solution for the association is kind of manual:
public Customers getCustomer() {
// detach the referenced object from the Hibernate session
// to avoid that changes to these association are persisted to the database
getCustomersDAO().evict(customer); // calls session.evict(o) under the hood
return customer;
}
Here is my question:
What ways are there to avoid changes to associations being persisted to the Database? E.g. using an annotation?
In general I would like this behaviour to be the default.
But it should also be possible to allow changes to be persisted. So I need it configurable. So I though about doing it on the Query-level.
My environment:
Hibernate v3.6
Spring 3.2 with HibernateDAOSupport /
HibernateTemplate and annotation based Transaction-handling.
I ran into this issue. I was storing filename as a property of an object in the database, and then trying to manipulate it in the application after reading it to reflect storage path based on environment. I tried making the DAO transaction readOnly, and hibernate still persisted changes of the property to the db. I then used entityManager.detach(object) before I manipulated the property, and that worked. But, I then later got tangled up into issues where other service methods that were dependent the auto persistence of objects to the db weren't behaving as they should.
The lesson I learned is that if you need to manipulate a property of a object that you don't want persisted, then make a "transient" property for that object with it's own getters and setters, and make sure the transient property setter isn't passed properties from the entity model that then become manipulated. For example, if you have a property "filename" of String, and you have a transient property of "s3key", the first thing the "s3Key" property setter should do is make a new String object passed into the setter for it to use in the manipulation.
#Transient
private String s3Key;
public String getS3Key {
String s3key = s3Prefix + "/" + <other path info> + this.filename;
return s3key;
}
Though all the "readOnly" and "detach" stuff for jpa/hibernate allows for custom handling of persistence of objects to the db, it just seems going to that length to customize the behavior of the objects to that extent causes supportability problems later, when persisting entity changes automatically is expected or counted on in other service functions. My own experience has been it is best to use a programming pattern of making use of transient properties for handling changes to entities you don't want persisted.
I use the date validaton using the #Future annotation.
#NotNull
#DateTimeFormat(pattern="yyyy-MM-dd")
#Column(name = "FROM")
#Temporal(TemporalType.DATE)
#Future
private Date from;
#NotNull
#Column(name = "FOO")
private String foo;
I perform CRUD operations using Rest API. The requirement is the from date will be in future - after the entity is being created (today). However, the time changes and in case of changing the field foo using ex. PUT method, the validation won't pass.
#PutMapping(value = "/{id}")
public ResponseEntity<?> put(
#Valid #RequestBody MyEntity myEntity,
#PathVariable("id") int id)
{
... update entity based on id
}
When I call this method in the far future (after the from value persisted), the validation doesn't let me perform the operation, because the field from is no more valid.
There is a simple in-built solution to trigger a certain validation only on create event?
I have been thinking over creating the own cross-field validation through annotation, however I am not able to determine the creation based on other fields.
You can use Grouping Constraints, to restrict which validation set to use for: pre-persist, pre-update, pre-remove and ddl(For database schema).
So to validate from field just for persist operation and ignore it for put(update), you may:
Add an interface e.g. GroupFuture:
package com.example.entity;
public interface GroupFuture {}
In your MyEntity, I think you should also add #NotNull constraint as #Future consider null as valid value:
//...
//Maybe #NotNull
#Future(groups = GroupFuture.class)
private Date from;
//...
Finally, if you've configured hibernate using:
persistence.xml, add this line in the persistence-unit setting:
<property name="javax.persistence.validation.group.pre-persist" value="javax.validation.groups.Default, com.example.GroupFuture">
Programmatically:
// If you're using pure hibernate
Configuration configuration = new Configuration().setProperty("javax.persistence.validation.group.pre-persist", javax.validation.groups.Default, com.example.GroupFuture);
`
// If you're using JPA/hibernate
entityManagerFactory.getJpaPropertyMap().put("javax.persistence.validation.group.pre-persist", javax.validation.groups.Default, com.example.GroupFuture);
Useful reading(even it's for hibernate 3.6): Chapter 23. Additional modules
I have a scenario where I have 2 labels that need to be configured. The names of the labels are 'Out Date' and 'In Date'. I only have one field in the database called 'Date'. Whether it is 'Out' or 'In' is decided at the runtime by the value of an Enum 'Scenario'. However, I need to actually show the user Out Date&In Date so that he can select 1 or both of them. I heard that calculated field concept it JPA will assist in this. Is this true or is there some other way that I can achieve this. Below is some sample code.
Date
#Override
#Convert("DateTimeConverter")
#Column(name = "DATE")
public DateTime getDate() {
return date;
}
Scenario
#Override
#Convert("EnumConverter")
#Column(name = "SCENARIO")
public Scenario getScenario() {
return scenario;
}
Scenario is any enum with the values OUT(1),IN(2)
There are no calculated properties in JPA.
You can use #Transient annotation to create properties that are not persisted but calculated based on other fields:
#Transient
public DateTime getInDate() {
if (scenario == Scenario.IN) {
return date;
}
return null;
}
#Transient
public DateTime getOutDate() {
if (scenario == Scenario.OUT) {
return date;
}
return null;
}
Alternatively, if you are using Hibernate you can use proprietary annotation #Formula:
#Formula("case when SCENARIO = 2 then DATE else NULL end")
#Convert("DateTimeConverter")
private DateTime inDate;
#Formula("case when SCENARIO = 1 then DATE else NULL end")
#Convert("DateTimeConverter")
private DateTime outDate;
I prefer the first option because:
it is easier to test with unit tests
it is easier to use the entity in unit tests
it does not require proprietary extensions
generally there might be some problems with portability of SQL, although in this problem case when is SQL 92 compatible so it does not apply here
The only problem I can is is that in simplest approach is that we abandon encapsulation by exposing to clients internals of the entity (scenario and date properties). But you can always hide these properties with accessor protected, JPA will still handle that.
To compute properties within JPA entities, you can use JPA callbacks.
See this Hibernate JPA Callbacks documentation. (Note: JPA callbacks are not specific to hibernate, it's part of latest JPA 2.1 specification).
And also this OpenJpa JPA Calbacks one.
Following entity life-cycle categories have a Pre and Post event which can be intercepted by the entity manager to invoke methods:
Persist -> #PrePersist, #PostPersist
Remove -> #PreRemove, #PostRemove
Update -> #PreUpdate, #PostUpdate
Load -> #PostLoad (No Pre for this ...)
So let's say you want to compute a complexLabel label from two persisted entity fields label1 and label2 in an entity titled MyEntity:
#Entity
public class MyEntity {
private String label1;
private String label2;
#Transient
private String complexLabel;
#PostLoad
#PostUpdate // See EDIT
// ...
public void computeComplexLabel(){
complexLabel = label1 + "::" + label2;
}
}
As #Dawid wrote, you have to annotate complexLabel with #Transient in order to make them ignored by persistence. If you don't do this, persistence fails because there is no such column in MyEntity corresponding table.
With #PostLoad annotation, computeComplexLabel() method is called by entity manager just after the loading of any instance of MyEntity from persistence.
Thus, #PostLoad annotated method is best suited to put your post loading entity properties enhancement code.
Bellow is an extract from JPA 2.1 specification about PostLoad:
The PostLoad method for an entity is invoked after the entity has been
loaded into the current persistence context from the database or
after the refresh operation has been applied to it. The PostLoad
method is invoked before a query result is returned or accessed or
before an association is traversed.
EDIT
As pointed out by #Dawid, you could also use #PostUpdate in case you want to compute this transient field just after the entity update, and use other callbacks when needed.
I wonder about the behavior of Hibernate regarding fields annotated with #Generated(value=GenerationTime.INSERT) when I do an update on the entity.
I have an entity with the following fields (which are populated by database triggers, the first one on insert, the second one on insert and for each update):
#Generated(value = GenerationTime.INSERT)
#Column(name="created_by", insertable = false, updatable = false)
private String createdBy;
#Generated(value = GenerationTime.ALWAYS)
#Column(name="updated_by", insertable = false, updatable = false)
private String updatedBy;
When I load an entity from the database, both fields are populated - as expected
When I receive the entity back from the web-ui, both fields will be null - as expected since they weren't bound to any form field.
After calling
sessionFactory.getCurrentSession().saveOrUpdate(object);
the createdBy field will be null but the updatedBy field will be set to the correct value (created by the database).
So the entity no longer contains the values from the database - an undesired behavior.
For my situation can solve the issue
- I could change the GenerationTime to ALWAYS - but this seems to be confusing since createdBy is really only set when the entity is inserted.
- I could do a refresh on the entity - but I would like to avoid the extra call.
What is the best practice for the described situation? Are there better options that avoid an explicit call of refresh and don't obscure the semantic of fields modified only on insert?
What are reasonable scenarios to use #Generated(value=GenerationTime.INSERT)?
can you try like this.
#Temporal(TemporalType.TIMESTAMP)
#Generated(GenerationTime.ALWAYS)
#Column(name="create_date", insertable=false,updatable=false)
private Calendar createDate;
In MySQL it is possible to make a TIMESTAMP row being updated on every update operation. Is there a way to realize this for a column with Hibernate and map it to a POJO property?
So that I have something like this:
#Column
private Date updated;
If you want to do it at the database side, you can specify a custom column definition (if schema is generated by Hibernate, otherwise you need to declare it in your schema as desired), and instruct Hibernate that this property is generated at the database side:
#Column(columnDefinition = "TIMESTAMP ON UPDATE CURRENT TIMESTAMP")
#Generated(GenerationTime.ALWAYS)
private Date updated;
Alternatively, you can do it at the application side, as suggested by Jigar Joshi.
You can have this to accomplish this thing
#PreUpdate
protected void onUpdate() {
updated = new Date();
}