Spring Boot joining two entities with invalid FK entries - java

I am mapping some Java classes using hibernate on a database that I have no write access for. One of the fields used as a FK between ClientEpisodes and Physician's has 6 rows that contain a -1 for the FK. THis is obviously an invalid FK, but I need to still join and simply ignore these Physicians and have a null physician.
I am joining the class using
#JoinColumn(name = "epi_phid1", referencedColumnName = "ph_id", foreignKey = #ForeignKey(ConstraintMode.NO_CONSTRAINT))
private Physician physician;
I tried specifying NO_CONSTRAINT to see if it would ignore it then, but even still I get the following error:
javax.persistence.EntityNotFoundException: Unable to find private.package.info.Physician with id -1
Is there any way to tell Hibernate to ignore values that aren't valid such as the -1?

The #ForeignKey annotation with NO_CONSTRAINT value tells hibernate to not generate foreign keys, if schema generation is used. It doesn't have any effect on the internal entity validation in the JPA framework.
There is a hibernate specific #NotFound annotation which you could use:
#JoinColumn(name = "epi_phid1", referencedColumnName = "ph_id")
#NotFound(action = NotFoundAction.IGNORE)
private Physician physician;
However be advised that if you use this in a #OneToMany or #ManyToMany annotation which maps a Collection, hibernate will fill the invalid/missing entities with null (so the list will contain null value(s)).

Related

Hibernate throws error after migration (references on same collection)

I have a problem with using a OneToMany-Mapping with Hibernate that was working in version 4.3.8.Final but is not with version 5.4.2.Final .
When I query multiple entities from the db that have the same entries in a list that is mapped with OneToMany and then try to update those entities, hibernate throws the following exception even though I do not update any entries from the mapped relation: "Found shared reference to a collection: de.Artikel.filialeLager; nested exception is org.hibernate.HibernateException: Found shared reference to a collection: de.Artikel.filialeLager"
Here is my mapping, that was working fine with version 4.3.8.Final of Hibernate but is not anymore with 5.4.2.Final.:
#OneToMany(fetch = LAZY)
#JoinColumn(name = "SOME_ID", referencedColumnName="SOME_ID", insertable = false, updatable = false)
private List<filialeLager> filialeLager;
It seems to have a unidirectional relationship but is adding a reference column for a two-way relationship
The name of the column referenced by this foreign key column. When
used with entity relationship mappings other than the cases described
below, the referenced column is in the table of the target entity.
When used with a unidirectional OneToMany foreign key mapping, the
referenced column is in the table of the source entity. When used
inside a Join- Table annotation, the referenced key column is in the
entity table of the owning entity, or inverse entity if the join is
part of the inverse join definition. When used in a collection table
mapping, the referenced column is in the table of the entity
containing the collection.
You should have something similar to this:
#OneToMany(fetch = LAZY)
#JoinColumn(name = "SOME_ID", insertable = false, updatable = false)
private List<filialeLager> filialeLager;

JPA validation - OneToOne to same table

I just enabled the JPA validation in eclipse and in shows me some errors (The code is actually running fine).
I have an article entity and it holds a refrence to the next article and the previous entity (of the same type). The validator complains with the message:
Column "nextArticle" cannot be resolved on table "article"
What does this mean exactly? The SQL table has the columns as well. I tried also to map the variables to each other with the "mappedBy" and "JoinColumn" annotation, but was not able to resolve the validation error.
That's the class and validation error:
And that' the mapping:
Edit: Tried the suggestion from anttix: The columns in the table are named "nextArticle_id" and "prevArticle_id", so I came up with that code:
#OneToOne(mappedBy = "prevArticle")
#JoinColumn(name = "nextArticle_id")
public Article getNextArticle() {
return nextArticle;
}
#OneToOne(mappedBy = "nextArticle")
#JoinColumn(name = "prevArticle_id")
public Article getPrevArticle() {
return prevArticle;
}
But the validator complains now about the "mappedBy" annotation with the message:
In attribute 'prevArticle', the "mapped by" attribute 'nextArticle' has an invalid mapping type for this relationship.
Edit 2: I found the solution. I had to tell the validator the names of the columns in the actual database with the #Column annotation like this:
Eclipse can't find a column called prevArticle in the table. You should specifiy the column name for nextArticle and create a bidirectional relation with prevArticle to indicate that it does not need a foreign key column of its own.
#OneToOne
#JoinColumn(name = "next_id")
private Article nextArticle;
#OneToOne(mappedBy = "nextArticle")
private Article prevArticle;
You can omit the #JoinColumn from nextArticle if you want, but I would keep it there to make it clear which relation "owns" the foreign key column.
See also:
http://en.wikibooks.org/wiki/Java_Persistence/OneToOne

org.hibernate.AnnotationException: referencedColumnNames referencing not mapped to a single property

I ran into the below exception while mapping a one-to-one between 2 entities. The 1 first entity has embedded composite key. The second entity also has embedded composite key. The tables are part of legacy system. Data is flat, relations are not well defined. Please help.
Caused by: org.hibernate.AnnotationException: referencedColumnNames(FLAG_NAME) of net.javabeat.spring.model.ReferralsM.mnEditFlag referencing net.javabeat.spring.model.MnEditFlag not mapped to a single property
at org.hibernate.cfg.BinderHelper.createSyntheticPropertyReference(BinderHelper.java:205)
at org.hibernate.cfg.ToOneFkSecondPass.doSecondPass(ToOneFkSecondPass.java:116)
at org.hibernate.cfg.Configuration.processEndOfQueue(Configuration.java:1515)
at org.hibernate.cfg.Configuration.processFkSecondPassInOrder(Configuration.java:1440)
at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1358)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1727)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1778)
at org.springframework.orm.hibernate4.LocalSessionFactoryBuilder.buildSessionFactory(LocalSessionFactoryBuilder.java:247)
at org.springframework.orm.hibernate4.LocalSessionFactoryBean.buildSessionFactory(LocalSessionFactoryBean.java:373)
at org.springframework.orm.hibernate4.LocalSessionFactoryBean.afterPropertiesSet(LocalSessionFactoryBean.java:358)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1571)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1509)
... 34 more
Here is my one to one mapping in the main/parent table.
#OneToOne(targetEntity = MnEditFlag.class, fetch = FetchType.LAZY)
#JoinColumn(name = "REFFLG", referencedColumnName = "FLAG_NAME", insertable = false, updatable = false)
MnEditFlag mnEditFlag;
The cause of the issue is that you are trying to use a single join column, while the identity of the referenced entity is defined by multiple columns. You simply have to define all the needed join colums and you are good to go:
#JoinColumns({
#JoinColumn(name = "REFFLG", referencedColumnName = "FLAG_NAME"),
#JoinColumn(name = "OTHER_KEY", referencedColumnName = "SOME_OTHER_NAME"))
...
})
MnEditFlag mnEditFlag;
OT: you should not need the targetEntity attribute on the OneToOne annotation. This is already defined by the type of the target entity: MnEditFlag. You probably need targetEntity only for untyped Collections.
EDIT: If there is a single join column, which is only part of the PK and you cannot change the existing tables, perhaps you can define a new join table with all necessary columns.
Then you define the join table to be used for the relationship:
#JoinTable(name="ReferralsM_MnEditFlag",
joinColumns={
#JoinColumn(name="REFERRALS_ID1", referencedColumnName="ID1"),
#JoinColumn(name="REFERRALS_ID2", referencedColumnName="ID2")
}, inverseJoinColumns={
#JoinColumn(name="REFFLG", referencedColumnName="FLAG_NAME"),
#JoinColumn(name="REFFLG2", referencedColumnName="FLAG_NAME2")
})
MnEditFlag mnEditFlag;
You would have to migrate the data to the new join table programmatically or by queries.
Unfortunately you cannot define a relationship with a partial PK with vanilla JPA, perhaps Hibernate has such a feature, like one-to-one by query, but I cannot confirm it.
EDIT2: The join table should contain all PK columns for both entities to be fully functional. That is why I have defined two join columns for each side in my example. The number of columns and their names are purely exemplary.
Extracting only the one join column you already have in your table would not add any value.
The optimal solution would be to change the entity tables so they define a proper relationship between the entities. It is sufficient to change only one of the tables and define it as the owning side as you did, but with all FK columns. This would require a migration effort, since you would need to add the data for the missing FK columns like described above.
EDIT3: The strategies I recommended were based on the assubmption that you want to have complete CRUD functionality. If you just want to pull the data for display or reporting, a view is perfectly fine. You can define the columns you need and map the whole view to a single entity. However, as it is a view, you will not be able to change the data or migrate it.
You could use targetEntity on #ManyToOne. The targetEntity should be equal to the parent (probably abstract) class.
The temporary solution is here depending to your class hierarchy:
https://hibernate.atlassian.net/browse/HHH-4975

JPA Read but not write/update an attribute

I have a JPA Entity that has an attribute declared in the following way
#OneToOne(cascade = CascadeType.MERGE, fetch = FetchType.EAGER)
#JoinColumn(name = "userId", referencedColumnName = "id", insertable = false, updatable = false)
private UserBare user;
(UserBare is a dummy entity I've created which is a dumbed down version of a User obj as I needed some date, not all of User obj be made available to this entity. Is there a better way to approach this?
This is a convenience attr that I use just make the userBare object available to this entity when reading this entity. But when I actually write this entity, it seems to create new entries of 'user' in the database (instead of updating). I already have insertable=false,updatable=false but it still writes to the database. I tried removing the CascadeType declaration but that is throwing an error.
Here is the database snapshot after the unwanted rows are added (last 3). Also I've noticed that deleting the original entity did not delete these three unwanted rows. So I guess JPA is storing them but the references are intact to the original entity.
My souspicioun would be that "OneToOne" means "OneToOne" excluding the possibility of "OneToZero" if you don't set Optional to true like this:
#OneToOne(optional=true)
But just a guess.. I'm not sure

How do I create an index on join tables using Hibernate annotations?

I have a relationship as follows using Hibernate annotations, this is what I tried:
public class Job{
...
#OneToMany(cascade = CascadeType.ALL)
#JoinTable(name = "jobs_resource_locations")
#ForeignKey(name = "job_inputs_fk")
#Index(name="job_inputs_fk")
private List<FileSystemLocation> inputs;
This sort of thing works nicely on ManyToOne like so:
#ManyToOne
#JoinColumn(name = "service_call_id", referencedColumnName = "id")
#ForeignKey(name = "job_service_call_fk")
#Index(name = "job_service_call_fk")
private ServiceCall serviceCall;
I wanted to ensure that the foreign key gets indexed on PostgreSQL and that the schema looks similar on MySQL, hence the #ForeignKey and #Index with the same name (MySQL always creates an index with the same name as the FK).
I cannot create the index on the inverse side because FileSystemLocation is unaware of the relationship. Hence the JoinTable.
The former example fails since Hibernate finds no column in Job to index:
org.hibernate.MappingException: Unable to find logical column name from physical name null in table jobs
Does anyone know how to create indices on JoinTable foreign keys using Hibernate?
It's not exactly the answer you would like to receive, but this is the expected behavior. In other words: this is not supported. See the following JIRA for more details:
https://hibernate.atlassian.net/browse/HHH-4263

Categories