JPA modelling, one-to-one relation? - java

I am new to JPA and stuggles with defining the relations between my classes. I have a class called Player and a class called Game. A game holds references to two Player instances. The question is, how should this be modelled?
This is my current code:
#Entity
#Table(name = "t_player")
#JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
public class Player {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Basic
#Column(name = "name")
private String name;
#Basic
#Column(name = "uuid")
private final String uuid = UUID.randomUUID().toString();
I think this is ok, but my problem is in the Game class:
#Entity
#Table(name = "t_game")
#JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
public class Game {
public Game() {
}
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Basic
#Column(name = "uuid")
private final String uuid = UUID.randomUUID().toString();
#OneToOne
#PrimaryKeyJoinColumn
#JoinColumn(name = "id")
private Player player_1;
#OneToOne
#PrimaryKeyJoinColumn
#JoinColumn(name = "player_2")
private Player player_2;
public Game(Player player_1, Player player_2) {
this.player_1 = player_1;
this.player_2 = player_2;
}
}
This is not working, my table t_game only has two field; id and uuid. Where is my problem?

Remove the PrimaryKeyJoinColumn annotation, as I don't think it is what you meant to use, as it conflicts with the joincolumn definition. Use the joincolumn annotation instead to define the foreign key field name and the field it references if necessary.

Related

Many to one relationship with a condition

I have two tables, student and teacher, with a relationship ManyToOne. The table structure is as follows
student(
id long,
student_id string,
....
teacher_id string,
active boolean
)
teacher(
id long,
teacher_id string,
....
active boolean
)
I'm using Spring boot and Hibernate. Here when updating an entity, the active column of the existing row in the table will be set to false and a new row will be added with a new id(long) and active as true. That is why there are two id values in each table. The problem here is I have specified the student-teacher relation as many to one in my entity with the foreign key as teacher_id.
#Entity
#Table(name = "student")
public class Student {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "student_id")
private String studentId;
#ManyToOne
#JoinColumn(name = "teacher_id", referencedColumnName = "teacher_id")
private Teacher teacher;
#Column(name = "active")
#JsonIgnore
private Boolean active = true;
}
#Entity
#Table(name = "teacher")
public class Teacher {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "teacher_id")
private String teacherId;
#OneToMany(mappedBy = "teacher")
private Set<Student> students = new HashSet<>();
#Column(name = "active")
#JsonIgnore
private Boolean active = true;
}
But since multiple teachers can occur with the same teacher_id, this fails. Is there any way to give a condition to the relationship to fetch the teacher with active as true? In table, there will be only one teacher with the given id and active as true.
I just came across your post. I had the same requirement few months ago and this is what i did..
public class Student {
#Column(name = "teacher_id ")
private String teacherId;
#ManyToOne
#JoinFormula(value = "(Select t.id from teacher t where t.teacher_id= teacher_id and t.active=1)"
)
private Teacher teacher;
}
#Entity
#Table(name = "teacher")
public class Teacher {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "active")
#JsonIgnore
private Boolean active = true;
}
This approach works for my case. If there is a better one you can share it.
Thanks
There should be no teacher_id foreign key field in the Teacher entity. Rather, just use the primary key id column instead. Consider this version of your entities:
#Entity
#Table(name = "student")
public class Student {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "student_id")
private String studentId;
#ManyToOne
#JoinColumn(name = "teacher_id", referencedColumnName = "id")
private Teacher teacher;
#Column(name = "active")
#JsonIgnore
private Boolean active = true;
}
#Entity
#Table(name = "teacher")
public class Teacher {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(mappedBy = "teacher")
private Set<Student> students = new HashSet<>();
#Column(name = "active")
#JsonIgnore
private Boolean active = true;
}
This design should enforce that a given student can be associated with only one teacher (though a given teacher can have multiple students).

One table has two same joins in another table

I have one table called image and another table called duplicate. There are two OneToMany relations are associated.
I am not quite sure whether below implementation is the right approach for that. Moreover whether we require that second private List<DuplicateEntity> duplicateEntities2; ?
ImageEntity:
#Entity
#Table(name = "image")
public class ImageEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "filename")
private String fileName;
#OneToMany(mappedBy = "imageEntity1")
private List<DuplicateEntity> duplicateEntities1;
#OneToMany(mappedBy = "imageEntity2")
private List<DuplicateEntity> duplicateEntities2;
}
DuplicateEntity:
#Entity
#Table(name = "duplicate")
public class DuplicateEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
#JoinColumn(name = "image_a_id")
private ImageEntity imageEntity1;
#ManyToOne
#JoinColumn(name = "image_b_id")
private ImageEntity imageEntity2;
}

How to persist objects and its nested objects at once?

I'm with some problems trying to persist an object and its items, here're my classes:
#Entity(name = "Contract")
#Table(name = "contract")
public class Contract implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(unique = true, nullable = false)
private Long id;
#OneToMany(mappedBy = "idContract", cascade = CascadeType.ALL,fetch = FetchType.EAGER)
private List<ContractItem> contractItem;
//getters & setters...
}
.
#Entity(name = "ContractItem")
#Table( name = "contract_item")
public class ContractItem implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(unique = true, nullable = false)
private Long id;
#Column(name = "id_contract")
private Long idContract;
//getters & setters...
}
I'm extending JpaRepository im my repositories and using .save(contract) to persist but every time my application only persists the contract not de items, I've already tried CascadeType.ALL, MERGE and PERSIST in which either the result is the same, or I get an exception that my idContract must not be null.
Need some help here guys, thanks in advance !

How to maintain foreign key relationship in hibernate

I have two classes and I want to have a one to many relation between them, for e.g.:
Home(id<int>, rooms<string>)
Vehicle(id<int>, home_id<int>, name<string>)
I need to have a relation between Home and Vehicle class using Home.id and vehicle.home_id.
Please suggest any example which I can use here for CURD operation to implement REST service.
I need to have a relation between Home and Vehicle class using Home.id
and vehicle.home_id.
Your entities should look like this :
Vehicle Entity
#Entity
#Table(name = "vehicle", catalog = "bd_name", schema = "schema_name")
#XmlRootElement
public class Vehicle implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer id;
#Column(name = "name")
private String name;
#JoinColumn(name = "home_id", referencedColumnName = "id")
#ManyToOne
private Home homeId;
//constructor getter & setters
}
Home Entity
#Entity
#Table(name = "home", catalog = "bd_name", schema = "schema_name")
#XmlRootElement
public class Home implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer id;
#Column(name = "room")
private Character room;
#OneToMany(mappedBy = "homeId")
private List<Vehicle> vehicleList;
//constructor getter & setters
}

How to setup JPA Entity classes?

I am struggling with how to setup my JPA entity classes and which annotations should go where
I have the following tables:
Table Customer {
id: primary key,
name
}
Table CustomerDimension {
id: primary key, foreign key(Customer.id),
detail
}
Currently I have the following entity classes:
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private long id;
#Column(name = "name")
private String name;
#OneToOne
private CustomerDimension customerDimension;
}
public class CustomerDimension {
// ? what is meant to go here?
private long id;
#Column(name = "detail")
private String detail;
}
What annotation is meant to go on CustomerDimension.id to allow me to insert a new Customer that has a new CustomerDimension?
Should CustomerDimension also have a reference back to Customer?
Table Customer {
id: primary key,
name
}
Table CustomerDimension {
id: primary key,
foreign key(Customer.id),
detail
}
CustomerDimension is the owning side. so, the #OneToOne mapping should be like
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private long id;
#Column(name = "name")
private String name;
}
public class CustomerDimension {
#Id
private long id;
#Column(name = "detail")
private String detail;
#OneToOne
private Customer customer;
}
You have the following problems :
Customer and CustomerDimension need the annotation #Entity.
In your DDL, the table CustomerDimension has a foreign key on Customer. Hence, the #OneToOne relationship should be declared on CustomerDimension's side.
Still in the DDL, your foreign key does not have an explicit name. I will assume it is customer_id and use it to declare the #JoinColumn (see below)
#Column annotations are required only if you need the column to have a name which is different from the attribute's name (but you can keep them for clarity).
Here is how I would map it.
#Entity
#Table(name = "Customer") //Optional
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "name") //Optional
private String name;
}
And for CustomerDimension :
#Entity
#Table(name = "CustomerDimension") //Optional
public class CustomerDimension {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "detail") //Optional
private String detail;
#OneToOne
#JoinColumn(name = "customer_id") //NOT optional
private Customer customer
}
EDIT (answer to your comment) :
If you really want your FK to be the primary key, you can do it like this :
#Entity
#Table(name = "CustomerDimension") //Optional
public class CustomerDimension {
#Column(name = "detail") //Optional
private String detail;
#Id
#OneToOne
#JoinColumn(name = "id") //NOT optional
private Customer customer
}
I still wonder why you do not put all information in the same table. It would save you a SQL join.
What you have here is a OneToMany biidirectional relationship with a foreign key instead of a join table. A join table seems to be preferred by vendors, but it's OK.
So, you have a list (or set) of CustomerDimensions in Customer, but with the mappedBy value set.
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private long id;
#Column(name = "name")
private String name;
#OneToMany(mappedBy="customer")
List<CustomerDimensions> dimensions;
}
and
public class CustomerDimension {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private long id;
#Column(name = "detail")
private String detail;
#ManyToOne
Customer customer;
}
It's natural that Customers have a set of dimensions. By having a bidirectional mapping, if you have a dimension, then you can look up the customer easy (just reference the customer field)
EDIT: Since the CustomerDimension table has a Customer id reference, you can select many CustomerDimensions for one Customer, hence a OneToMany relationship. In order to set the CustomerDimension.customer_id field, simply put a CustomerDimension in the Customers list of dimensions.

Categories