JPA: ignore field on save, but fetch on select - java

I am using Hibernate as my JPA provider, and I want one of the fields in an entity to be ignored when calling save(). However, I do have a matching column in the corresponding database table and I want my entity field to be populated with the database value when I fetch the entity. So, I want the field to be ignored when saving the entity, but not when fetching it.
If I use #Transient, the field is completely ignored, which is not what I want. Is there any way to do this?

From the excellent book Pro JPA 2 :
JPA defines options to set individual mappings to be read-only using
the insertable and updatable elements of the #Column and #JoinColumn
annotations. These two settings default to true but can be set to
false if we want to ensure that the provider will not insert or update
information in the table in response to changes in the entity
instance. If the data in the mapped table already exists and we want
to ensure that it will not be modified at runtime, then the
insertable and updatable elements can be set to false, effectively
preventing the provider from doing anything other than reading the
entity from the database.
#Column(insertable = false, updatable = false)
private String readOnlyField;

Related

JPA equivalent of Hibernate's #Generated(GenerationTime.ALWAYS)

When certain non key fields of a entity are generated in the database (for instance, by triggers) a call to persist will not bring back values that the database has just generated. In practice this means that you may need to refresh an entity after persist or merge (and when level 2 cache is enabled you may even need to evict the entity).
Hibernate have a custom annotation #Generated which handles Generated Properties.
// Refresh property 1 on insert and update
#Generated(GenerationTime.ALWAYS)
#Column(insertable = false, updatable = false)
private String property1;
// Refresh property 2 on insert
#Generated(GenerationTime.INSERT)
#Column(insertable = false)
private String property2;
JPA #GeneratedValue only works with primary key properties.
So, my question is if there is a replacement for #Generated on JPA API (maybe on 2.1)? And if there isn't one, what is the best practice to handle non key database generated fields?
I read the specs from the beginning until the end and it is not such thing, nothing comparable with #Generated, sorry , and as you said.
The GeneratedValue annotation may be applied to a primary key property
or field of an entity or mapped superclass in conjunction with the Id
annotation.
What you could do is use Event Listener #PrePersist and #PreUpdate to set some properties by default or generated by utility classes before em persist the object , try that approach it comes to my mind to something similiar.

JPA Read but not write/update an attribute

I have a JPA Entity that has an attribute declared in the following way
#OneToOne(cascade = CascadeType.MERGE, fetch = FetchType.EAGER)
#JoinColumn(name = "userId", referencedColumnName = "id", insertable = false, updatable = false)
private UserBare user;
(UserBare is a dummy entity I've created which is a dumbed down version of a User obj as I needed some date, not all of User obj be made available to this entity. Is there a better way to approach this?
This is a convenience attr that I use just make the userBare object available to this entity when reading this entity. But when I actually write this entity, it seems to create new entries of 'user' in the database (instead of updating). I already have insertable=false,updatable=false but it still writes to the database. I tried removing the CascadeType declaration but that is throwing an error.
Here is the database snapshot after the unwanted rows are added (last 3). Also I've noticed that deleting the original entity did not delete these three unwanted rows. So I guess JPA is storing them but the references are intact to the original entity.
My souspicioun would be that "OneToOne" means "OneToOne" excluding the possibility of "OneToZero" if you don't set Optional to true like this:
#OneToOne(optional=true)
But just a guess.. I'm not sure

Stating table column properties with Hibernate

I am using Hibernate in my project. I have a database schema already created.
I see in the tutorials online
#Column(name = "STOCK_ID", unique = true, nullable = false)
public Integer getStockId() {
return this.stockId;
}
the columns properties like unique nullable etc are being used. My question is do I need to
specify these properties when I already have a db schema prepared prehand with the columns being given all the not null and all the checks while creating the tables?.
No you don't, only the required parameters in the Hibernate annotations have to be filled in. But the optional parameters frequently have default values. Your DB will have to be compatible with the default values, else you will have to fill in the values you use.
unique is only used when generating the database schema from the JPA annotations. nullable = false allows JPA to be more efficient: if the value is null when it writes the entity to the database, it doesn't even have to execute the insert/update query and can throw an exception immediately.
Even if that's not absolutely necessary, I like having these informations in the mapping for documentation purposes. Being able to know immediately from the code, without looking at the database schema, that an attribute is nullable or not, is useful.

What are reasonable scenarios to use GenerationTime.INSERT in the Hibernate #Generated annotation?

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;

Read-only association with JPA OneToMany mapping

I have a transactional entity associated to another entity whereby the associated should not be updated in an case.
Eg. Case *-> User
Where a Case is owned by a User and reversely a User can have many associated Case.
The association is mapped using OneToMany and JoinColumn JPA annotations.
I have also tried marking the Trasactional annotation for User entity as readonly and also made the fetch methods Transient. But this doesnot seem to stop update on User if its state is changed.
Please help me a figure a way to declare a "read-only" association to User.
You can add updatable=falseon #JoinColumn annotation.
Furthermore you should not add a setter method for user in your Case entity and same for caseSet in your User entity. The getter getCaseSet in User entity should also return an unmodifiable collection:
public Set<Case> getCaseSet() {
return Collections.unmodifiableSet(caseSet);
}
The Column annotation and XML element defines insertable and updatable options. These allow for this column, or foreign key field to be omitted from the SQL INSERT or UPDATE statement. These can be used if constraints on the table prevent insert or update operations. They can also be used if multiple attributes map to the same database column, such as with a foreign key field through a ManyToOne and Id or Basic mapping. Setting both insertable and updatable to false, effectively mark the attribute as read-only.
In #OneToMany mapping, #JoinColumn annotation, add both updatable=false and insertable=false, then specify the cascade type as PERSIST instead of ALL
#OneToMany(cascade = CascadeType.PERSIST)
#JoinColumn(name = "<ReadOnlyTableName>", updatable = false, insertable = false)

Categories