【问题标题】:InheritanceType.JOINED, @PrimaryKeyJoinColumn and nested subclassesInheritanceType.JOINED、@PrimaryKeyJoinColumn 和嵌套子类
【发布时间】:2014-08-05 18:17:40
【问题描述】:

我开始使用 Hibernate 有一个关于 InheritanceType.JOINED 和 @PrimaryKeyJoinColumn 的问题。

鉴于以下数据库表,其中员工引用人员和经理引用员工:

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));

我可以为 Person 和 Employee 创建前两个类,如下所示:

@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;
}

当涉及到 Manager 类时,我不确定要为 @PrimaryKeyJoinColumn 使用哪个值。通过阅读 JPA 规范,我应该在整个超类/子类/子类层次结构中只使用一个 id,我怀疑应该是 person_id。

我应该从 manager 表中删除 employee_id 列并用 person_id 替换它吗?我问的原因是在数据库中有“级联”主键似乎很自然(即经理不直接指代人),但我相信 Hibernate 要求所有子子类(无论多么“深”)都包含一个外键返回到超类。对我的断言进行一些确认会非常有用。

【问题讨论】:

    标签: java hibernate jpa


    【解决方案1】:

    您可以使用任何您想要的列名。 @PrimaryKeyJoinColumn注解用于告诉hibernate连接表中外键列的名称是什么。

    @Entity
    @Table(name = "manager")
    @PrimaryKeyJoinColumn(name = "employee_id")
    public class Manager extends Employee{
        private String branch;
    }
    

    这段代码意味着在manager 表中有一个名为employee_id 的列,它是employee 表的外键。

    此注释是可选的,因此如果您不提供它,那么外键列名称将与基类中的键名称相同(如果是多级继承,则为超级基),在您的情况下,如果您不提供那么任何子类都会有 person_id 作为外键。

    如果您想在manager 表中将字段名称保留为employee_id,那么您必须提供这样的信息。

    @PrimaryKeyJoinColumn(name="employee_id")

    如果您根本不提供此注释,那么将有一个列名person_id 用作employee 表的外键。

    文档是这样说的。

    此注解指定一个主键列,用作 外键加入另一个表。

    用于连接实体子类的主表 加入到其超类的主表的映射策略;它是 在 SecondaryTable 注释中用于将辅助表连接到 主表;它可以用于 OneToOne 映射,其中 引用实体的主键用作外键 引用的实体。

    如果没有为子类指定 PrimaryKeyJoinColumn 注释 JOINED 映射策略,外键列被假定为 与主表的主键列同名 超类

    编辑

    当我尝试通过 id 检索管理器时,Hibernate 正在执行的查询。

    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=?
    

    如您所见,经理有一个员工外键,而不是人员表。我还没有创建表。我用的是hbm2ddl=create

    这意味着 Hibernate 本身使用级联主键而不是超级父键创建表。

    我理解你的困惑,即使我在休眠documentation 中也找不到任何东西。它只是说 FK 将指向超类主键。它没有指定直接超类或顶级超类。 虽然我检查了其他 JPA 实现,即OpenJPA。它确实明确指出 FK 指向直接超类。

    【讨论】:

    • 通过使用 show_sql=true 进行一些实验,Hibernate 使用相同的 id 值连接所有表,这意味着数据库中的每个子类表都必须包含超类的 @Id。例如,Hibernate 执行“select * from person p inner join employee e on e.person_id = p.id inner join manager m on m.employee_id = p.id”。问题是最后的连接列 - 它使用的是 p.id 而不是 e.id。
    • 我自己试过了,子类中的 id 总是指向它的直接超类表。在我的情况下,经理中的 fk 指向员工而不是人员表。我用查询更新了我的答案。
    【解决方案2】:
    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();
    }
    }
    

    【讨论】:

      猜你喜欢
      • 2018-09-10
      • 1970-01-01
      • 1970-01-01
      • 2021-02-23
      • 2014-10-03
      • 2014-07-02
      • 2023-03-13
      • 2015-05-28
      • 1970-01-01
      相关资源
      最近更新 更多