The following is copied from hibernate's document. (http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/#d0e2770)
#CollectionOfElements
#JoinTable(
table=#Table(name="BoyFavoriteNumbers"),
joinColumns = #JoinColumn(name="BoyId")
)
#Column(name="favoriteNumber", nullable=false)
However, when I put this in practice, I just found that #JoinTable has no "table" property, instead it has a "name" property to specify the table name. But I need "table" property to specify indexes.
What's going on here? I'm almost driven crazy!
No, it doesn't, this sample is not accurate. Just in case, the #IndexColumn annotation that you see in this sample has nothing to do with a database index, it is used to store the index number of an element in an indexed collection. But I guess you're aware of that.
Actually, I'd suggest to raise a Jira issue specifying your use case and your database dialect (it seemts that generating an index on the FK works with some dialects, like MySQL, but doesn't with say Oracle).
Related
I have a mapped entity like this:
#OneToMany(targetEntity = Child.class)
#JoinColumn(name = "PARENT_ID", referencedColumnName = "PARENT_ID")
#OrderBy("orderNumber")
private List<Child> children;
I would like to specify NULLS LAST in #OrderBy annotation of my mapped collection.
I am using Oracle database, which considers NULL values larger than any non-NULL values.
The problem is in my integration test, which uses h2 database and it seems the NULL values are evaluated differently.
So far, I came up with a hack to use nvl2() function inside of the #OrderNumber like this:
#OrderBy("nvl2(orderNumber, orderNumber, 100000000)")
This hack works, but it seems nasty and I don't like the idea that I have this code there just because of the integration tests. As I mentioned above, Oracle returns the rows in correct order by default, so the basic #OrderBy(orderNumber) without handling nulls works good. On the other hand, I want to have it tested in case the app will use different database.
Is there any way how to solve this issue in a better way?
To enable Oracle compatibility mode in H2 you need to append ;MODE=Oracle;DEFAULT_NULL_ORDERING=HIGH to JDBC URL as suggested in documentation:
https://h2database.com/html/features.html#compatibility
The DEFAULT_NULL_ORDERING setting changes the default ordering of NULL values (when neither NULLS FIRST nor NULLS LAST are used). There are four possible options, LOW is default.
This setting can be used separately without this compatibility mode if you don't need it.
Let's say I have an entity with a very long name:
#Entity
public class SupercalifragilisticexpialidociousPanda
{
...
}
Using Hibernate to persist it to a Postgres DB works flawlessly. Oracle, however, doesn't allow for table/column/index names longer than 30 characters.
That should be easy to fix, since i can just specify the table name manually, like this:
#Entity
#Table(name="SuperPanda")
public class SupercalifragilisticexpialidociousPanda
{
...
}
Now everything is back to working perfectly... except that any references I have to the entity in other tables still use the long class name ("SupercalifragilisticexpialidociousPanda") instead of the short table name ("SuperPanda").
For instance, if the entity has an embedded ElementCollection, like this:
#ElementCollection
private Set<String> nicknames;
Hibernate will try to create a DB like this: create table SupercalifragilisticexpialidociousPanda_nicknames, which will naturally cause an ORA-00972: identifier is too long error.
The same thing also happens for #OneToOne associations, where the lookup column would be called something like supercalifragilisticexpialidociousPanda_uuid, which also fails with oracle.
Now, one option would be to add a #CollectionTable(name="SuperPanda_nicknames") and #Column(name="...") annotation manually to every field that references this entity, but that's a lot of work and really error-prone.
Is there a way to just tell Hibernate once to use the short name everywhere a reference to the entity is required?
I also tried setting the entity name, like this:
#Entity(name="SuperPanda")
#Table(name="SuperPanda")
public class SupercalifragilisticexpialidociousPanda
{
...
}
... but it doesn't fix the issue.
What does one normally do in such a case?
Usually people give names for each database thing (table, column, index) by themselves. Letting Hibernate decide for you can lead to problem in future when you decide to refactor something.
All reference can be configured one way or another to use names you decide to use.
Ask specific question in case you can figure out the way to do it yourself.
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 have a simple jpa entity 'ApplicationForm' with a one to many list in it:
#OneToMany(cascade=CascadeType.REMOVE, mappedBy="textQuestion")
private List<Dictionary> questions;
The variable Dictionary contained in ApplicationForm is just another plain entity with just the text of the question.
The corresponding database table mapped by Dictionary is:
'locale' 'text' 'formId'
en my question 123
it mia domanda 123
I was wondering if it's possible with jpa or hibernate, to build a query for retrieving an ApplicationForm entity with a Dictionary for a specific locale, for example 'it' only.
That would be easy enough to do with standard sql, but I cannot translate in hql.
If not possible, could you suggest an alternative way ? I have tried to manually iterate the Dictionary questions list and remove the not required locale, but is not really elegant, and also I got a jpa/hibernate error.
I hope I made myself clear, and code supplied is enough.
thanks
I was wondering if it's possible with jpa or hibernate, to build a query for retrieving an ApplicationForm entity with a Dictionary for a specific locale, for example 'it' only.
Not with standard JPA. But Hibernate allows to apply arbitrary filters to a collection load during a given session. From the Hibernate Annotations Reference Guide:
2.4.8. Filters
Hibernate has the ability to apply
arbitrary filters on top of your data.
Those filters are applied at runtime
on a given session. First, you need to
define them.
#org.hibernate.annotations.FilterDef
or #FilterDefs define filter
definition(s) used by filter(s) using
the same name. A filter definition has
a name() and an array of
parameters(). A parameter will allow
you to adjust the behavior of the
filter at runtime. Each parameter is
defined by a #ParamDef which has a
name and a type. You can also define a
defaultCondition() parameter for a
given #FilterDef to set the default
condition to use when none are defined
in each individual #Filter. A
#FilterDef(s) can be defined at the
class or package level.
We now need to define the SQL filter
clause applied to either the entity
load or the collection load. #Filter
is used and placed either on the
entity or the collection element
#Entity
#FilterDef(name="minLength", parameters=#ParamDef( name="minLength", type="integer" ) )
#Filters( {
#Filter(name="betweenLength", condition=":minLength <= length and :maxLength >= length"),
#Filter(name="minLength", condition=":minLength <= length")
} )
public class Forest { ... }
When the collection use an association
table as a relational representation,
you might want to apply the filter
condition to the association table
itself or to the target entity table.
To apply the constraint on the target
entity, use the regular #Filter
annotation. However, if you wan to
target the association table, use the
#FilterJoinTable annotation.
#OneToMany
#JoinTable
//filter on the target entity table
#Filter(name="betweenLength", condition=":minLength <= length and :maxLength >= length")
//filter on the association table
#FilterJoinTable(name="security", condition=":userlevel >= requredLevel")
public Set<Forest> getForests() { ... }
See also
Chapter 17. Filtering data In the Hibernate Core Reference Documentation.
Hibernate3 Filters
Will this work -
#OneToOne()
#JoinColumn(name = "id", referencedColumnName = "type_id")
#Where(clause = "type_name = OBJECTIVE")
public NoteEntity getObjectiveNote() {
return objectiveNote;
}
This is what I am trying to do - get the record from table note whose type_id is the id of the current object and type_name is OBJECTIVE.
I can't get the above mapping to work. What am I doing wrong here?
This just plain does not work, sorry :( You will need to do it as one to many and live with getting a collection with a single element.
If you really want it to work this way, you can trick hibernate by storing both the foreign key ID and the type_name in a join table and telling it that both columns make up the foreign key.
Actually you can achieve this by specifying #OneToOne without any #Where, but putting #Where on the referenced entity class. I tested this on Hibernate 4.3.11.
This works if you don't care about any entity objects that do not match your #Where.
If you do care about other entities, you can probably create a subclass entity, put #Where on it and join that subclass. But I have not tested this scenario.