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

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

Related

Spring Boot joining two entities with invalid FK entries

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)).

Select from different join tables

I'm building a generic property system, where different entities may have properties (represented as 'PropertyEntity') assigned. Such a property entity has a key, a value and knows the id of the entity to which it is attached. Consider a 'UserEntity' in which a mapping for its properties would look like this:
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.REMOVE)
#JoinTable(name = "UserProperties",
joinColumns = #JoinColumn(name = "parentId"),
inverseJoinColumns = #JoinColumn(name = "id"))
private Map<String, PropertyEntity> properties;
There may also be more entities with properties, that each have their own join table.
I'm now wondering what would be the most efficient way to query for a User (or different entity that also has properties) with certain properties. Like 'Give me all Users where property A has value B and property X has value Y'.
Remember that the querying code must be generic in a way that it does not know whether a User or some other entity which has a property join table should be retrieved. The type to search for may be given as a Class object.
I'm currently running the naive approach in which the PropertyEntity also knows the name of the type to which it belongs. I then query the properties by their key and parent type and post process the result by manually finding the respective parent entities.
SELECT p FROM PropertyEntity p WHERE p.propertyKey in(?1) AND p.parentType = ?2
Where parameter 1 is a set of keys and parameter 2 the name of the parent type like 'com.domain.UserEntity'. This approach is obviously bad because it totally subverts the use of the extra join tables per entity and needs post filtering of the results by their values.

Hibernate embeddable list mapping with identifier

I have a Person entity with an embeddable Address and there's a one-to-many relation between them (a person can have multiple addresses). The current mapping is something like this:
#Embeddable
public class Address {
// ... attributes
}
#Entity
public class Person {
#ElementCollection(fetch = FetchType.EAGER)
#JoinTable(name = "Person_addresses", joinColumns = #JoinColumn(name = "personid")
)
/*
Attribute ovverrides with annotations
*/
private java.util.Set<Address> addresses = new java.util.HashSet<Address>();
}
Using this annotation means that in the database I have a Person_addresses table which contains all the address attributes and a personid. But it also means that if I have a person with an address list and I update the address list, Hibernate deletes all the related records and inserts them (the modified ones) again.
As far as I know there's a way to have a primary key in this table for each record - in this case hibernate can decide which item of the list needs to be updated. So my question is, how can I map an embeddable list with identifiers in the joining table? (I hope it's understandable what I want:)).
http://en.wikibooks.org/wiki/Java_Persistence/ElementCollection#Primary_keys_in_CollectionTable
The JPA 2.0 specification does not provide a way to define the Id in
the Embeddable. However, to delete or update a element of the
ElementCollection mapping, some unique key is normally required.
Otherwise, on every update the JPA provider would need to delete
everything from the CollectionTable for the Entity, and then insert
the values back. So, the JPA provider will most likely assume that the
combination of all of the fields in the Embeddable are unique, in
combination with the foreign key (JoinColumn(s)). This however could
be inefficient, or just not feasible if the Embeddable is big, or
complex. Some JPA providers may allow the Id to be specified in the
Embeddable, to resolve this issue. Note in this case the Id only needs
to be unique for the collection, not the table, as the foreign key is
included. Some may also allow the unique option on the CollectionTable
to be used for this. Otherwise, if your Embeddable is complex, you may
consider making it an Entity and use a OneToMany instead.
So thats it, it can't be done.
As maestro's reply implies, the only portable solution is to convert this to use an entity and a one-to-many.
That said, Hibernate has a non-spec feature called an "id bag" which allows you to map a basic or embeddable collection with an identifier for each row, thereby giving you the efficient updates you want:
#Entity
public class Person {
#CollectionId( columns={"address_id"}, type="int", generator="increment" )
#ElementCollection(fetch = FetchType.EAGER)
#JoinTable(name = "Person_addresses", joinColumns = #JoinColumn(name = "personid"))
private java.util.List<Address> addresses = new java.util.ArrayList<Address>();
}
Notice the switch from Set to List however. Also notice the generated table structure... looks an awful lot like an entity ;)

How can I access the underlying column after defining a #ManyToOne relationship on it in Spring?

I'm using Spring 3.2 with Roo 1.2.3 to build a database-backed Java application via Hibernate. I have several bidirectional OneToMany/ManyToOne relationships among the tables in my database. When I set up the ManyToOne side of the relationship using #JoinColumn (via "field reference" in Roo), a new field whose type is the related entity (the "one" in ManyToOne) is created. However, once this is done, there seems to be no way to access the underlying column value on which the ManyToOne relationship is based. This is a problem when the underlying join column contains data needed by the application (i.e. when the join column contains product stock numbers).
Is there any way to set up my entity class so that the column on which its ManyToOne relationship is based remains accessible without traversing the new join property? How can I define an accessor method for the value of this column?
I've been looking online for an answer to this question for several days, but to no avail. Thanks in advance for your help.
just map the column a second time with insertable=false and updateable=false
To make it more concrete. It's possible to do a HQL-SELCT and restrict a ManyToOne relationship, without any join in the resulting SQL:
Instead of using a join in
session.createQuery("FROM Person person WHERE person.adress.id = 42")
we use can use the adress_idcolumn
session.createQuery("FROM Person person WHERE person.adressId = 42")
This works, if you specify an additional adressId field, which is only used as mapping info for Hibernate:
#Entity
#Access(AccessType.FIELD)
public class Person{
#Id
String id;
#JoinColumn(name = "adress_id")
#ManyToOne(fetch = FetchType.LAZY)
#Nullable
public Adress adress;
#Column(name = "adress_id", insertable = false, updatable = false)
private String adressId;
}
#Entity
#Access(FIELD)
public class Adress{
#Id
String id;
}
The AccessType.FIELD is not needed (But we can leave getters/setters in example). The FetchType.LAZY and #Nullable are also optional, but make it clear when it makes sense to use it. We are able to load Person entities which have a specific Address (we know the address id). But we don't need a join because it's not needed for the WHERE-clause and not for the initial fetch (the address can be fetched lazy).

ManyToMany properties

This may be a stupid question or may be asked, But i didnt find which helped me.
Can anyone of you guys tell me what these properties described in ManyToMany relation do. And if there are any which i missed and should be used in ManyToMany mapping please help me with that too.
#ManyToMany(cascade = CascadeType.ALL)
#JoinTable(name = "customer_service",
joinColumns = {#JoinColumn(name = "customer_id")},
inverseJoinColumns = #JoinColumn(name = "service_id"))
This is description of a many-to-many relationship between (I am guessing) Customer and Service tables. Technically all attributes are optional so:
#ManyToMany
private Set<Customer> services;
in Service would be fine (similar annotation on Customer side if the relationship is bidirectional).
Now assuming you have a some basic relational databases design knowledge:
#JoinTable describes a join table, the one used to store relationships between customer and service. Each row contains one reference to customer and one to service. This table will be named customer_service
joinColumns - definition of column(s) referencing the second side of the relationship - customer_id
inverseJoinColumns - definition of column(s) referencing back to the starting side - service_id in this example.
Join- and inverse join columns are symmetric - if you define the many-to-many relationship on the other side you switch them. Otherwise everything will work from Java perspective but Hibernate will create a foreign key from customer_id to Service table and vice-versa and will insert values accordingly.

Categories