How to setup JPA Entity classes? - java

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.

Related

Springboot JPA Hibernate CascadeType confusion

How to make a column cascade enable only for insertion not update and delete.
Here are two class. There are #ManyToOne relationship between Qualification and Department. At the time of insertion I want to insert new department with qualification. But at the time of update of qualification I don't want to update department. How can I configure that??
#Table(name = "department")
public class Department {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
}
#Table(name = "qualification")
public class Qualification implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Integer id;
private Integer passingYear;
private String result;
#ManyToOne(optional = true, cascade = { CascadeType. ? }, fetch = FetchType.EAGER)
#JoinColumn(name = "department_id", referencedColumnName = "id")
private Department department;
}
CascadeType.PERSIST is what you are looking for. The various CascadeType enums essentially say, which persistence operation of EntityManager persist/merge/remove etc. should cascade to the objects of an association.

How to join column with multiple primary keys or specific primary key in jpa

#Entity
#Table(name = "A")
public class A {
#Id
#Column(name = "id")
private Long id;
#Id
#Column(name = "name")
private String name;
#JoinColumn(name = "test_id")
private List<Test> testId;
}
#Entity
#Table(name = "Test")
public class Test {
#Id
#Column(name = "test_id")
private Long testId;
}
Error Result is
" JPA trouble with OneToOne relationship: A Foreign key refering has the wrong number of column. should be 2 "
How to specific primary key for join Test table ?
Table A : column id
map with
Table B : column test_id
Since your table A has a composite key, you should separate the columns out into another key class and then join on the individual part of the key of the table.
For instance, create AKey
#Embeddable
public class AKey {
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
//getters and setters
}
Then replace the ids in class A
#Entity
#Table(name = "A")
public class A {
#EmbeddedId
private AKey key;
#JoinColumn(name = "test_id")
private List<Test> testId;
}
Then you can do a join on Test.testId = A.key.id

I have to persist a Map<EntityType, List<EntityType>> with JPA

I'm trying to persist an Entity that has a Map as one of its values. To be more precise. I have the #Entity Request that have a compound primary key with three elements. This primary key is composed by an id, the User an Map<EntityType, List<EntityType>> where the first EntityType is the selected service and the related value is the list of the items where the service will be applied to.
Below the code that I have but I'm missing the annotation that i have to use for the Map. I read online that the good way to go is the create a wrapper entity like to one that i created (SelectedService2MyItem) that holds the list and then the map is just a key-value pair between two entity but I can't make it works and I don't know how to proceed.
Does anyone can help me?
Request Entity
#Entity
public class Request {
#EmbeddedId
private RequestId id;
#Column
private String name;
#ManyToOne
#JoinColumn(name = "user_id", foreignKey = #ForeignKey(name = "FK_user_id"), nullable=false)
private User user;
//Getter, setter, constructor omitted
}
RequestId
#Embeddable
public class RequestId {
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne
#JoinColumn(name = "user_id", foreignKey = #ForeignKey(name = "FK_user_id"), nullable=false)
private User user;
private Map<ServiceOffered, SelectedService2MyItem> service2MyItem = new HashMap<ServiceOffered, SelectedService2MyItem>();
//Getter, setter, constructor omitted
}
SelectedService2MyItem
#Entity
public class SelectedService2MyItem {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id", updatable = false, nullable = false)
private Long id;
#OneToMany(mappedBy = "id")
private List<MyItem> myItemsSelected;
//Getter, setter, constructor omitted
}

How to use a foreign key within a composite primary key?

I'd like to create a composite primary key with hibernate. Usually I'd go for #IdClass.
But this time I want to use a foreign key also inside the composite primary key.
Question: is that possible at all?
Example:
#Entity
class Person {
long id;
}
class CarPK implements Serializable {
private int code;
private String name;
public CarPK(int code, String name) {
this.code = code;
this.name = name;
}
}
#Entity
#IdClass(CarPK.class)
class Car {
#Id
private int code;
#Id
private String name;
//can I also mark "person.id" with #Id?
#ManyToOne
#JoinColumn(name = "fk_person_id", foreignKey = #ForeignKey(name = "fk_person"))
private Person person; //assume car is shared
}
The person reference will show in database as fk_person_id. Is it possible to also add this column to the primary key of the car table?
So I'd be getting similar to: CONSTRAINT car_pkey PRIMARY KEY (code, name, fk_person_id)?
Update:
#ManyToOne
#JoinColumn(name = "id")
private Person person;
Results in: Property of #IdClass not found in entity path.to$Car: id
Yes, you can add the #Id to the join column, but you must use the key type in your IdClass. I'm doing exactly the same thing in my current project.
#Entity
#IdClass(MyIdClass.class)
public class MyObject {
#Id
private String key;
#Column
#Lob
private String value;
#ManyToOne(cascade = CascadeType.PERSIST)
#Id
#JoinColumn(name = "id")
private MyOtherObject otherObject;
...
and
public class MyIdClass implements Serializable {
private long otherObject;
private String key;
...
MyOtherObject.id is a long in this scenario.

JPA modelling, one-to-one relation?

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.

Categories