Combined PK leads to duplicate column definition - java

I am new to JPA and Hibernate and I am trying to create an assignment table with an additional column position which is part of the PK which also contains a FK reference.
#Entity
#IdClass(ComponentAssignmentEntityPK.class)
public class ComponentAssignmentEntity implements Serializable {
#Id
private Integer containerID;
private Integer elementID;
#Id
private Integer position;
....
#ManyToOne
#PrimaryKeyJoinColumn(name="CONTAINERID", referencedColumnName = "ID")
public ComponentEntity getContainer() {
return container;
}
}
My Key class looks basically like this
public class ComponentAssignmentEntityPK implements Serializable {
private Integer containerID;
private Integer position;
....
}
However, if I now generate the init script using Hibernate it contains a duplicate definition for the containerid
create table ComponentAssignment (
containerID integer not null,
position integer not null,
...
elementID integer,
container_id integer not null, <===
primary key (containerID, position)
);
What am I doing wrong? I am using Hibernate 4.3.5.Final.

In your entity, you have defined two columns with same name CONTAINERID, one of them should be with different name.
As a result, when table creation script is generated, container_id is defined to make different
create table ComponentAssignment (
containerID integer not null, //refer to PK column name with #Id
position integer not null,
...
elementID integer,
container_id integer not null, //refer to column for ComponentEntity with #ManyToOne
primary key (containerID, position)
);
Assuming that it can be solved by renaming the column name for ComponentEntity like this;
#ManyToOne
#PrimaryKeyJoinColumn(name="container_id", referencedColumnName = "ID")

Related

JPA #OneToMany with composite primary keys

I need to map with JPA the following legacy DB table structure I cannot change.
It’s a one to many relationship between table ao_rda_acq (1) -> ao_rda_acq_righe (many) (purchase requisition -> purchase requisition rows)
Table (1)
create table ao_rda_acq
(
id_divisione varchar(4) not null,
esercizio smallint not null,
id_rda varchar(10) not null,
...
other fields
...
constraint pk_ao_rda_acq
primary key (id_divisione, esercizio, id_rda)
)
table many
create table ao_rda_acq_righe
(
id_divisione varchar(4) ,
esercizio smallint not null,
id_rda varchar(10) not null,
nr_riga integer not null,
...
other fields
...
constraint pk_ao_rda_righe
primary key (id_divisione, esercizio, id_rda, nr_riga),
constraint ao_rda_acq_righe_ao_rda_acq_id_divisione_esercizio_id_rda_fk
foreign key (id_divisione, esercizio, id_rda) references ao_rda_acq
)
The primary key of table ao_rda_acq side one of the relationship has 3 fields id_divisione, esercizio, id_rda. The primay key of the table side many of the relationship has the same 3 filed plus a 4th field nr_riga.
I tryed with this JPA approch using #IdClass annotation for composite primary keys
#Table(name="ao_rda_acq")
#Entity
#IdClass(RdaId.class)
#JsonIgnoreProperties(ignoreUnknown = true)
public class Rda {
#Id
public String idDivisione;
#Id
public Integer esercizio;
#Id
public String idRda;
#OneToMany(
cascade = CascadeType.ALL,
orphanRemoval = true
)
#JoinColumns({
#JoinColumn(name="id_divisione", referencedColumnName = "id_divisione"),
#JoinColumn(name="esercizio", referencedColumnName = "esercizio"),
#JoinColumn(name="id_rda", referencedColumnName = "id_rda")
})
#OrderBy("nrRiga")
public List<RdaRiga> righe = new ArrayList<>();
//Additional fields
}
where
public class RdaId implements Serializable {
String idDivisione;
Integer esercizio;
String idRda;
}
The entity for the rows is
#Entity
#JsonIgnoreProperties(ignoreUnknown = true)
#IdClass(RdaRigaId.class)
#Table(name = "ao_rda_acq_righe")
public class RdaRiga {
#Id
public String idDivisione;
#Id
public Integer esercizio;
#Id
public String idRda;
#Id
public Long nrRiga;
//More fields
}
where
public class RdaRigaId implements Serializable {
String idDivisione;
Integer esercizio;
String idRda;
Long nrRiga;
}
This code compiles but JPA at start-up complains with this message
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is org.hibernate.DuplicateMappingException: Table [ao_rda_acq_righe] contains physical column name [id_divisione] referred to by multiple logical column names: [id_divisione], [idDivisione]
Is this approch the best one to map my unhappy DB structure? If so what did I miss?
When you use #Id without the #Column annotation, the name of the column is assumed to be the name of the annotated property.
Given that your DB column seems to be *id_divisione* you need to use also the annotation #Column(name = "id_divisione").
This also applies to the other properties annotated with #Id.

JPA - Secondary Table mapping direction

I've got two tables:
CREATE TABLE Checkin (
ID int primary key,
foo varchar(100),
bar varchar(100),
FK_type int foreign key references Type(ID)
)
CREATE TABLE Type (
ID int primary key,
type varchar(100)
)
Since the secondary table only stores labels, I'd like to map the values directly into my entity. I figured it should be possible using #SecondaryTable...
#Table(name = "Checkin")
#SecondaryTable(name = "Type",
pkJoinColumns = #PrimaryKeyJoinColumn(name="FK_type", referencedColumnName = "ID")
)
class Checkin {
#Id
private Integer id;
private String foo;
private String bar;
#Column(name="FK_type", table="Type")
private String type;
}
Unforunately, it would seem that the #SecondaryTable annotation works the other way around, meaning it wants my actual primary data table with the most columns to be the one joining. So I get thrown the error
Invalid column name 'FK_type'.
Is there a way to solve that through different annotations or do I really need to build the whole construct the other way round and have the main entity refer to "Type" and "Chekin" be the secondary table?
You should join Type entity in Checkin:
class Checkin {
#Id
private Integer id;
private String foo;
private String bar;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "type_id", referencedColumnName = "id")
private Type type;
}
Try to correct this:
#Column(name="FK_type", table="Type")
private String type;
to this:
#Column(name="type", table="Type")
private String type;
The table Type just do not have the FK_type column, as I understand you want to use Type.type here.
P.S. You can omit to use referencedColumnName if this is a reference to the PK.

How to map a OneToOne with ZeroOrOneToOne back reference in JPA?

I have a legacy app which has an entity relationship that looks like this. I changed the names of the fields to less realistic values and reduced to only the relevant fields.
CREATE TABLE `billing_target` (
`billingTargetID` int(11) NOT NULL,
`targetType` char(5) NOT NULL,
`targetID` int(11) NOT NULL
PRIMARY KEY (`billingTargetID`)
);
CREATE TABLE `Client` (
`clientID` int(11) NOT NULL,
`name` varchar(200),
`color` varchar(200),
`shape` varchar(200)
PRIMARY KEY (`clientID`),
CONSTRAINT `fk_client_billingTarget`
FOREIGN KEY (`clientID`)
REFERENCES `billing_target` (`targetID`)
);
My most current attempt which causes an issue when saving as it gives a null entity key exception.
#Data
#Entity
public class BillingTarget implements Serializable {
#Id
#Column(name = "billingTargetID")
private Integer id;
#Column(name = "targetID", nullable = false)
private Integer targetID;
#Column(name = "targetType", nullable = false)
private String type;
}
#Data
#Entity
public class Client implements Serializable {
#Id
#Column(name = "clientID")
private Integer id;
#OneToOne
#JoinColumn(name = "clientID")
#MapsId("targetID")
private BillingTarget billingTarget;
private String name;
private String color;
private String shape;
}
Here's the PlantUML code if interested
#startuml
hide circle
entity BillingTarget {
* billingTargetID <<generated>>
--
* targetType
* targetID <<unique>>
}
entity Client {
* clientID <<fk>>
--
* name
color
shape
}
BillingTarget ||--o| Client : "targetID:clientID"
#enduml
I was already thinking of using a MappedSuperclass but right now it is only one type (though it could be more). Second the billing target may be zero and not null as there's a NOT NULL constraint already present.

How to define MapKey column name on Map<String, Embeddable>

Short version: I'm trying to create an:
#ElementCollection
private Map<String, Item> items;
, where Item is #Embeddable, and be able to define the column name created in Item table for the map's key. Hibernate version: 5.0.12.Final
Full explanation:
Following is a simplified example that reproduces my problem.
My starting point is a List of #Embeddable.
#Entity
public class Root {
#Id
private Long code;
private String value;
#ElementCollection
private Set<Item> items;
}
#Embeddable
public class Item {
private String keyValue;
private String otherValue;
}
with this mappings hibernate creates the following tables:
create table root (
code int8 not null,
value varchar(255),
primary key (code)
);
create table root_items (
root_code int8 not null,
key_value varchar(255),
other_value varchar(255)
);
alter table root_items
add constraint FK1o7au7f9ccr8144vyfgsr194v
foreign key (root_code)
references root;
So far so good. Next, I'm trying to convert the List to a Map and use Item.keyValue as the map's key:
#Entity
public class Root {
#Id
private Long code;
private String value;
#ElementCollection
private Map<String, Item> items;
}
#Embeddable
public class Item {
private String otherValue;
}
It works, but in root_items table the map's key column is always called items_key and I'm unable to find how to force this name to key_value.
I've tried the following alternatives:
#ElementCollection
#MapKeyJoinColumn(name = "keyValue")
private Map<String, Item> items;
/* #MapKey doesn't work at all.
org.hibernate.AnnotationException: Associated class not found: com.demo.Item
at org.hibernate.cfg.annotations.MapBinder.bindKeyFromAssociationTable(MapBinder.java:116)
*/
#ElementCollection
#MapKey(name = "keyValue")
private Map<String, Item> items;
#ElementCollection
#CollectionTable(joinColumns = #JoinColumn(name = "code"))
#MapKeyJoinColumn(name = "keyValue")
private Map<String, Item> items;
#ElementCollection
#JoinColumn(name = "keyValue")
private Map<String, Item> items;
#ElementCollection
#CollectionTable(joinColumns = #JoinColumn(name = "code"))
#MapKeyJoinColumn(name = "keyValue")
#Column(name = "keyValue")
private Map<String, Item> items;
but the column name is always generated as items_key:
create table root_items (
code int8 not null,
other_value varchar(255),
items_key varchar(255) not null,
primary key (code, items_key)
);
I've also tried all the above combinations keeping keyValue in Item but then I only get and extra column:
create table root_items (
code int8 not null,
key_value varchar(255),
other_value varchar(255),
items_key varchar(255) not null,
primary key (code, items_key)
);
Similar questions I've found but doesn't solve my problem:
#ElementCollection with Map<Entity, Embeddable> where Entity is a field of the Embeddable
Key & value column name overrides when mapping java.util.Map with JPA annotations
Is there anyway to force the column name to some custom value?
EDIT 1:
I've found that if the map key is #Embeddable then I can achieve it.
But I don't think it's a valid solution to achieve this. I would like to keep the map key as String
#Entity
public class Root {
#Id
private Long code;
private String value;
#ElementCollection
private Map<ItemKey, Item> items;
}
#Embeddable
public class ItemKey {
private String keyValue;
}
Is there anyway to force the column name to some custom value when the key is String?
Finally I found it. It's as simple as that:
#Entity
public class Root {
#Id
private Long code;
private String value;
#ElementCollection
#MapKeyColumn(name = "keyValue")
private Map<String, Item> items;
}
I leave the answer in case it may help another one as lost as I. :P

Hibernate JPA, one to one relationship with a composite key and a constant value

I'm attempting to implement a limited type of object level ACL and its lead me to a place where I'm attempting to create a #OneToOne relationship using a composite key with a constant and dynamic value.
I have an Entity with a database id and a constant value defined in the class.
public class Entity{
private static final int objectType = 1;
#Id
Integer id;
}
I have an access_levels table with a composite key of objectId and objectType.
public class AccessLevel {
#EmbeddedId
private AccessLevelKey accessLevelKey;
#Embeddable
class AccessLevelKey implements Serializable{
private Integer objectType;
private Integer objectId;
....
}
}
Schema of access_levels
CREATE TABLE access_levels(
object_type INTEGER NOT NULL,
object_id INTEGER NOT NULL,
....
CONSTRAINT access_levels_type_id PRIMARY KEY (object_type, object_id)
);
I'm attempting to come up with a one to one relationship that Entity can use to fetch and update its associated AccessLevel
After taking a look a the docs on Non-Standard Joins it seems like I need something like this,
Inside of Entity:
#OneToOne
#JoinColumns({
#JoinColumn(name = "id", referencedColumnName = "object_id"),
#JoinColumn(name = "access_levels.object_type", referencedColumnName = "1"),
})
private AccessLevel accessLevel;
However this throws a hibernate MappingException at app launch
Caused by: org.hibernate.MappingException: Unable to find column with logical name: 1 in access_levels
Thanks!

Categories

Resources