What's the difference between #Basic(optional = false) and #Column(nullable = false) in JPA persistence?
Gordon Yorke (EclipseLink Architecture Committee Member, TopLink Core Technical Lead, JPA 2.0 Expert Group Member) wrote a good answer on this topic so instead of paraphrasing him, I'll quote his answer:
The difference between optional and
nullable is the scope at which they
are evaluated. The definition of
'optional' talks about property and
field values and suggests that this
feature should be evaluated within the
runtime. 'nullable' is only in
reference to database columns.
If an implementation chooses to
implement optional then those
properties should be evaluated in
memory by the Persistence Provider and
an exception raised before SQL is sent
to the database otherwise when using
'updatable=false' 'optional'
violations would never be reported.
So I tried the #Basic(optional=false) annotation using JPA 2.1 (EclipseLink) and it turns out the annotation is ignored in actual usage (at least for a String field). (e.g. entityManager.persist calls).
So I went to the specification and read up about it.
Here is what the spec has to say:
http://download.oracle.com/otndocs/jcp/persistence-2.0-fr-oth-JSpec/
Basic(optional): Whether the value of the field or property may be
null. This is a hint and is disregarded for primitive types; it may be
used in schema generation.
So I think this sentence explains the real use case for Basic(optional) it is used in schema generation. (That is: when you generate CREATE TABLE SQL from Java Entity classes. This is something Hibernate can do for example.)
Related
I'm wondering why there is no optional (not necessary from Java 8) in Hibernate? It looks like good way to implement lazy OneToOne relation. Instead hibernate suggest to enable byte-code manipulation to achieve lazy one to one.
There is an 'optional' parameter.
#Basic(optional = true) above the column.
"Defines whether the value of the field or property may be null. This is a hint and is disregarded for primitive types; it may be used in schema generation. If not specified, defaults to true."
source: https://www.objectdb.com/api/java/jpa/Basic/optional
I often specify my #Column annotations like this:
#Column(columnDefinition="character varying (100) not null",length=100,nullable=false)
As you can see I specify length and nullable even though the columnDefinition already specifies those. That's because I don't know where/when these values are used exactly.
So, when specifying columnDefinition, what other properties of #Column are made redundant?
If it matters, I use Hibernate and PostgreSQL
My Answer: All of the following should be overridden (i.e. describe them all within columndefinition, if appropriate):
length
precision
scale
nullable
unique
i.e. the column DDL will consist of: name + columndefinition and nothing else.
Rationale follows.
Annotation containing the word "Column" or "Table" is purely physical - properties only used to control DDL/DML against database.
Other annotation purely logical - properties used in-memory in java to control JPA processing.
That's why sometimes it appears the optionality/nullability is set twice - once via #Basic(...,optional=true) and once via #Column(...,nullable=true). Former says attribute/association can be null in the JPA object model (in-memory), at flush time; latter says DB column can be null. Usually you'd want them set the same - but not always, depending on how the DB tables are setup and reused.
In your example, length and nullable properties are overridden and redundant.
So, when specifying columnDefinition, what other properties of #Column are made redundant?
In JPA Spec & javadoc:
columnDefinition definition:
The SQL fragment that is used when generating the DDL for the column.
columnDefinition default:
Generated SQL to create a column of the inferred type.
The following examples are provided:
#Column(name="DESC", columnDefinition="CLOB NOT NULL", table="EMP_DETAIL")
#Column(name="EMP_PIC", columnDefinition="BLOB NOT NULL")
And, err..., that's it really. :-$ ?!
Does columnDefinition override other properties provided in the same annotation?
The javadoc and JPA spec don't explicity address this - spec's not giving great protection. To be 100% sure, test with your chosen implementation.
The following can be safely implied from examples provided in the JPA spec
name & table can be used in conjunction with columnDefinition, neither are overridden
nullable is overridden/made redundant by columnDefinition
The following can be fairly safely implied from the "logic of the situation" (did I just say that?? :-P ):
length, precision, scale are overridden/made redundant by the columnDefinition - they are integral to the type
insertable and updateable are provided separately and never included in columnDefinition, because they control SQL generation in-memory, before it is emmitted to the database.
That leaves just the "unique" property. It's similar to nullable - extends/qualifies the type definition, so should be treated integral to type definition. i.e. should be overridden.
Test My Answer
For columns "A" & "B", respectively:
#Column(name="...", table="...", insertable=true, updateable=false,
columndefinition="NUMBER(5,2) NOT NULL UNIQUE"
#Column(name="...", table="...", insertable=false, updateable=true,
columndefinition="NVARCHAR2(100) NULL"
confirm generated table has correct type/nullability/uniqueness
optionally, do JPA insert & update: former should include column A, latter column B
columnDefinition will override the sql DDL generated by hibernate for this particular column, it is non portable and depends on what database you are using. You can use it to specify nullable, length, precision, scale... ect.
I've bumped into this example in JPA 2.0 FR Specification, 11.1.37. OneToOne Annotation, page 403:
#OneToOne(optional=false)
#JoinColumn(name="CUSTREC_ID", unique=true, nullable=false, updatable=false)
public CustomerRecord getCustomerRecord() { return customerRecord; }
Is there any reason that I should put #OneToOne(optional=false) and at that same time put #JoinColumn(... nullable=false)?
Aren't these two declarations the same? Isn't one of them redundant?
Are both of them used in DDL schema generation?
Formally optional=false is a runtime instruction to the JPA implementation, and nullable=false is an instruction to the DDL generator. So they are not strictly redundant.
The difference can become significant when there is entity inheritance involved. If a particular mapping exists only on a subclass, and you have single table table per-hierarchy strategy, then the OneToOne mapping may be optional=false on the particular subclass that contains the mapping. However, the actual join column cannot be made not-null, since then other sub classes that share the table can't be inserted!
In practice different versions of different providers may or may not interpret either one at either time, caveat emptor.
I've configured Hibernate to use PostgreSQL sequence (via annotations) to generate values for primary key id column as follows:
#Id
#SequenceGenerator(name="pk_sequence",sequenceName="entity_id_seq")
#GeneratedValue(strategy=GenerationType.SEQUENCE,generator="pk_sequence")
#Column(name="id", unique=true, nullable=false)
public int getId() {
return this.id;
}
What I see with this configuration is that hibernate is already assigning id values > 3000 on persisting, whereas the query on used sequence shows the following:
database=# select last_value from entity_id_seq;
last_value
------------
69
(1 row)
Questions:
Is there anything wrong or not?
Should hibernate sync with the sequence table?
If not, where does it store the last generated id?
Thank you.
I had the same problem. It is related to the id allocating strategies of Hibernate. Whe n you choose GenerationType.SEQUENCE, Hibernate uses HiLo strategy which allocates IDs in blocks of 50 by default. So you can explicitly set allocationSize value like this:
#Id
#SequenceGenerator(name="pk_sequence",sequenceName="entity_id_seq", allocationSize=1)
#GeneratedValue(strategy=GenerationType.SEQUENCE,generator="pk_sequence")
#Column(name="id", unique=true, nullable=false)
public int getId() {
return this.id;
}
Though, I've also heard opinions that using HiLo strategy with allocationSize=1 is not a good practice. Some people recommend to use GenerationType.AUTO instead when you have to deal with database-managed sequences
Update: I did end up going with allocationSize=1, and things seem to work as I expect now. My application is such that I don't really need blocks of IDs anyway, so YMMV.
DO NOT USE GenerationType.SEQUENCE for Postgres sequences!
It's completely counter-intuitive, but the Hibernate folks completely messed up on this. You must use GenerationType.AUTO or Hibernate will demolish your sequences if you have to restart/rebuild your DB. It's almost criminally negligent that they would allow this code to go into a production build, but the Hibernate team is rather famous for their bull-headed stances towards flatly-wrong positions (check out their position on LEFT JOINs, for instance).
First, you have to determine which version of Hibernate you are using. In terms of hibernate-core versions, 3.2 onwards introduced more consistent support for id generators especially in regards to defined in annotations. See http://in.relation.to/Bloggers/New323HibernateIdentifierGenerators for a discussion.
Next 3.6 introduced a setting ('hibernate.id.new_generator_mappings') which makes the generators discussed in that blog the default way JPA-annotations are handled. The setting is false by default because Hibernate has to maintain backwards compatibility with older versions. If you want the new behavior (which is completely recommended) then simply set that setting to true.
How GenerationType is handled depends on which version you are using and whether you have 'hibernate.id.new_generator_mappings' set to true. I will assume you are using 3.6+ (since anything older is, well, old) and do have 'hibernate.id.new_generator_mappings' set to true (since that is the recommendation for new apps):
GenerationType.AUTO -> treated as GenerationType.SEQUENCE
GenerationType.SEQUENCE -> maps to the org.hibernate.id.enhanced.SequenceStyleGenerator class discussed in the blog
GenerationType.TABLE -> maps to the org.hibernate.id.enhanced.TableGenerator class discussed in the blog
In Postgres I would do this:
#Id
#SequenceGenerator(name="pk_sequence",sequenceName="\"entity_id_seq\"")
#GeneratedValue(strategy=GenerationType.SEQUENCE,generator="\"pk_sequence\"")
#Column(name="\"id\"", unique=true)
private int id;
Mostly with uppercase names Hibernate need to be passed escaped quotes in order to understand Postgres and find the tables, columns or sequences names.
What is the difference between #Column and #Basic annotations in JPA? Can they be used together? Should they be used together? Or does one of them suffice?
#Basic 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.
If you specify one without the other then you get default behaviour which is sensible, so commonly folks use only one with the exception of special cases.
So if we wanted a lazy loading of an attribute and to specify a column name we can say
#Basic(fetch=FetchType.LAZY)
#Column(name="WIBBLE")
If we neeed the default, non-lazy behaviour then just the #Column would have been sufficient.
In addition to #djna's answer, it is worth noting that #Basic should be compared with #OneToMany, #ManyToOne and #ManyToMany. Only one of these can be specified on any property.
#Column and #JoinColumn can be specified along with any of these to describe the database column properties.
These are two sets of annotations that can be used together, but only one annotation of each set can be used at a time.
It is worth noting that Basic is designed for primitive fields
http://en.wikibooks.org/wiki/Java_Persistence/Basic_Attributes
A basic attribute is one where the attribute class is a simple type such as String, Number, Date or a primitive. A basic attribute's value can map directly to the column value in the database.
The types and conversions supported depend on the JPA implementation and database platform. Any basic attribute using a type that does not map directly to a database type can be serialized to a binary database type.
The easiest way to map a basic attribute in JPA is to do nothing. Any attributes that have no other annotations and do not reference other entities will be automatically mapped as basic, and even serialized if not a basic type. The column name for the attribute will be defaulted, named the same as the attribute name, as uppercase.
The #Basic annotation are applied to JPA entities, and the of #Column are applied to the database columns
#Basic annotation's optional attribute defines whether the entity field can be null or not; on the other hand,
#Column annotation's nullable attribute specifies whether the corresponding database column can be null
We can use #Basic to indicate that a field should be lazily loaded
The #Column annotation allows us to specify the name of the mapped database column
#Basic annotation marks the property as not optional on the Java object level. And (nullable = false) on the column mapping, is only responsible for the generation of a NOT NULL database constraint.