Hibernate Referencing Embedded ID columns in FK - java

I'm having a problem mapping multiple #EmbeddedId columns of different entity classes. I have an entity structure like the following (have simplified the entities for readability).
Entity: Action
#Entity
#AttributeOverrides({ #AttributeOverride(name="pk.method", column=#Column(name = "http_method", nullable = false)) })
#AssociationOverrides({ #AssociationOverride(name = "pk.operation", joinColumns = #JoinColumn(name = "id_operation", nullable = false)) })
public class Action {
#EmbeddedId
private ActionId pk = new ActionId();
}
This entity has an #EmbeddedId defined as the following:
#Embeddable
private class ActionId implements Serializable {
#ManyToOne private Operation operation;
private HttpMethod method;
}
The mappings here between the pk columns work fine. The problems come when I attempt to add another entity which is a link table to the Action entity. This link table also has an #EmbeddedId.
Entity: UserAction
#Entity
#AssociationOverrides .. ?
public class UserAction {
#EmbeddedId private UserActionId pk = new UserActionId();
}
The #EmbeddedId class looks like:
#Embeddable
private class UserActionId implements Serializable {
#ManyToOne private Action action;
#ManyToOne private User user;
}
How to I reference the #EmbeddedId fields of the Action class, but only one of the columns is a join column. The other column in the ActionId class is a regular #Column.
I have tried this:
#AssociationOverrides({
#AssociationOverride(name = "pk.action.pk.operation", joinColumns = #JoinColumn(name = "id_operation", nullable = false)) })
But this is giving me an error:
Caused by: org.hibernate.MappingException: Foreign key (FK_gwvslpytxm695kdw5lxqneyss:user_action [useraction])) must have same number of columns as the referenced primary key (useraction[id_operation,http_method])
at org.hibernate.mapping.ForeignKey.alignColumns(ForeignKey.java:110)
at org.hibernate.mapping.ForeignKey.alignColumns(ForeignKey.java:93)
at org.hibernate.cfg.Configuration.secondPassCompileForeignKeys(Configuration.java:1816)
at org.hibernate.cfg.Configuration.originalSecondPassCompile(Configuration.java:1739)
at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1424)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1844)
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:850)
How do i override the other non-join column method association of the ActionId class in the UserAction entity?

Related

Entity Mapping with One to One and One to Many Java/JPA/Hibernate

Need help in entity mapping. I have Debtor entity and it has one to many mapping with Addresses entity.
I also have different address types and Debtor should have one to one mapping with each address type.
Each address type is a subclass of Addresses. While running test case I am getting below error
"Provided id of the wrong type for class inquiry.entity.CurrentAddress. Expected: class java.lang.Integer, got class
java.lang.String; nested exception is java.lang.IllegalArgumentException:"
Can anyone help, how to map the entities.
#Entity
#Table(name="debtors")
public class Debtor{
#Id
#Column(name = "id" , length = 127)
private String id;
#OneToOne
#JoinColumn(name = "id", referencedColumnName = "parent_id")
private CurrentAddressStd currAddrStd;
#OneToOne
#JoinColumn(name = "id", referencedColumnName = "parent_id")
private CurrentAddress currAddr;
#OneToMany
#JoinColumn(name = "parent_id")
private List<Addresses> addresses;
}
#Entity
#Table(name="addresses")
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(discriminatorType = DiscriminatorType.STRING, name = "type")
public class Addresses{
#Id
#GeneratedValue(strategy= GenerationType.SEQUENCE)
private int id;
#Any(metaColumn = #Column(name = "parent_type"))
#AnyMetaDef(idType = "string", metaType = "string",
metaValues = {
#MetaValue(targetEntity = Contact.class, value = "Contact"),
#MetaValue(targetEntity = Debtors.class, value = "Debtor"),
})
#JoinColumn(name = "parent_id")
public Object parentItem;
}
#Entity
#DiscriminatorValue("CurrAddrStd")
public class CurrentAddressStd extends Addresses{
}
public interface AddressesRepository extends CrudRepository<Addresses,Integer> {
}
In your Debtor class you have this:
#OneToOne
#JoinColumn(name = "id", referencedColumnName = "parent_id")
private CurrentAddress currAddr;
The #OneToOne is telling Hibernate that the foreign key is found in the Debtor class itself, with column name "id", which is a String. In your CurrentAddress class, which is mapped back to Addresses, you have a #Id field, which is an int. Hibernate can't match an int Primary Key with a String Foreign Key, which results in the error you are getting.
Taken from here for the definition of the name attribute of the JoinColumn annotation:
(Optional) The name of the foreign key column. The table in which it is found depends upon the context.
If the join is for a OneToOne or ManyToOne mapping using a foreign key
mapping strategy, the foreign key column is in the table of the source
entity or embeddable.

Spring Data. Repository returns wrong Id (entity data is correct)

Good day, here is problem:
CrudRepository returns wrong Id for entities.
Here is base JPA user entity:
#Data
#Entity(name = "user")
#EqualsAndHashCode(exclude = {"roles", "password", "data"})
#ToString(exclude = {"roles", "password", "data"})
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToOne(mappedBy = "user", fetch = FetchType.LAZY)
private SomeData data;
...
There is a relation one-to-one to some data entity.
#Data
#Entity(name = "some_data")
#TypeDefs({
#TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
})
#ToString(exclude = {"user", "views", "biData"})
#EqualsAndHashCode(exclude = {"user", "views", "biData"})
public class SomeData {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToOne
#MapsId
private User user;
...
And there is crud repository:
#Repository
public interface SomeDataRepository extends CrudRepository<SomeData, Long> {
Optional<SomeData> findByUserId(Long userId);
}
Method findUserById returns correct SomeData entity from DB, but this entity has the same ID with userId...
And because of it I can't do other activities (insert or update on table "public_view" violates foreign key constraint "fk_view_to_some_data")
It's quite strange.
The problem could be because you use the #MapsId annotation. Here's what the value of the annotation does, as per Javadoc:
The name of the attribute within the composite key to which the relationship attribute corresponds. If not supplied, the relationship maps the entity's primary key.
You could try to set a specific value to your annotation, or map differently your one-to-one relationship. For example, use the #JoinColumn annotation in your SomeData class:
// ... your annotations ...
public class SomeData {
// ... your other fields ...
#OneToOne
#JoinColumn(name = "some_data_id", referencedColumnName = "id")
private User user;
}
Here are some alternatives that you could use: https://www.baeldung.com/jpa-one-to-one

Hibernate is multiple #EmbeddedId supported?

Is it possible to have multiple #EmbeddedId composite keys defined for a #ManyToOne relationship between classes? Example:
Entity 1
#Entity
#AttributeOverrides({ #AttributeOverride(name="pk.method", column=#Column(name = "http_method", nullable = false)) })
#AssociationOverrides({ #AssociationOverride(name = "pk.operation", joinColumns = #JoinColumn(name = "id_operation", nullable = false)) })
public class Action {
#EmbeddedId
private ActionId pk = new ActionId();
}
#Embeddable
private class ActionId implements Serializable {
#ManyToOne private Operation operation;
private HttpMethod method;
}
Entity 2:
#Entity
#AssociationOverrides
// Need to override the embedded if attributes of the Action entity inside the UserActionId class
public class UserAction {
#EmbeddedId private UserActionId pk = new UserActionId();
}
#Embeddable
private class UserActionId implements Serializable {
// How to provide a #JoinColumn for the Action.pk composite key (action.pk.authURL) and #Column for (action.pk.httpMethod)
#ManyToOne private Action action;
#ManyToOne private User user;
}
Is this possible? I have tried but keep getting a Hibernate error saying that the FK must have the same number of columns as the ActionId pk.

JPA mapping annotation error org.hibernate.MappingException: Foreign key must have same number of columns as the referenced primary key

I can't propper map DB tables with JPA annotation.
Tables Subject and Place is ManyToMany through JoinTable.
Subject.java
#Entity
#Table(name = "SUBJECT")
public class Subject implements Serializable {
#Id
#Column(name = "SID")
private Integer sid;
#Column(name = "NAME")
private String name;
// getters and setters
}
SubjectPlace.java
#Entity
#Table(name = "SUBJECT_PLACE")
public class SubjectPlace implements Serializable {
#Id
#Column(name = "SPID")
private Integer spid;
#ManyToOne
#JoinColumn(name = "SUB_KEY") //Subject FK
private Subject subject;
#ManyToOne
#JoinColumn(name = "PLC_KEY") //Place FK
private Place place;
// getters and setters
}
Place.java
#Entity
#Table(name = "PLACE")
public class Place implements Serializable {
#Id
#Column(name = "PID")
private Integer pid;
#Column(name = "NAME")
private String name;
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
#JoinTable(name = "SUBJECT_PLACE",
joinColumns = { #JoinColumn(name = "PLC_KEY", nullable = false, updatable = false) },
inverseJoinColumns = { #JoinColumn(name = "SUB_KEY", nullable = false, updatable = false) })
private Set<Subject> subjects;
// getters and setters
}
But than I need to link Person with Subject in selected Places. I mean that each Place has its own collection of Subject. And a Person have link to Subject whitch resides in particular Place.
like This:
Subject (M) -- (M) Place through JoinTable Subject (1) -- (M) Subject_Place (M) -- (1) Place
Person (M) -- (M) Subject_Place through JoinTable Person (1) -- (M) Person_Subject_Place (M) -- (1) Subject_Place
Person.java
#Entity
#Table(name = "PERSON")
public class Person implements Serializable {
#Id
#Column(name = "PRSID")
private Integer prsid;
#Column(name = "NAME")
private String name;
// How to annotate this code?
// I experience problem in this part of code
#OneToMany
#JoinColumn(name="SPID_KEY")
private List<SubjectPlace> subjectPlaces;
// getters and setters
}
PersonSubjectPlace.java
#Entity
#Table(name = "PERSON_SUBJECT_PLACE")
public class PersonSubjectPlace implements Serializable {
#Id
#Column(name = "PSPID") // Person_Subject_Place ID
private Integer pspid;
#ManyToOne
#JoinColumn(name = "PER_KEY") //Person FK
private Person person;
// How to annotate this code?
// I experience problem in this part of code
#ManyToOne
#JoinColumn(name = "SPID_KEY") //Subject_Place FK
private SubjectPlace subjectPlace;
// getters and setters
}
And when I try so get Persons and its Subjects, I get this error:
Caused by: org.hibernate.MappingException: Foreign key (FK2C3B79384AABC975:PERSON_SUBJECT_PLACE [SPID_KEY])) must have same number of columns as the referenced primary key (SUBJECT_PLACE [PLC_KEY,SUB_KEY])
What, How shoul I map?
In your OneToMany mapping you don't need to specify the foreign key, you just need to use mappedBy property to refer your mapping object, you can learn more about it in OneToMany Mapping Documentation, and here's what you need to map Person and PersonSubjectPlace entities:
In your Person class:
#OneToMany(mappedBy="person")
private List<PersonSubjectPlace> personsubjectPlaces;
In your PersonSubjectPlace class:
#ManyToOne
#JoinColumn(name="PRSID") //Specify the primary key of Person
private Person person;
For further information about the difference between JoinColumn and mappedBy you can take a look at this answer.
EDIT:
For the mapping between SubjectPlace and PersonSubjectPlace:
In your SubjectPlace class:
#OneToMany(mappedBy="subjectPlace")
private List<PersonSubjectPlace> personsubjectPlaces;
In your PersonSubjectPlace class:
#ManyToOne
#JoinColumn(name="SPID") //Specify the primary key of SubjectPerson
private SubjectPlace subjectPlace;
Note:
The best approach to map those classes is to use #JoinTable between Person and SubjectPlace, take a look at this #JoinTable example, because PersonSubjectPlace is pratically an asociation-entity between Person and SubjectPlace.
You should remove #Joincolumn annotation and add mappedBy variable to #OneToMany annotation.
#OneToMany(mappedBy = "spid")
You should have a variable in SubjectPlace that has a Person where you should put #JoinColumn annotation

Joining to a certain property of a composite primary key in hibernate

I'm using hibernate, and i have two tables (legacy, they cant be restructured) like the following
Table A
#Entity
#Table(name = "tableA")
public Class TableA implements Serializable {
#EmbeddedId private TableAId tableAId;
#OneToOne(mappedBy = "tableA")
private TableB tableB;
// getters, setters, hashCode and equals ommited
}
Composite Id of Table A
#Embeddable
public class TableAId implements Serializable {
protected int id1;
protected String id2;
protected int id3;
// getters, setters, hashCode and equals ommited
}
Table B
#Entity
public class TableB implements Serializable {
#OneToOne
#JoinColumn(name = "tableB", referencedColumnName = "id3")
protected TableA tableA;
// getters, setters, hashCode and equals ommited
}
As can be seen on above code, i need that table B to join table A using one or more (not all) properties of the composite key of the table. I already 've tried with no success, with these three approaches:
Exactly like code above.
Changing referencedColumnName value like this
referencedColumName = "tableAId.id3"
Adding properties that compound the composite key also in the pojo entity of TableA like this:
// TableA
#Column(name = "id1", insertable = false, updatable = false)
private Integer id1;
I if this is even posible??, if it is any help will be appreciated!!
I think the JPA annotation #MapsId might work for you here:
http://docs.oracle.com/javaee/6/api/javax/persistence/MapsId.html
In TableA class, try this for the tableB property:
#MapsId("id3")
#OneToOne(mappedBy = "tableA")
private TableB tableB;
Remove the #JoinColumn annotation from the tableA property in TableB class as well.

Categories