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
Related
Currently I have to add #Enumerated(EnumType.STRING) to all fields that use enums, and very often I forget to do it, and then the default kicks in (EnumType.ORDINAL).
Is it possible to configure JPA to map all enums to String? (To make the default EnumType.STRING ?)
Unfortunately not.
But think twice when using enums. I wrote an article about the issues:
https://martinelli.ch/should-you-use-enums-with-jpa/
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'm hoping someone can help me with my hibernate issue as I've been banging my head against Google for around an hour without result.
So the issue is that I have a SortedMap in a class, using Integer as the Key (and its natural built-in compareTo method) and another class as the value type. I'm using the key to keep the user-defined order of the value type and trying to get Hibernate to persist this.
For whatever reason, Hibernate has defaulted to disregarding the key I have inputted and instead using the value type's primary key as the key instead. When I load my Map back out of the database all of my keys have been changed in this way.
Definition of the Map is shown below (I'm using annotation-style Hibernate);
#ManyToMany(cascade = CascadeType.ALL)
#MapKey
#Sort(type = SortType.NATURAL)
private SortedMap<Integer, Column> columnOrder;
I can't use the Column type to store the order itself as the Column may be used in many instances of the containing type, with a different key value each time. Any guidance would be most appreciated.
So I found the answer after discovering this StackOverflow question with a similar issue: Sorted map of Java primitives using JPA2 and Hibernate?
By changing the #MapKey annotation to the #MapKeyColumn annotation, I was able to give Hibernate the instruction to persist the key in the column name I specified, as below;
#ManyToMany(cascade = CascadeType.ALL)
#MapKeyColumn(name = "hierarchyOrdering")
#Sort(type = SortType.NATURAL)
private SortedMap<Integer, Column> columnOrder;
Thanks for the help.
From the javadoc of javax.persistence.OrderColumn:
Specifies a column that is used to maintain the persistent order of a list. The persistence provider is responsible for maintaining the order upon retrieval and in the database. The persistence provider is responsible for updating the ordering upon flushing to the database to reflect any insertion, deletion, or reordering affecting the list.
So it is possible to use a list for that.
The JPA 2.0 spec states in section 2.2 Persistent Fields and Properties:
Collection-valued persistent fields and properties must be defined in terms of one of the following collection-
valued interfaces regardless of whether the entity class otherwise adheres to the JavaBeans
method conventions noted above and whether field or property access is used: java.util.Collection,
java.util.Set, java.util.List[3], java.util.Map. The collection implementation
type may be used by the application to initialize fields or properties before the entity is made
persistent. Once the entity becomes managed (or detached), subsequent access must be through the
interface type.
So it seems as if a SortedMap is not supported by JPA.
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.)
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.