I am reviewing the code of a colleague. In his entity object he has set nullable = false and he is also checking in the setter that the value to set is not null.
Is this useful? In any case, the nullable = false will throw an exception at some point.
(The checkArgumentNotNull will throw an illegal argument exception if the value is null.)
private TypeChampMaterielDefaillant typeChamp;
#Column(name = "TYPE_CHAMP", nullable = false, length = 30)
#Enumerated(EnumType.STRING)
public TypeChampMaterielDefaillant getTypeChamp() {
return typeChamp;
}
public void setTypeChamp(TypeChampMaterielDefaillant typeChamp) {
checkArgumentNotNull(typeChamp, "typeChamp");
this.typeChamp = typeChamp;
}
EDIT
So if I understand correctly nullable=false only apply to schema generation, thus if the database is not generated with the current entity it will be possible to persist a null value
Parameter nullable = false will happen on database operation (you won't be able so persist entity with this value equals to null). Additional check in setter is useful because you'll get exception earlier (during setter invocation) but not necessary.
I would say nullable = false is used for schema generation, not for validation from jpa(unless some third party library uses for validating before persist)
JPA 2.1 Specification:
11.2.2.1 Column
The following elements of the Column annotation are used in schema generation:
name
unique
nullable
columnDefinition
table
length (string-valued columns only)
precision (exact numeric (decimal/numeric) columns only)
scale (exact numeric (decimal/numeric) columns only)
See section 11.1.9 for the rules that apply to these elements and column creation. The AttributeOverride annotation may be used to override column
mappings.
As you can see jpa spec talks nothing about validation, unless hibernate/or some third party does. I am not positive either about hibernate validator for that nullable = false statement.
You better use some validator framework or use #PrePersist, #PreUpdate annotations.
Setter validation also bad, how about if user does not call it at all ?
No, this is very bad practice. A Hibernate-Bean is a bean, any kind of intelligent setter is an workaround anyway.
Use the annotations instead like #NotNull!
Use asserations if you realy need something that is thrown in test/devel!
Related
I have property in Enum:
#Basic
#Column(name = "payment_status", columnDefinition = "varchar(32) default 'ENTERED'", nullable = false)
#Enumerated(EnumType.STRING)
private PaymentStatus paymentStatus;
I want to get the default value for a field from enum
I have error:
org.hibernate.PropertyValueException: not-null property references a null or transient value
The field cannot be null
The error is when I want to create an object and save in the database without entering this field (PaymentStatus)
EDIT:
#Basic
#ColumnDefault(value = "ENTERED")
#Column(name = "payment_status", nullable = false)
#Enumerated(EnumType.STRING)
private PaymentStatus paymentStatus = PaymentStatus.ENTERED;
Why is it not working?
default 'ENTERED' tells the database to use value 'ENTERED' if the column is not included in the INSERT statement. Since the column is in the class, JPA will always include it in the INSERT statement.
To make JPA fill in the default value, simply assign it with an initializer, so it has that value until replaced by you (calling setter method), or replaced from database (when reading from there).
private PaymentStatus paymentStatus = PaymentStatus.ENTERED;
If you have defined a default in the database and if you want to leave it to the database to set the default value then you need to prevent a null being inserted to that column in the SQL insert statement
You would then need to look at what your JPA provider offers in that area. In Hibernate, for example, you would simply annotate your Entity with #DynamicInsert:
https://docs.jboss.org/hibernate/orm/5.2/javadocs/org/hibernate/annotations/DynamicInsert.html
For inserting, should this entity use dynamic sql generation where
only non-null columns get referenced in the prepared sql statement?
It should be noted that defining a default in the JPA annotations (as you have done) only has any effect on DDL generation i.e. if you are having your JPA provider generate or update your schema and has to effect on any insert or update operations.
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.
I don't understand at all the difference between these annotations.
Could you please explain me if this is redundant?
#NotNull
#Basic(optional = false)
#Column(nullable = false)
private int userId;
Thanks in advance.
#NotNull : This annotation you are defining that your code will not except a null parameter, if you were to provide a null parameter, the annotation would throw a RuntimeException.
#Basic : This annotation signifies that an attribute is to be persisted and a standard mapping is to be used. It has parameters which allow you to specify whether the attribute is to be lazily loaded and whether it's nullable.
#Column allows you to specify the name of the column in the database to which the attribute is to be persisted.
Here are the difference that I noticed.
#NotNull comes from javax.validation JSR303 it define if the property of the bean can be null, set this indicate that property can not be null, this are called constraints and are verified in validation.validate() method.
#Basic(optional = false) comes from javax.persistence JSR317, there are Basic types and collection Types it indicate the logical model can be null or not set optional to false indicate that the propery can not be null at jpa logical model. Also allows you to declare the fetching strategy for a property
#Column(nullable = false) comes from javax.persistence JSR317, it is related to physical mapping with the database (DDL) telling to the database that the property can not be null in the database. (NOT NULL statement in table creation)
As you can see first one works with Validation API, second and third apply to JPA API but one in the logical and the other in the physical mode.
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.
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;