使用@OneToOne和@OneToMany时,如何进行Hibernate OUTER JOIN

时间:2015-03-11 06:51:12

标签: hibernate

假设我有一个名为employee的表,其中包含employee_idnamesupervisor_id字段, employee_supervisor字段为supervisor_idnameemployeeemployee_supervisor列相对于supervisor_id列存在外键关系。

如果我在Employee类中执行多对一注释映射,我如何确保Hibernate使用LEFT OUTER JOIN来加入关联实体?

3 个答案:

答案 0 :(得分:2)

employee_supervisor{
      id, 
      name
}

employee{
      id, 
      name,
 ManyToOne 
      supervisor_id
}

 select supervisor FROM Employee e 
 left outer JOIN e.supervisor_id supervisor 
 WHERE e.id = :id 

答案 1 :(得分:1)

在我的测试中,Hibernate默认执行了LEFT OUTER JOIN。但是,您可以确保始终使用带注释的LEFT OUTER JOIN。我花了一些时间使用my templates作为基础的双向一对多映射来模拟您的情况,然后更改类名以符合您的情况。

我提出的课程如下:

EmployeeSupervisor类:

@Entity(name="EMPLOYEE_SUPERVISOR")
public class EmployeeSupervisor {
    @Id
    @GenericGenerator(name = "gen", strategy = "increment")
    @GeneratedValue(generator = "gen")
    @Column(name = "id")
    private int supervisorId;

    @Column
    private String name;

    @OneToMany(mappedBy = "supervisor")
    private List<Employee> employees;
    ....
}

员工类:

@Entity
public class Employee {
    @Id
    @GenericGenerator(name = "gen", strategy = "increment")
    @GeneratedValue(generator = "gen")
    @Column(name = "id")
    private int employeeId;

    @Column
    private String name;

    @ManyToOne
    @Fetch(FetchMode.JOIN)
    @JoinColumn(name = "supervisorId")
    private EmployeeSupervisor supervisor;
    ....
}

确保始终使用LEFT OUTER JOIN的注释是@Fetch(FetchMode.JOIN注释。这告诉Hibernate使用LEFT OUTER JOIN在相同的select语句中加载相关记录(对于其他类型的FetchMode,请参阅the documentation以及每个人做的事情)。< / p>

然后我在jUnit中模拟了数据库,配置了Hibernate来打印log4j中所有生成的SQL

log4j.logger.org.hibernate.SQL=DEBUG
log4j.logger.org.hibernate.type=TRACE

并进行了一次非常基本的单元测试。

public class EmployeeDAOTest extends SpringTest{

    @Autowired
    private EmployeeDAO dao;

    private Employee testLinkedEmployee;
    private Employee testUnlinkedEmployee;
    private EmployeeSupervisor testSupervisor;

    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
        System.out.println("Starting DAO Test");
    }

    @AfterClass
    public static void tearDownAfterClass() throws Exception {
        System.out.println("Finished DAO Test");
    }

    @Before
    public void setUp() throws Exception {
        //Irrelevant horrible setup code snipped.
        /* Set up 2 employees and a EmployeeSupervisor in the database.
         * Link one employee to the EmployeeSupervisor and not the other
         */
    }

    @Test
    @Transactional
    public void test() {
        Employee actualLinkedEmployee = dao.getEmployee(testLinkedEmployee.getEmployeeId());
        Employee actualUnlinkedEmployee = dao.getEmployee(testUnlinkedEmployee.getEmployeeId());

        assertNotNull("The linked employee's supervisor didn't get selected.", actualLinkedEmployee.getSupervisor());
        assertNull("The unlinked employee's supervisor was not null.", actualUnlinkedEmployee.getSupervisor());
    }
}

我的DAO非常简陋:

@Repository
public class EmployeeDAOImpl implements EmployeeDAO {

    @Autowired
    private SessionFactory sessionFactory;

    @Override
    public Employee getEmployee(int id) {
        Criteria query = sessionFactory.getCurrentSession().createCriteria(Employee.class);
        query.add(Restrictions.idEq(id));
        return (Employee) query.uniqueResult();
    }
}

,SQL输出如下:

[junit]     /* criteria query */ select
[junit]         this_.id as id1_1_1_,
[junit]         this_.name as name2_1_1_,
[junit]         this_.supervisorId as supervis3_1_1_,
[junit]         employeesu2_.id as id1_0_0_,
[junit]         employeesu2_.name as name2_0_0_ 
[junit]     from
[junit]         Employee this_ 
[junit]     left outer join
[junit]         EMPLOYEE_SUPERVISOR employeesu2_ 
[junit]             on this_.supervisorId=employeesu2_.id 
[junit]     where
[junit]         this_.id = ? 01:23:54:0.668 
[junit] binding parameter [1] as [INTEGER] - 5 01:23:54:0.671 

[junit]     /* criteria query */ select
[junit]         this_.id as id1_1_1_,
[junit]         this_.name as name2_1_1_,
[junit]         this_.supervisorId as supervis3_1_1_,
[junit]         employeesu2_.id as id1_0_0_,
[junit]         employeesu2_.name as name2_0_0_ 
[junit]     from
[junit]         Employee this_ 
[junit]     left outer join
[junit]         EMPLOYEE_SUPERVISOR employeesu2_ 
[junit]             on this_.supervisorId=employeesu2_.id 
[junit]     where
[junit]         this_.id = ? 01:23:54:0.704 
[junit] binding parameter [1] as [INTEGER] - 6 01:23:54:0.704 

应该注意的是,默认情况下,Hibernate似乎急切地获取这些实体并使用LEFT OUTER JOIN来执行此操作。但是,如果您尝试将默认提取类型设置为FetchType.LAZY,则连接类型将更改为FetchMode.SELECT,并且只为该员工发出一个选择,而不会选择主管。

然后使用FetchType.LAZY设置@Fetch(FetchMode.JOIN),覆盖您的延迟抓取并使用联接来急切地获取您的主管。

答案 2 :(得分:0)

这完全取决于您的业务逻辑,意味着如果您希望拥有员工记录的employee_supervisor ID是强制性的,那么使用内部联接其他用于左联接。您可以在此处阅读更多内容:Difference between Left and Inner join