JPA OneToOne unidirectional mapping - java

I have 2 entities : Field and ViewOptions
fields and methods not related to the problem are omitted
Table structure:
CREATE TABLE FIELD (
ID INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY (ID),
);
CREATE TABLE VIEW_OPTIONS (
ID INT NOT NULL AUTO_INCREMENT,
FIELD_ID INT NOT NULL,
PRIMARY KEY (ID),
INDEX (FIELD_ID ASC),
CONSTRAINT
FOREIGN KEY (FIELD_ID)
REFERENCES FIELD (ID)
ON DELETE NO ACTION
ON UPDATE NO ACTION);
Mapping:
#Table(name = "FIELD")
#Entity
public class Field {
#OneToOne(mappedBy ="field")
ViewOptions viewOptions;
#Table(name = "VIEW_OPTIONS")
#Entity
public class ViewOptions {
#OneToOne
#JoinColumn(name = "FIELD_ID")
Field field;
In this relation the owner side is ViewOptions and the inverse side is Field.
What i want to do is to swap sides - make Field the owner side.
But if i mark viewOptions like this
#OneToOne
#JoinColumn(name = "FIELD_ID")
ViewOptions viewOptions;
Hibernate expecting join column in Field table.
Is there any way to tell hibernate to search for join column in VIEW_OPTIONS?

No, that's just not how it works. If you want the Field object to be the owner of the relationship, then the FIELD table will have to have a column for the VIEW_OPTIONS id.

In a bidirectional relationship, one of the sides (and only one) has to be the owner: the owner is responsible for the association column(s) update. To declare a side as not responsible for the relationship, the attribute mappedBy is used. mappedBy refers to the property name of the association on the owner side.
The join column annotation here is optional an if no #JoinColumn is declared on the owner side, the defaults apply. A join column(s) will be created in the owner table and its name will be the concatenation of the name of the relationship in the owner side, _ (underscore), and the name of the primary key column(s) in the owned side.
So, if you want to make Field the owner side :
#Table(name = "FIELD")
#Entity
public class Field {
#OneToOne
ViewOptions viewOptions;
#Table(name = "VIEW_OPTIONS")
#Entity
public class ViewOptions {
#OneToOne(mappedBy ="viewOptions")
Field field;

Related

fk column created in child entity with ManyToOne in parent for InheritanceType.JOINED

I'm having a problem with Hibernate entities inheritance where it creates a copy of fk column of a many-to-one relation without updating it.
#Data
#Entity
public class Vehicle {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(mappedBy = "vehicle")
private Set<Human> owner;
}
#Data
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Integer age;
#ManyToOne
private Vehicle vehicle;
}
#Data
#Entity
public class Human extends Person {
private String fullName;
}
With this entities it creates:
create table human (
full_name varchar(255),
id bigint not null,
vehicle_id bigint,
primary key (id)
) engine=InnoDB
create table person (
id bigint not null auto_increment,
age integer,
vehicle_id bigint,
primary key (id)
) engine=InnoDB
create table vehicle (
id bigint not null auto_increment,
primary key (id)
) engine=InnoDB
alter table human
add constraint FKqctp8ycdo4mm7fsou0v4jalx3
foreign key (id)
references person (id)
alter table person
add constraint FKgo297ke05qjubymwq6unmcnot
foreign key (vehicle_id)
references vehicle (id)
Why it declares Human.vehicle_id? I don't need it and hibernate dosn't sync it with Person.vehicle_id. The worst part is when hibernate Join Human with Vehicle it check Human.vehicle_id = Vehicle.id
How can I resolve this issue? I can't change InheritanceType because it will be huge migration.
Test case:
final var car = Vehicle.builder().build();
vehiclesRepo.save(car);
final var mario = Human.builder()
.fullName("Mario Rossi")
.age(20)
.vehicle(car)
.build();
humansRepo.save(mario);
Vehicle
id|
--+
1|
Person
id|age|vehicle_id|
--+---+----------+
2| 20| 1|
Human
full_name |id|vehicle_id|
-----------+--+----------+
Mario Rossi| 2| |
-- generated query for findAll
select
human0_.id as id1_1_,
human0_1_.age as age2_1_,
human0_1_.vehicle_id as vehicle_3_1_,
human0_.full_name as full_nam1_0_
from
human human0_
inner join
person human0_1_
on human0_.id=human0_1_.id
select
vehicle0_.id as id1_2_0_
from
vehicle vehicle0_
where
vehicle0_.id=?
select
owner0_.vehicle_id as vehicle_3_0_0_,
owner0_.id as id2_0_0_,
owner0_.id as id1_1_1_,
owner0_1_.age as age2_1_1_,
owner0_1_.vehicle_id as vehicle_3_1_1_,
owner0_.full_name as full_nam1_0_1_,
vehicle1_.id as id1_2_2_
from
human owner0_
inner join
person owner0_1_
on owner0_.id=owner0_1_.id
left outer join
vehicle vehicle1_
on owner0_1_.vehicle_id=vehicle1_.id
where
owner0_.vehicle_id=?
owner0_.vehicle_id=? here is where it fails the join because vehicle_id in human table is never populated, in this test scenario it's a bit better than real one because it actually made the join right and fails only in where conditions, in real use case it just use on 1=1 in join.
There is a discrepancy in database/system design. If only Humans can be Vehicle owners - you need to move Vehicle field into Human class (it solves your issue). Or (in case all Persons can do it) - you need to change Set{Human} to Set{Person} (it solves the issue too)
To avoid the vehicle_id in children tables the association in Vehicle must use Person as base type explicitly in field type or using OneToMany.targetEntity
This resolve my problem partially because it added Robot amoung owners, I don't need that. I can use a discriminator column with a #Where condition or leave children vehicle_id and sync it programatically.

SpringBoot - Entity propagation for relationships

I have two entities: WorkoutTemplate and ConcreteExercise.
In WorkoutTemplate I have this relationship with ConcreteExercises
#OneToMany(cascade = CascadeType.ALL, mappedBy = "belongingWorkout", fetch = FetchType.LAZY)
private List<ConcreteExercise> concreteExercises;
And in ConcreteExercise I have this relationship with WorkoutTemplate
#ManyToOne
private WorkoutTemplate belongingWorkout;
I would like to insert a WorkoutTemplate into my database...
I make the request to the controller sending a Json like this:
{ "workoutName" : "My Workout",
concreteExercises: [
{
"name" : "Squat"
}
]
}
The DAO insert into my DB the WorkoutTemplate
And insert also in the table of the ConcreteExercise the name.
But not the reference to the WorkoutTemplate...
Practically, the table ConcreteExercise is made of:
id, name, belongin_workout_id
With the request above, we populate the id (auto-increment) and the name, but not the foreign key to the WorkoutTemplate.
How can I solve this ?
I would like to automatically insert the foreign key without sending it in the request or doing it manually into the service
Hi there it's because you are not using #JoinColumn which marks a column for as a join column for an entity association or an element collection.
On your WorkoutTemplate entity - you can retain this:
#OneToMany(cascade = CascadeType.ALL, mappedBy = "belongingWorkout", fetch = FetchType.LAZY)
private List<ConcreteExercise> concreteExercises;
But on your ConcreteExercise entity - you need to have this change:
#ManyToOne
#JoinColumn(name = "workout_template_id", nullable = false)
private WorkoutTemplate belongingWorkout;
The above code will create a foreign key linking the ConcreteExercise entity with the primary key from the WorkoutTemplate entity. The name of the foreign key column in the ConcreteExercise entity is specified by name property which for this case is workout_template_id - feel free to change this.
If you have set this up but still not working, it will also be helpful to share a code snippet on how are you saving these.

Hibernate #SecondaryTable - Specifying foreign key of primary table

I have two tables:
language
CREATE TABLE language (
id BIGSERIAL,
name TEXT NOT NULL UNIQUE,
PRIMARY KEY (id)
);
translation
CREATE TABLE translation (
id BIGSERIAL,
language_id BIGINT REFERENCES language (id),
translation_key TEXT NOT NULL,
translation_value TEXT NOT NULL,
PRIMARY KEY (id)
);
And I would like to get such entity, where translation table (primary table) joins language table by language_id from primary table. Problem: at the moment it joins by translation PK(id).
#Entity
#Table(name = "translation")
#SecondaryTable(name = "language", pkJoinColumns = #PrimaryKeyJoinColumn(name = "id"))
public class Translation {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(table = "language", name = "name")
// ON translation.language_id = language.id
private String language;
#Column(name = "translation_key")
private String translationKey;
#Column(name = "translation_value")
private String translationValue;
// getters and setters
}
Where I should specify it in my code to do it correctly?
SQL example: SELECT t.id, l.name, translation_key, translation_value FROM translation t INNER JOIN language l on t.language_id = l.id;
You cannot use #SecondaryTable for the purpose you describe.
#SecondaryTable is used when a single entity is spread across multiple tables. Each of these 'pieces' of the entity must be privately owned by the entity, and is in a one-to-one relation with every other 'piece'.
If you want a many-to-one relation between translations and languages, you need to use #ManyToOne (and create a separate Language entity) instead.
#SecondaryTable(name = "language")
this way it is going to generate value for the translation id and insert it to the language foreign key automatically if you specify pkJoinColumn it is going to relate the tables through the primary key while if you don't mention that, it would do it through the foreign key. After that you need to create a trigger and sequence for the language table id column. It should work.

ManyToMany Relation in JPA with Index possible?

I want to have a many to many relation between to Entities and I'm using a junction table for it right now on the MySql end. Now I need a JPA solution like an index which allows me to use the id's of both of those tables/ entities as a Key/ Index without the Entities itself to avoid some cross package references. When querying I have the ID of EntityA and want to find the ID of EntityB with it, nothing more. This is how I thought it might work:
(It doesn't because I don't have IDs for the JunctionEntity and if I use IDs then obviously the entries have to be unique when the only unique Thing should be both entries together. A PK Class isn't working either since it would still require said references to both entities)
EntityA:
#Entity
#Table(name = "EntityA")
})
public class EntityA {
#Id
private int id;
}
EntityB:
#Entity
#Table(name = "EntityB")
})
public class EntityB {
#Id
private int id;
}
JunctionEntity:
#Entity
#Table(name = "junction", indexes = {
#Index(name = "ix_a_b", columnList = "a_id, b_id")
})
public class JunctionEntity {
private int a_id;
private int b_id;
}
MySQL for JunctionTable:
CREATE TABLE junction (
a_id INT NOT NULL,
b_id INT NOT NULL,
CONSTRAINT junction_fk_a FOREIGN KEY (a_id) REFERENCES entityA (id),
CONSTRAINT junction_fk_b FOREIGN KEY (b_id) REFERENCES entityB (id)
);
CREATE UNIQUE INDEX ix_a_b
ON junction (a_id, b_id);
You can add a primary key and search in junction table using a_id, b_id or both.
CREATE TABLE junction (
id INT NOT NULL,
a_id INT NOT NULL,
b_id INT NOT NULL,
CONSTRAINT junction_fk_a FOREIGN KEY (a_id) REFERENCES entityA (id),
CONSTRAINT junction_fk_b FOREIGN KEY (b_id) REFERENCES entityB (id)
);
You don't need to know the id of junction table. You can query the junction like this:
select b_id from junction where a_id = ?;

Hibernate Exception: Missing Column (column exists)

Okay, so within the database we have a table called distributionCompanies, created like so:
CREATE TABLE `distributionCompanies` (
`distributionCompanyID` INT(11) NOT NULL,
`distributionCompanyName` VARCHAR(255) NOT NULL,
PRIMARY KEY (distributionCompanyID)
);
I'm trying to map this table to a class using Hibernate:
#Entity
#Table(name = "distributionCompanies")
public class DistributionCompany implements DatabaseObject {
#Id
#GeneratedValue
#Column(name = "distributionCompanyID", length = 11, unique = true, nullable = false)
private int distributionCompanyID;
....
However, when running, I hit this issue:
Initial SessionFactory creation failedorg.hibernate.HibernateException: Missing column: distributionCompanyID_distributionCompanyID in database2.distributionCompanies
This isn't the only table in the database, and I've managed to map other classes successfully using the same method, so I'm a little stumped as to why this is causing an issue.
Thank you for your time,
Samuel Smith
EDIT: In response to Xavi's comment, I temporarily removed another mapping for the column, and the error went away, so the bad-egg probably lays in the following code:
#ManyToOne(targetEntity = DistributionCompany.class)
#JoinTable(name = "distributionCompanies", joinColumns = { #JoinColumn(name = "distributionCompanyID", nullable = false) })
private int distributionCompanyID;
Hibernate is looking for a column named distributionCompanyID_distributionCompanyID in your distributionCompanies table.
This is probably due to a ToOne association mapping towards this table without #JoinColum.
From Hibernate Documentation:
The #JoinColumn attribute is optional, the default value(s) is like in one to one, the concatenation of the name of the relationship in the owner side, _ (underscore), and the name of the primary key column in the owned side. In this example company_id because the property name is company and the column id of Company is id.
If you've got a #ManyToOne or #OneToOne association mapping in another entity, this would explain why Hibernate is looking for such a column.
EDIT Seeing the association mapping you posted, it looks like it should be:
#ManyToOne(targetEntity = DistributionCompany.class)
#JoinColumn(name = "distributionCompanyID")
private DistributionCompany distributionCompany;
The #JoinTable annotation is used to specify a join table (that means an intermediate table used to model many-to-many associations). And the point of mapping an association would be to dispose of the mapped object instance (in this case a DistributionCompany, not just a distributionCompanyId).

Categories

Resources