Reputation: 1733
I am getting started with Hibernate have a question about InheritanceType.JOINED and @PrimaryKeyJoinColumn.
Given the below database tables where employee references person and manager references employee:
create table person (person_id int(10) auto_increment,
name varchar(100),
primary key (person_id));
create table employee (employee_id int(10) auto_increment,
person_id int(10),
salary int(10),
primary key (employee_id));
create table manager (manager_id int(10) auto_increment,
employee_id int(10),
shares int(10),
primary key (manager_id));
I can create the first two classes for Person and Employee as follows:
@Entity
@Table(name="person")
@Inheritance(strategy=InheritanceType.JOINED)
public class Person {
@Id
@GeneratedValue
@Column(name="person_id")
private int personId;
@Column(name="name")
private String name;
}
@Entity
@Table(name="employee")
@PrimaryKeyJoinColumn(name="person_id")
public class Employee extends Person {
@GeneratedValue
@Column(name="employee_id")
private int employeeId;
@Column(name="salary")
private int salary;
}
When it comes to the Manager class I am not sure which value to use for the @PrimaryKeyJoinColumn. From reading the JPA spec I should be using only one id throughout the superclass/subclass/subclass hierarchy, which I suspect should be person_id.
Should I remove the employee_id column from the manager table and replace it with person_id? The reason I ask is that it seems natural to have "cascading" primary keys in the database (i.e. manager does not directly refer to person) but I believe Hibernate requires that all child subclasses (however "deep") contain a foreign key back to the superclass. Some confirmation on my assertions would be really useful.
Upvotes: 1
Views: 16306
Reputation: 1758
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;
@Entity
@Inheritance(strategy=InheritanceType.JOINED)
@Table(name="Person")
public class Person implements Serializable{
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private int id;
@Column(name="name")
private String name;
@Column(name="age")
private int age;
Person(String name,int age){
this.name=name;
this.age=age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
@Entity
@Table(name="Student")
@PrimaryKeyJoinColumn(name="person_id")
public class Student extends Person{
@Column(name="stu_className")
private String className;
@Column(name="stu_collegeName")
private String collegeName;
Student(String name, int age, String className,String collegeName) {
super(name, age);
this.className=className;
this.collegeName=collegeName;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getCollegeName() {
return collegeName;
}
public void setCollegeName(String collegeName) {
this.collegeName = collegeName;
}
}
@Entity
@Table(name="Teacher")
@PrimaryKeyJoinColumn(name="person_id")
public class Teacher extends Person{
@Column(name="qualification")
private String qualification;
@Column(name="designation")
private String designation;
Teacher(String name, int age,String qualification,String designation) {
super(name, age);
this.qualification=qualification;
this.designation=designation;
}
public String getQualification() {
return qualification;
}
public void setQualification(String qualification) {
this.qualification = qualification;
}
public String getDesignation() {
return designation;
}
public void setDesignation(String designation) {
this.designation = designation;
}
}
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
public enum HibernateUtil {
INSTANCE;
HibernateUtil(){
buildSessionFactory();
}
private SessionFactory sessionFactory=null;
public SessionFactory getSessionFactory() {
return sessionFactory;
}
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
private void buildSessionFactory() {
Configuration configuration = new Configuration();
configuration.addAnnotatedClass (Com.InheritenceType.Person.class);
configuration.addAnnotatedClass (Com.InheritenceType.Teacher.class);
configuration.addAnnotatedClass (Com.InheritenceType.Student.class);
configuration.setProperty("connection.driver_class","com.mysql.jdbc.Driver");
configuration.setProperty("hibernate.connection.url", "jdbc:mysql://localhost:3306/hibernate");
configuration.setProperty("hibernate.connection.username", "root");
configuration.setProperty("hibernate.connection.password", "root");
configuration.setProperty("dialect", "org.hibernate.dialect.MySQLDialect");
configuration.setProperty("hibernate.hbm2ddl.auto", "update");
configuration.setProperty("hibernate.show_sql", "true");
configuration.setProperty(" hibernate.connection.pool_size", "10");
configuration.setProperty(" hibernate.cache.use_second_level_cache", "true");
configuration.setProperty(" hibernate.cache.use_query_cache", "true");
configuration.setProperty(" cache.provider_class", "org.hibernate.cache.EhCacheProvider");
configuration.setProperty("hibernate.cache.region.factory_class" ,"org.hibernate.cache.ehcache.EhCacheRegionFactory");
// configuration
StandardServiceRegistryBuilder builder = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties());
sessionFactory = configuration.buildSessionFactory(builder.build());
setSessionFactory(sessionFactory);
}
public static SessionFactory getSessionFactoryInstance(){
return INSTANCE.getSessionFactory();
}
}
public class Main {
public static void main(String[] args) {
HibernateUtil util=HibernateUtil.INSTANCE;
SessionFactory factory=util.getSessionFactory();
save(factory);
retrieve(factory);
}
private static void retrieve(SessionFactory factory) {
Session session=factory.openSession();
Query query = session.createQuery("select s.id,s.name, s.age, s.qualification from Teacher s ");
Iterator sal = query.iterate();
System.out.println(" person_id \t name \t age \t qualification");
while(sal.hasNext())
{
Object[] obj = (Object[]) sal.next();
System.out.println(obj[0]+" \t "+ obj[1]+ " \t "+ obj[2]);
}
}
private static void save(SessionFactory factory) {
Session session=factory.openSession();
Student student=new Student("Deepak", 30, "MCA", "Thapar");
Teacher teacher=new Teacher("Bekaar Teachers", 60, "PHD", "Sr.professor");
session.beginTransaction();
session.save(student);
session.save(teacher);
session.getTransaction().commit();
session.close();
}
}
Upvotes: 2
Reputation: 4504
You can have the column name whatever you want.
@PrimaryKeyJoinColumn
annotation is used to tell hibernate what is the name of the foreign key column in the joining table.
@Entity
@Table(name = "manager")
@PrimaryKeyJoinColumn(name = "employee_id")
public class Manager extends Employee{
private String branch;
}
This code means that in manager
table you have a column named employee_id
which is a foreign key for employee
table.
This annotation is optional so if you don't provide it then the foreign key column name will of same as the key name in the base class(super base if multilevel inheritance), in your case if you don't provide then any subclass will have person_id
as the foreign key.
If you want to keep the field name as employee_id
in manager
table then you have to provide like this.
@PrimaryKeyJoinColumn(name="employee_id")
if you don't provide this annotation at all then there will be a column name person_id
to be used as foreign key for employee
table.
Here's what documentation says.
This annotation specifies a primary key column that is used as a foreign key to join to another table.
It is used to join the primary table of an entity subclass in the JOINED mapping strategy to the primary table of its superclass; it is used within a SecondaryTable annotation to join a secondary table to a primary table; and it may be used in a OneToOne mapping in which the primary key of the referencing entity is used as a foreign key to the referenced entity.
If no PrimaryKeyJoinColumn annotation is specified for a subclass in the JOINED mapping strategy, the foreign key columns are assumed to have the same names as the primary key columns of the primary table of the superclass
Edit
The query being executed by Hibernate when I try to retrieve manager by id.
select
manager0_.id as id1_4_0_,
manager0_2_.name as name2_4_0_,
manager0_1_.employee_id as employee1_1_0_,
manager0_1_.salary as salary2_1_0_,
manager0_.branch as branch1_2_0_
from
manager manager0_
inner join
employee manager0_1_
on manager0_.id=manager0_1_.id
inner join
person manager0_2_
on manager0_.id=manager0_2_.id
where
manager0_.id=?
As you can see manager has a foreign key of employee and not the person table. I have not created the table. I used the hbm2ddl=create
.
This means that Hibernate itself creates table using cascading primary key and not super parent key only.
I understand your confusion, even I could not find anything in the hibernate documentation. It just says that the FK will be pointing to superclass primary key. It does not specify immediate superclass or top superclass. Though I checked other JPA implementation i.e OpenJPA. It does say specifically that the FK points to immediate superclass.
Upvotes: 5