Foreign key mapping to Embeddable class - java

I am using Hibernate to join Entity1 to Entity2 where Entity2 have a composite primary key.
Entity2 contains 2 columns id and type
public class Entity2 {
#EmbeddedId
private Entity2PK id;
}
#Embeddable
public class Entity2PK implements Serializable {
#Column(name = "id")
private String id;
#Column(name = "type")
private String type;
}
Entity1 contains 2 columns id and entity_2_id (foreign key ref to Entity2)
public class Entity1 {
#Id
#Column(name = "id", updatable = false, nullable = false)
private String id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="entity_2_id")
private Entity2 entity2;
}
All the calls are failing because of this. Does anyone have a solution for this ?
Error Message:
org.hibernate.AnnotationException: A Foreign key refering Entity2 from Entity1 has the wrong number of column. should be 4
at org.hibernate.cfg.annotations.TableBinder.bindFk(TableBinder.java:646)
at org.hibernate.cfg.ToOneFkSecondPass.doSecondPass(ToOneFkSecondPass.java:102)
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processEndOfQueue(InFlightMetadataCollectorImpl.java:1814)
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processFkSecondPassesInOrder(InFlightMetadataCollectorImpl.java:1758)
at org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1646)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:286)
at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.build(MetadataBuildingProcess.java:83)
at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:473)
at org.hibernate.boot.internal.MetadataBuilderImpl.build(MetadataBuilderImpl.java:84)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:689)

You seem to have the same problem as this user: AnnotationException: A Foreign key refering has the wrong number of column. should be 2
Entity1 can not store the relationship in one single column, because Entity2 has an id with 2 columns. You can solve this by using JoinColumns instead of JoinColumn.
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumns({
#JoinColumn(name = "entity2_id", insertable = false, updatable = false),
#JoinColumn(name = "entity2_type", insertable = false, updatable = false)
})
private Entity2 entity2;

Related

Joining 2 tables with part of a Composite Primary key in JPA, Hibernate

I have an old database where can't change the schema so I have to adapt my code to work.
Within that db I have two tables which I'd like to join.
The main Table called SFTSEB2 has a 5 Column Composite primary key: empfkey, ebmsg, ebbord, ebsort, ebrefn only these will going to fulfill the uniqueness on that Table.
I'd like to join SFTSEB3 to SFTSEB2 but based on only 4 Columns: empfkey, ebmsg, ebbord, ebsort
Entites:
#Entity
#Table(name = "SFTSEB2")
#IdClass(Sftseb2ID.class)
#Immutable
public class Sftseb2 {
#Id
private String empfkey;
#Id
private String ebmsg;
#Id
private String ebbord;
#Id
private int ebsort;
private int ebbpos;
private String ebsdgn;
#Id
private String ebrefn;
#OneToMany(fetch = FetchType.LAZY)
#JoinColumns({
#JoinColumn(name = "empfkey", referencedColumnName = "empfkey", insertable = false, updatable = false),
#JoinColumn(name = "ebmsg", referencedColumnName = "ebmsg", insertable = false, updatable = false),
#JoinColumn(name = "ebbord", referencedColumnName = "ebbord", insertable = false, updatable = false),
#JoinColumn(name = "ebsort", referencedColumnName = "ebsort", insertable = false, updatable = false)
})
List<Sftseb3> sftseb3;
//GettersSetters
...
}
#Entity
#Table(name = "SFTSEB3")
#IdClass(Sftseb3ID.class)
#Immutable
public class Sftseb3 {
#Id
private String empfkey;
#Id
private String ebmsg;
#Id
private String ebbord;
#Id
private int ebsort;
#Id
private int ebsrt2;
#Id
private String ebsdgn;
private int ebbpos;
private String ebstst;
private String ebstsc;
//GettersSetters
//...
}
Unfortunately I can get past this because hibernate is giving back:
A Foreign key refering Sftseb2 from Sftseb3 has the wrong number of column. should be 5
I tried to have a bi-directional mapping so the complete JoinColumns part went to Sftseb3 with a #ManyToOne and Sftseb2 would only contain the mappedBy at the #OneToMany but I got the same issue.
As I see there is no way to have different PK on an entity and Join another table to it based on part of the PK or even other columns? Why?
If I reduce the number of #Id columns to match in the two tables so Sftseb2's #Ids: empfkey, ebmsg, ebbord, ebsort then it starts to work, however because this is non-unique all instances of Sftseb2 will be the same...

how do i create entity class for a table having primary key as foreign key?

how do i create entity for area_item_table which have foreign key as primary key
the Item entity:
#Entity
#Table(name = Item.ITEM)
public class Item {
#Id
#Column(name = ITEM_ID)
private long itemId;
#OneToOne
#JoinTable(name = "area_item_table",
joinColumns = {#JoinColumn(name = ITEM_ID, referencedColumnName = ITEM_ID)},
inverseJoinColumns = {
#JoinColumn(name = AREA_ID, referencedColumnName = ID)})
private Area area;
[removed get set and unrelated fields from entity]
}
the Area entity:
#Entity
#Table(name = Area.AREA)
public class Area {
#Id
#Column(name = ID, nullable = false)
private Long id;
[removed get set and unrelated fields from entity]
the table area_item_table :
item_id
area_id
1
121
is there a way to create an Entity for this table without creating new primary key field

JPA modeling of FK pointing to various tables

There's a table with a "FK" column whose values might point back to either TableA or TableB, something like this:
CREATE TABLE TableC (
"id" bigint GENERATED BY DEFAULT AS IDENTITY,
"linked_entity_id" integer,
"linked_entity_type" varchar(15),
...other columns
PRIMARY KEY ("id")
);
I'm struggling with representing this in JPA. I'd like to have both TableA and TableB entities in JPA to have a List. Is there a way to do this?
This is my attempt, having TableC entity modeled like this:
#Entity
public class TableC {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Long linkedEntityId;
#Enumerated(EnumType.STRING)
private LinkedEntityType linkedEntityType;
#ManyToOne
#JoinColumn(name = "linked_entity_id", referencedColumnName = "id", insertable=false, updateable=false)
private TableA tableA;
#ManyToOne
#JoinColumn(name = "linked_entity_id", referencedColumnName = "id", insertable=false, updateable=false)
private TableB tableB;
}
But, what happens when the TableC has a row whose id in the linked_entity_id column belongs to TableA id not to TableB? I think an exception would be thrown.
PS: TableA and TableB do not have anything in common.
You must use the #Where annotation:
#ManyToOne
#Where(clause = "linked_entity_type = 'TableA'")
#JoinColumn(name = "linked_entity_id", referencedColumnName = "id", insertable=false, updateable=false)
private TableA tableA;
#ManyToOne
#Where(clause = "linked_entity_type = 'TableB'")
#JoinColumn(name = "linked_entity_id", referencedColumnName = "id", insertable=false, updateable=false)
private TableB tableB;
Source: https://docs.jboss.org/hibernate/orm/current/userguide/html_single/Hibernate_User_Guide.html#pc-where

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.

Hibernate association tables

I have 2 entities that have Id's annotated but those Id's aren the primary keys in the tables. I am still mapping the PK's in to the entity for now to limit the initial impact of the change. But the association table that uses the PK's to associate the many to many relationship is throwing the following error:
java.lang.IllegalArgumentException: Provided id of the wrong type for class. Expected: class java.util.UUID, got class java.lang.Long
The entity #Id is the UUID but the table PK which is a Long is mapped as the #JoinColumn
The composite key for the association entity
#Embeddable
public class CustomerAccountId implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "user_id", nullable = false)
private Long customerId;
#Column(name = "account_id", nullable = false)
private Long accountId;
The association entity:
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name = "customerId", column = #Column(name = "user_id", nullable = false)),
#AttributeOverride(name = "accountId", column = #Column(name = "account_id", nullable = false))
})
private CustomerAccountId id;
#ManyToOne
#JoinColumn(name = "user_id", insertable = false, updatable = false, referencedColumnName = "user_id")
private Customer customer;
#ManyToOne
#JoinColumn(name = "account_id", insertable = false, updatable = false, referencedColumnName = "id")
private Account account;
The failing entity:
#Column(name = "user_id")
private Long serialId; // also the primary key in the table
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#org.hibernate.annotations.Type(type="pg-uuid")
#Column(name = "uid", updatable = false)
private UUID id;
Does anyone know if this is even possible? or am I going to be forced to update the content in the association table when I push this change?
put #Id and #GeneratedValue on the field that represent the table data then hibernate will map a long (sgbd) whit a long (table)
or
your (sgbd) table data type must be compatible with the (java) uuid type.
Why these 2 keys on your table?
I think it's not possible to have 2 PK for one entity. At most you can have a composite key base on your serialID and the UUID.
see How to map a composite key with Hibernate? for that
Or mark as #Id the real PK in the SGBD. Use the other in Java as a classic value in the table's point of view
The solution I decided to go with is the following:
#ManyToOne
#JoinColumns ( value = {
#JoinColumn(name = "user_id", insertable = false, updatable = false, referencedColumnName = "user_id"),
#JoinColumn(name = "user_uid", insertable = false, updatable = false, referencedColumnName = "uid")
})
private Customer customer;
In a nutshell I simply added the UUID to the association table and used both columns for the Customer Entity.
To address #Tokazio's question about using UUID and serial Id, the data warehouse conversion is impacted significantly so I need to slowly move from serial Id's to UUID's to minimize impacts and prevent outages.

Categories