Confusion: #NotNull vs. #Column(nullable = false) with JPA and Hibernate - java

When they appear on a field/getter of an #Entity, what is the difference between them? (I persist the Entity through Hibernate).
What framework and/or specification each one of them belongs to?
#NotNull is located within javax.validation.constraints. In the javax.validation.constraints.NotNull javadoc it says
The annotated element must not be null
but it does not speak of the element's representation in the database, so why would I add the constraint nullable=false to the column?

#NotNull is a JSR 303 Bean Validation annotation. It has nothing to do with database constraints itself. As Hibernate is the reference implementation of JSR 303, however, it intelligently picks up on these constraints and translates them into database constraints for you, so you get two for the price of one. #Column(nullable = false) is the JPA way of declaring a column to be not-null. I.e. the former is intended for validation and the latter for indicating database schema details. You're just getting some extra (and welcome!) help from Hibernate on the validation annotations.

The most recent versions of hibernate JPA provider applies the bean validation constraints (JSR 303) like #NotNull to DDL by default (thanks to hibernate.validator.apply_to_ddl property defaults to true). But there is no guarantee that other JPA providers do or even have the ability to do that.
You should use bean validation annotations like #NotNull to ensure, that bean properties are set to a none-null value, when validating java beans in the JVM (this has nothing to do with database constraints, but in most situations should correspond to them).
You should additionally use the JPA annotation like #Column(nullable = false) to give the jpa provider hints to generate the right DDL for creating table columns with the database constraints you want. If you can or want to rely on a JPA provider like Hibernate, which applies the bean validation constraints to DDL by default, then you can omit them.

The JPA #Column Annotation
The nullable attribute of the #Column annotation has two purposes:
it's used by the schema generation tool
it's used by Hibernate during flushing the Persistence Context
Schema Generation Tool
The HBM2DDL schema generation tool translates the #Column(nullable = false) entity attribute to a NOT NULL constraint for the associated table column when generating the CREATE TABLE statement.
As I explained in the Hibernate User Guide, it's better to use a tool like Flyway instead of relying on the HBM2DDL mechanism for generating the database schema.
Persistence Context Flush
When flushing the Persistence Context, Hibernate ORM also uses the #Column(nullable = false) entity attribute:
new Nullability( session ).checkNullability( values, persister, true );
If the validation fails, Hibernate will throw a PropertyValueException, and prevents the INSERT or UPDATE statement to be executed needesly:
if ( !nullability[i] && value == null ) {
//check basic level one nullablilty
throw new PropertyValueException(
"not-null property references a null or transient value",
persister.getEntityName(),
persister.getPropertyNames()[i]
);
}
The Bean Validation #NotNull Annotation
The #NotNull annotation is defined by Bean Validation and, just like Hibernate ORM is the most popular JPA implementation, the most popular Bean Validation implementation is the Hibernate Validator framework.
When using Hibernate Validator along with Hibernate ORM, Hibernate Validator will throw a ConstraintViolation when validating the entity.

Interesting to note, all sources emphasize that #Column(nullable=false) is used only for DDL generation.
However, even if there is no #NotNull annotation, and hibernate.check_nullability option is set to true, Hibernate will perform validation of entities to be persisted.
It will throw PropertyValueException saying that "not-null property references a null or transient value", if nullable=false attributes do not have values, even if such restrictions are not implemented in the database layer.
More information about hibernate.check_nullability option is available here: http://docs.jboss.org/hibernate/orm/5.0/userguide/html_single/Hibernate_User_Guide.html#configurations-mapping.

Related

Do I need redundant JPA annotations if the database has already been created in a SQL script?

This doesn't feel very DRY at all.
I have an SQL script that generates all of my database tables, so I have a lot of redundant annotations in my #Entity class.
For example,
#Column(unique = true, length = 254)
#NotNull
private String email;
or auto increment logic such as
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
All of these field annotations are redundant considering in the SQL I have already declared such things.
Is there any reason to keep these annotations around? From what I understand, any kind of serious application should be using SQL scripts to create the database tables anyways, and it sure would be nice to have less noise in the classes I'm writing.
there are two type of annotations (DDL and validation types) in your example:
#Column(unique = true, length = 254)
#NotNull
#NotNull is a JSR 303 Bean Validation annotation. It has nothing to do with database constraints itself. It's used by validation processor and doen't connected with DB. so it's not redundant in your mapping.
But , for example
#Column(nullable = false) - it gives the jpa provider hints to generate the right DDL for creating table columns with the database constraints
2.#Column(unique = true, length = 254) they are hints for ddl that hibernate generate.
BUT for #Column(updatable = false, name = "flight_name", nullable = false, length=50)
updatable - it's not ddl , it's optimization.
from hibernate docs
#Column(
ddl/mapping : name="columnName";
optimization: boolean insertable() default true;
optimization: boolean updatable() default true;
ddl/mapping : String table() default "";
DDL -->: boolean unique() default false;
DDL -->: boolean nullable() default true;
DDL -->: String columnDefinition() default "";
DDL -->: int length() default 255;
......
)
If you use ddl scripts all hibernate ddl-annotation attributes are redundant (if you use in memory tests , there will be autogenaration for in-memory db ), and will be used only if you configured hibernate.hbm2ddl.auto option in hibernate/jpa config. Also it's very dangerous scenarios when you give hibernate permission to do DDL in production.
for work with production : see liquibase , flywaydb for DDL.
Two reasons that come to my mind:
They can be useful in integration tests when you let hibernate create and drop schema in an in-memory database that is used in tests.
Very few of them are also used by Hibernate in runtime to make some optimizations, like optional = false (not null) in combination with #PrimaryKeyJoinColumn for one-to-one associations when Hibernate knows that it is safe to make a proxy for lazy association instead of switching to eager loading.

Hibernate handling of nullable=true in #Column

I am using Hibernate with Springs backed by a Mysql db.
Here is the entity similar to the one i use to make an entry
#Entity
#Table(name = "my_table") {
#Basic
#Column(name = "my_string", nullable = false)
private String myString;
}
The sql definition is
CREATE TABLE `my_table` (
`my_string` varchar(200) DEFAULT NULL,
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Though the table allows null values, the column myString is non-nullable as per the JPA annotation. This is leading to unpredictable behaviour.
Q: Is the non-nullable ALWAYS enforced at the entity level while making inserts? Or is there some case in which it may be ignored
My expectation was that all of the entries should have been rejected. But with this setup, many entries (>7000) have gone into the table.
Only sometimes i get a DataIntegrityViolation exception from spring
org.springframework.dao.DataIntegrityViolationException: not-null property references a null or transient value: ...; nested exception is org.hibernate.PropertyValueException: not-null property references a null or transient value: ....
at org.springframework.orm.hibernate3.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:652)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:104)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:403)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:58)
....
....
Caused by: org.hibernate.PropertyValueException: not-null property references a null or transient value: ....
at org.hibernate.engine.internal.Nullability.checkNullability(Nullability.java:103)
....
I understand that having separate signature in the entity and the table is a bad practice, But i'm trying to identify what causes this behaviour and any other loopholes that might have been left out due to it.
Versions -
Mysql- 5.5
Hibernate - 4.0.0
Hibernate-jpa - 2.0
Spring-core- 3.1.0
Spring-jdbc- 3.1.2
People frequently confuse the #Column(nullable) and #NotNull annotations.
The #Column(nullable) is meant for the generated DDL scripts. But you don't use Hibernate to generate your DDL, so you shouldn't rely on it at all. What you want is #NotNull which is a runtime validation trigger.
If you define the Hibernate Validator Annotation (or JPA 2.0 ones) you can get the DDL for those as well:
Out of the box, Hibernate (as of version 3.5.x) will translate the
constraints you have defined for your entities into mapping metadata.
For example, if a property of your entity is annotated #NotNull, its
columns will be declared as not null in the DDL schema generated by
Hibernate.
And make sure you enable validation if you're using JPA with Hibernate.
If you are using JPA 2 and Hibernate Validator is in the classpath the
JPA2 specification requires that Bean Validation gets enabled. The
properties javax.persistence.validation.group.pre-persist,
javax.persistence.validation.group.pre-update and
javax.persistence.validation.group.pre-remove as described in Section
10.1.2, “Hibernate event-based validation” can in this case be configured in persistence.xml. persistence.xml also defines a node
validation-mode which can be set to AUTO, CALLBACK, NONE. The default
is AUTO.

Writing comments for an entity bean property in hibernate

Is it possible to write comments for every property of an Entity bean in Hibernate, such that when i generate the table using ddl, it creates the tables with column comments?
I think you have to use the columnDefinition property of the #Column annotation. See http://docs.redhat.com/docs/en-US/JBoss_Enterprise_Web_Server/1.0/html-single/Hibernate_Annotations_Reference_Guide/index.html
or EJB3 reference (ejb-3_0-fr-spec-persistence.pdf -- page 167):
http://download.oracle.com/otndocs/jcp/ejb-3_0-fr-eval-oth-JSpec/

Why does Hibernate re-implement functionality that databases already have?

For example:
#Table(name = "stock", catalog = "mkyong", uniqueConstraints = {
#UniqueConstraint(columnNames = "STOCK_NAME"),
#UniqueConstraint(columnNames = "STOCK_CODE") })
or
#Column(name = "STOCK_NAME", unique = true, nullable = false, length = 20)
Constraints like 'unique', 'nullable', even field length are core database features. Why include this here? Also (although this may hurt some) I'd also wager that a database's implementation of such constraints, particularly mainstream commercial DBs like Oracle, is probably better than whatever the OSS Hibernate devs can come up with.
Is it wise to use this type of stuff in Hibernate, or is it a better practice to put constraints and such in the database? It seems that if you utilize these Hibernate features, you're practically treating the database as a file system, so what's the point? Usage of this is everywhere but I've yet to find the documentation explaining why you'd do this.
It does not implement them - it has the option to validate the data model against the schema, or create it.
The hibernate.hbm2ddl.auto configuration property is the one that allows you to create the schema based on the mappings.
Automatically validates or exports schema DDL to the database when the SessionFactory is created. With create-drop, the database schema will be dropped when the SessionFactory is closed explicitly.
e.g. validate | update | create | create-drop
This is very useful, if you want your data model to be in the central place, rather than the database structure
Hibernate can create a database schema based on those annotations for you.

Unique constraint with JPA and Bean Validation

I'd like to have a #Unique constraint with Bean Validation, but that is not provided by the standard. If I would use JPA's #UniqueConstraint I wouldn't have a unique validation and error reporting mechanism.
Is there a way to define #Unique as a Bean Validation constraint and combine it with JPA, such that JPA creates a column with an unique constraint and checks wheter a value is unique or not?
Unless you acquire a lock on a whole table, it is basically not possible to check for unicity using a SQL query (any concurrent transaction could modify data after a manual check but before the commit of the ongoing transaction). In other words, it isn't possible to implement a valid unique verification at the Java level and thus to provide a validation implementation. The only reliable way to check for unicity is while committing the transaction.
The BV spec summarizes it like this:
Appendix D. Java Persistence 2.0 integration
Question: should we add #Unique that
would map to #Column(unique=true)?
#Unique cannot be tested at the Java
level reliably but could generate a
database unique constraint generation.
#Unique is not part of the BV spec
today.
So while I agree that it would be nice to have unique (and non null) constraint violations wrapped in a Bean Validation exception, this is currently not the case.
References
Bean Validation specification (JSR 303)
Appendix D. Java Persistence 2.0 integration
Question about validation and persistence constraints
More information on how to implement a #Unique and the problematic around it can be found here - http://community.jboss.org/wiki/AccessingtheHibernateSessionwithinaConstraintValidator
Well you CAN do it, but it's not trivial. The problem is: the validator requires database access to perform some queries to check, if the value you want to insert is already there or not. And this can't be really done from the validator, as it doesn't have access to the sessionFactory/session. Of course you could instantiate it (session/sessionFactory) inside the validator, but it's not a good coding practice.
You can make a validator read the JPA annotations and apply it. Here is somewhat of an example using spring validators that can be used as an idea to expand on.
JPA JSR303 Spring Form Validation
You can also inject (#Inject or Spring's #Autowired) your session bean in a custom validator and the container should know how to wire it up. I only know this as a Spring example:
import javax.validation.ConstraintValidator;
public class MyConstraintValidator implements ConstraintValidator {
#Autowired //#Inject
private Foo aDependency;
...
}
You should try (insert or update), catch the exception and do some action. For example in a JSF backing bean :
try {
dao.create(record);//or dao.modify(record)
//add message success
} catch(EJBException e) {
//look for origin of error (duplicate label, duplicate code, ...)
var err = dao.isUnique(record);
if(err == null) throw e;//other error
String clientId = null;
String message = null;
switch(err) {
case CODE:
clientId = "client_id_of_input_code";
message = "duplicate code";
break;
case LABEL:
clientId = "client_id_of_input_label";
message = "duplicate label";
break;
default:
throw new AssertionError();//or something else
}
facesContext.addMessage(clientId, new FacesMessage(FacesMessage.SEVERITY_ERROR, message));
facesContext.validationFailed();
}
Another option is to check before the insertion/modification. This can be time consuming and doesn't prevent the error to happen in the end.

Categories

Resources