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

use*_*454 7 hibernate

假设我有一个名为 的表,employee其字段为employee_id,namesupervisor_idemployee_supervisor字段为supervisor_idnameemployeeemployee_supervisor表相对于列存在外键关系supervisor_id

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

Jam*_*ENL 6

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

我想出的课程如下:

员工主管类:

@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;
    ....
}
Run Code Online (Sandbox Code Playgroud)

员工类别:

@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;
    ....
}
Run Code Online (Sandbox Code Playgroud)

确保你总是使用a的注解LEFT OUTER JOIN就是@Fetch(FetchMode.JOIN注解。这告诉 Hibernate 使用 a 在同一个 select 语句中加载关联的记录LEFT OUTER JOIN(有关其他类型的' 以及每种类型的作用,请参阅文档)。FetchMode

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

@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;
    ....
}
Run Code Online (Sandbox Code Playgroud)

并运行了一个非常基本的单元测试。

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());
    }
}
Run Code Online (Sandbox Code Playgroud)

我的 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();
    }
}
Run Code Online (Sandbox Code Playgroud)

SQL输出如下:

@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;
    ....
}
Run Code Online (Sandbox Code Playgroud)

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

FetchType.LAZY但是,设置@Fetch(FetchMode.JOIN)会覆盖您的延迟获取并使用连接来急切地获取您的主管。


Arp*_*wal 0

这完全取决于您的业务逻辑,意味着如果您想要拥有员工记录的employee_supervisor id是强制性的,那么使用内连接,否则使用左连接。您可以在这里阅读更多内容:左连接和内连接之间的区别