#OneToOne(optional=false) and #JoinColumn(nullable=false) used together - java

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.

Related

JPA #OneToMany with 1 - 1..* relationship

How to properly map #OneToMany relationship where to create entity, on #One side of #OneToMany relationship, it is required to have atleast one entity from #Many side but entity on #Many side also requires entity on #One side to exist? To put this nightmare of a sentence simply, this is the scenario I have:
This is what I want:
[ENTITY A] 1 <-----> (1..*)[ENTITY B]
At the moment I have this:
[ENTITY A] 1 <-----> (0..*)[ENTITY B]
Which is easily done like this.
#OneToMany(cascade=CascadeType.ALL, mappedBy="customer")
public Set<Agreement> agreements = new HashSet<>();
and
#ManyToOne
#JoinColumn(name = "CUSTOMER_ID", nullable=false)
private Customer customer;
So the problem is my CUSTOMER table has no column corresponding to AGREEMENT table therefore I can't enforce rule of creating Customer only when Agreement is given. At the moment I can only setup rule to create Agreement when Customer is given because AGREEMENT table has column corresponding to CUSTOMER tabel, which is easily done by nullable=false condition.
It depends very much on what type of relationship you want to enforce. If the Agreement can exist independently from the Customer then this mean that the customer_id in agreement must be nullable.
If the Agreement can not exist independently this presumes that the customer id is not nullable in which case the Agreement can not be created in first place without the customer being created. This mean you have stronger association in between the customer and the corresponding Agreement.
Once we define that we have a relationship that is strong we need to investigate how strong it really is and who will own whom. Normaly the it is the Many side that owns the relationship and the updates are happening through the many side. This mean that your JoinColumn needs to be on the MANY and the mapped by needs to be on the ONE side.
It is interesting case when the ownership is inverse when the ONE side actually owns the relationship in this case the foreign key on the many side can not be NULL because there is no way for the owning ONE side to know what the MANY side key is.
JPA doesn't provide a way to validate this, but Hibernate Validator does:
#NotNull
#Size(min=1)
public Set<Agreement> agreements = new HashSet<>();
Then you have to manually test it via the Validator:
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
Validator validator = validatorFactory.getValidator();
validator.validate(customer)

Performing join given complex entity relationships in hibernate

I'm having difficulty establishing an association between two entities in Hibernate. The relationship is complicated.
We have a table (lets call it Translations) that contains translations used within our program whose primary key is the base text and the language code. This means there are several lines with the same base text but with differing language codes.
Translations:
BaseText
LanguageCode
I have a second table (lets call it Usage) which indicates, for each distinct BaseText in Translations table, where it is used in the program. The same text can be used in multiple places within the program, so there are potentially several lines with the same text but with differing programs.
Usage:
Text
Program
The one field that ties them both together is Text in Usage and BaseText in Translations. If I wanted to write a query that selects translations pertaining to a specific Program (as indicated in Usage table), I would probably write something like:
select * from Translations inner join Usage on Translations.BaseText = Usage.Text where Usage.Program = ?
However, I'm using hibernate and JPA to do this. I see many examples for creating a Parent-Child association between two entities, however this relationship is clearly a bit dysfunctional. This would probably be better represented with a middle table and a many-to-many relationship, however to make things more complicated, this is a legacy program using a legacy database, and thus I can't really add any tables.
The closest question that I could find related to my problem I found here, however it deals with a much cleaner Parent-Child type relationship. If I use only the one JoinColumn, it complains that the whole primary key is not satisfied.
Using annotations, how should I structure my entities so that I can optionally query Translation entities by Usage.program (if at least one line exists in Usage with a given program, I would like to retrieve its corresponding Translation line)?
This is what I have currently:
#Entity
public class Translation
#Id
private String baseText;
#Id
private String languageCode;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="baseText", referencedColumnName="text", nullable=false, insertable = false, updatable = false)
private Usage usage;
// .. getters and setters
}
#Entity
public class Usage {
#Id
private String text;
#Id
private String program;
// .. getters and setters
// OneToMany counterpart added, but I removed it because it seemed to
// make Usage the "owner" entity of the relationship, which isn't my case
}
I appreciate any help you can provide.

JPA throwing "multiple assignments to same column" during save operation

I have a model class that references another model class and seem to be encountering an issue where the #OneToOne annotation fixes one problem but causes another. Removing it causes the inverse.
JPA throws "multiple assignments to same column" when trying to save changes to model. The generated SQL has duplicate columns and I'm not sure why.
Here's a preview of what the classes look like:
The parent class references look like this:
public class Appliance {
public Integer locationId;
#Valid
#OneToOne
public Location location;
}
The child Location class has an id field and a few other text fields -- very simple:
public class Location {
public Integer id;
public String name;
}
When I attempt to perform a save operation, does anyone know why JPA is creating an insert statement for the Appliance table that contains two fields named "location_id"?
I need to annotate the reference to the child class with #OneToOne if I want to be able to retrieve data from the corresponding database table to display on screen. However, If I remove #OneToOne, the save works fine, but it obviously won't load the Location data into the child object when I query the db.
Thanks in advance!
It appears you did not define an #InheritanceType on the parent Class. Since you did not, the default is to combine the the parent and the child class into the same Table in the Single Table Strategy.
Since both entities are going into the same table, I think that #OneToOne is trying to write the id twice - regardless of which side it is on.
If you want the parent to be persisted in its own table, look at InheritanceType.JOINED.
Or consider re-factoring so that you are not persisting the parent separately as JOINED is not considered a safe option with some JPA providers.
See official Oracle Documentation below.
http://docs.oracle.com/javaee/7/tutorial/doc/persistence-intro002.htm#BNBQR
37.2.4.1 The Single Table per Class Hierarchy Strategy
With this strategy, which corresponds to the default InheritanceType.SINGLE_TABLE, all classes in the hierarchy are mapped to a single table in the database. This table has a discriminator column containing a value that identifies the subclass to which the instance represented by the row belongs.
In OpenJPA, according to the docs (http://openjpa.apache.org/builds/1.0.1/apache-openjpa-1.0.1/docs/manual/jpa_overview_mapping_field.html), section 8.4, the foreign key column in a one-to-one mapping:
Defaults to the relation field name, plus an underscore, plus the name
of the referenced primary key column.
And the JPA API seems to concur with this (http://docs.oracle.com/javaee/6/api/javax/persistence/JoinColumn.html)
I believe this means that in a one-to-one mapping, the default column name for properties in a dependent class is parentClassFieldName_dependentClassFieldName (or location_id in your case). If that's the case, the location_id column you are defining in your Appliance class is conflicting with the location_id default column name which would be generated for your Location class.
You should be able to correct this by using the #Column(name="someColumnName") annotation and the #JoinColumn annotation on your #OneToOne relationship to force the column name to be something unique.
Ok gang, I figured it out.
Here's what the new code looks like, followed by a brief explanation...
Parent Class:
public class Appliance {
public Integer locationId;
#Valid
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name="location_id", referencedColumnName="id")
public Location location;
}
Child Class:
public class Location {
public Integer id;
public String name;
}
The first part of the puzzle was the explicit addition of "cascade = CascadeType.ALL" in the parent class. This resolved the initial "multiple assignments to same column" by allowing the child object to be persisted.
However, I encountered an issue during update operations which is due to some sort of conflict between EBean and JPA whereby it triggers a save() operation on nested child objects rather than a cascading update() operation. I got around this by issuing an explicit update on the child object and then setting it to null before the parent update operation occurred. It's sort of a hack, but it seems like all these persistence frameworks solve one set of problems but cause others -- I guess that's why I've been old school and always rolled my own persistence code until now.

#ManyToOne and #BatchSize

I found in some old code strange thing (at least for me).
The field which is annotated #ManyToOne is also annotated with #BatchSize.
I always thought that #BatchSize annotation only affects when annotated at class level or on a collection (#OneToMany) and affects pre-fetching when iterating.
But maybe I am wrong and annotating #ManyToOne with #BatchSize affects something. I can't find the answer in the documentation.
Does annotating #ManyToOne with #BatchSize have sense?
I think the question refers to combining #ManyToOne and #BatchSize on the same field, e.g.:
#ManyToOne
#BatchSize(size = 5)
private User owner;
This use case is not supported by Hibernate, at least when using annotations. The only uses of batch fetching mentioned by the documentation are:
On collection fields, i.e., #OneToMany or #ManyToMany (but not #ManyToOne)
On the entity class to be fetched
E.g.:
#Entity
#BatchSize(size = 5)
public class User {
...
}
This latter case enables batching for all relationships of type User, including many-to-one relationships. However, with the annotation on the entity class it is not possible to control the behaviour on a field-by-field basis.
A search through the Hibernate source code for all uses of #BatchSize confirms the lack of support for your usage. From what I see in AnnotationBinder.java, the #BatchSize annotation is only inspected on the entity class and on fields which have some kind of #XxxToMany annotation.
#ManyToOne associated with #BatchSize could make sense only if the corresponding field is marked as lazy (lazy=true).
Indeed, if the field is not lazy, it's by definition already loaded since the enclosing entity is loaded, so the problem of database calls doesn't apply.
Imagine a Person class who has a collection of ShoesPair element (ShoesPair.class) and within this one is present an owner field marked as lazy (since optional and not really bringing an important information when retrieving a specific pair of shoes).
One wants to iterate through 25 pair of shoes (25 ShoesPair objects) in order to retrieve their owner.
If the owner field (corresponding to one person) is only annotated with #ManyToOne, there would be 25 select to database.
However, if annoted with #BatchSize(size=5), there would be merely 5 calls and so increasing performance.
From the Hibernate documentation, it is precised that batch size does not only apply with collections:
You can also enable batch fetching of collections.
Hibenate mentions especially #OneToMany cases, because these one are applied with fields that are in 90% of cases marked as lazy.
Solving N+1 query problem with Hibernate
1 Using Criteria queries with fetchMode
Criteria criteria = session.createCriteria(Customer.class);
criteria.setFetchMode("contact", FetchMode.EAGER);
2 HOL fetch join
3 #BatchSize
The #BatchSize annotation can be used to define how many identical associations to populate in a single database query. If the session has 100 customers attached to it and the mapping of the 'contact' collection is annotated with #BatchSize of size n. It means that whenever Hibernate needs to populate a lazy contact collection it checks the session and if it has more customers which their contact collections need to be populated it fetches up to n collections.
#OneToMany(mappedBy="customer",cascade=CascadeType.ALL, fetch=FetchType.LAZY)
#BatchSize(size=25)
private Set<Contact> contacts = new HashSet<Contact>();

#Basic(optional = false) vs #Column(nullable = false) in 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.)

Categories

Resources