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.
Related
My tables look like this:
School
-------
school_id (pk)
...
Student
school_id (pk) (fk)
student_id (pk)
...
So using JPA (Hibernate), I tried something like this.
#Entity
#Table("SCHOOL")
public School {
#Column(name = "SCHOOL_ID")
private String schoolId;
#OneToMany
private List<Student> students;
}
#Entity
#Table("STUDENT")
public Student {
#EmbeddedId
private StudentPK studentPK;
#ManyToOne
#JoinColumn(name = "SCHOOL_ID")
private School school;
}
#Embeddable
public StudentPK implements Serializable {
#Column(name = "SCHOOL_ID")
private String schoolId;
#Column(name = "STUDENT_ID"
private String studentId;
}
When I do this, I frequently get an error that says that the foreign key constraint is being violated on runtime. I suspect that JPA is trying to generate a new foreign key called "SCHOOL_ID" instead of using the existing column from my composite key, but I'm not sure how to force it to use the existing column.
These relationships should be mapped a bit differently:
#Entity
#Table("SCHOOL")
public School {
#Column(name = "SCHOOL_ID")
private String schoolId;
#OneToMany(mappedBy="school")
private List<Student> students;
}
#Entity
#Table("STUDENT")
public Student {
#EmbeddedId
private StudentPK studentPK;
#ManyToOne
#MapsId("schoolId")
private School school;
}
#Embeddable
public StudentPK implements Serializable {
#Column(name = "SCHOOL_ID")
private String schoolId;
#Column(name = "STUDENT_ID"
private String studentId;
}
Note the #OneToMany.mappedBy attribute on School.students and #MapsId annotation on Student.school.
Derived identities are discussed (with examples) in the JPA 2.2 spec in section 2.4.1.
I have a to make a one-to-one association between two Entities, but one of them must have two #Id. One is PRI another one is MUL. How must i declare composite id, and how do i need to map the classes?
#Entity
#Table(name = "PERSONS")
public class Person implements Serializable{
private static final long serialVersionUID = -3451407520028311143L;
#Id
#Column(name = "ID")
private Integer id;
#Column(name = "ADDRESS_ID")
private Integer addressId;
#Column(name ="NAME")
private String name;
#OneToOne(mappedBy= "person", cascade= CascadeType.ALL)
private Address address;
...
}
second class is mapped via #IdClass annotation
#Entity
#Table ( name = "ADDRESS" )
#IdClass(AddressKeys.class)
public class Address implements Serializable {
#Id
#Column ( name = "ID")
private Integer id;
#Id
#Column ( name = "PERSON_ID")
private Integer idPerson;
#Column ( name = "CITY" )
private String city;
#OneToOne(cascade= CascadeType.ALL)
#JoinColumn(name="PERSON_ID")
private Person person;
...
}
and the id class
class AddressKeys implements Serializable{
private Integer id;
private Integer idPerson;
//getters and setters
#Override
public int hashCode() {
...
return result;
}
#Override
public boolean equals(Object obj) {
...
}
}
So when i try to create and save a record i have a next error
Could not open sessionRepeated column in mapping for entity:
hibernateMappedModels.base1.mappedClasses.oneToOne.Address column:
PERSON_ID (should be mapped with insert="false" update="false")
java.lang.NullPointerException at
hibernateMappedModels.base1.Main.run(Main.java:45) at
hibernateMappedModels.base1.Main.main(Main.java:24
I tryed to make an Id fields unInsertable and unUpdatable, and it was working, but i need them to be insertable and updatable; Is there any possibility to do it?
I am confused by your mappings and not sure what is required other then the simple mappings below: if I am missing something then you will need to expand on your question. You are getting the error as you have mapped the column twice - once via the one-to-one and once as a simple property. Additionally, I am not sure why you require a composite key on address.
#Entity
#Table(name = "PERSONS")
public class Person implements Serializable{
private static final long serialVersionUID = -3451407520028311143L;
#Id
#Column(name = "ID")
private Integer id;
#Column(name ="NAME")
private String name;
#OneToOne(mappedBy= "person", cascade= CascadeType.ALL)
private Address address;
}
#Entity
#Table ( name = "ADDRESS" )
public class Address implements Serializable {
#Id
#Column ( name = "ID")
private Integer id;
#Column ( name = "CITY" )
private String city;
#OneToOne(cascade= CascadeType.ALL)
#JoinColumn(name="PERSON_ID")
private Person person;
}
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.
I have two tables called Stock and stock_daily_record. Please find the table structure below
Stock
Id (primary key) Int
Name varchar
stock_daily_record
Stockid(primary key & foreign key on id of Stock)
Stock_price (Primary key)
My Entity class
#Entity #Table(name = "stock")
class Stock
{
#id #column(name=”id”)
Private int id;
#column(name=”name”)
Private String name;
#oneToMany(fetch = FetchType.LAZY)
Private Set<DailyStockRecord> dailyRecords;
//Getters and setters,equals
}
My next class DailyStockRecord contains composite key alone. How to define mapping between this two classes. Plus how to define DailyStockRecord entity?
Note:
Please don't consider my Database design cos I tried to project my actual problem through this dummy design
#Entity #Table(name = "stock_daily_record")
class StockDailyRecord
{
#id #column(name=”Stockid”)
Private int stockId;
#column(name=”Stock_price”)
Private String stockPrice;
#ManyToOne(fetch = FetchType.LAZY)
#joinColumn(name="id")
Private Stock stock;
//Getters and setters,equals
}
Try this configuration
#Entity
#Table("stock_daily_record")
public class DailyStockRecord
#EmbeddedId
private DailyStockId stockId;
#MapsId("stockId")
#ManyToOne
private Stock stock;
And the embeddable key as follows:
#Embeddedable
public class DailyStockId
private int dailyStockId;
private int stockId;
And update your code to
#Entity
#Table(name = "stock")
public class Stock
#id #column(name=”id”)
Private int id;
#column(name=”name”)
Private String name;
#OneToMany(fetch = FetchType.LAZY, mappedBy="stock" )
Private Set<DailyStockRecord> dailyRecords;
//Getters and setters,equals
}
Database
*user_account*
id (PK)
email
password
*user_detail*
id(PK)(FK)
name
city
Entities
#Table(name="user_detail")
public class UserDetail implementsSerializable{
#Id private Integer id;
...
#OneToOne
#JoinColumn(name="id")
private UserAccount userAccount;
}
#Table(name="user_account")
public class UserAccount implementsSerializable{
#Id private Integer id;
#OneToOne(mappedBy="userAccount")
private UserDetail userDetails;
}
Error
Exception Description: Multiple writable mappings exist for the field [user_detail.ID]. Only one may be defined as writable, all others must be specified read-only.
If the ID in UserAccount is both a primary key and a foreign key, then you should declare it as a single field and map it appropriately. Like this:
#Entity
public class UserAccount implements Serializable {
#Id
#OneToOne(mappedBy="userAccount")
private UserDetail userDetails;
}
Or else using #MapsId.
However, i suspect that what you really want is a single class spread over two tables:
#Entity
#Table(name = "user_account")
#SecondaryTable(name = "user_detail")
public class User implements Serializable {
#Id
private int id;
private String email;
private String password;
#Column(table = "user_detail")
private String name;
#Column(table = "user_detail")
private String city;
}
You cannot have both #Id private Integer id; and #JoinColumn(name="id"), you must remove one of them: I doubt that you really need a primary key in the details, so just remove the #Id line from there.