因此,我现在正在研究一个RESTful API,该API充当客户端和我们的数据库之间的一个层,我目前正在将Hibernate用于ORM,并且我已经尝试了好几天了,以找到解决问题的方法,到处都无济于事。
我不能发布我正在使用的实际代码,因为它太大了,但请想象一下:
我有一个类Employee
映射为如下所示的Hibernate实体
@Entity
public class Employee {
@Id
Integer id;
String name;
Double salary;
String department;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "company_id")
Company company;
// Getters/Setters ommitted
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", name='" + name + '\'' +
", salary=" + salary +
", department='" + department + '\'' +
", company=" + company +
'}';
}
}
另一个实体类Company
如下所示:
@Entity
public class Company {
@Id
Integer id;
String name;
String address;
@OneToMany(mappedBy = "company", fetch = FetchType.LAZY)
List<Employee> employees;
// Getters and setters ommited
@Override
public String toString() {
return "Company{" +
"id=" + id +
", name='" + name + '\'' +
", address='" + address + '\'' +
", employees=" + employees +
'}';
}
}
数据库看起来像这样:
Employee:
| id | name | salary | department | company_id |
|----|----------|--------|------------|------------|
| 1 | john doe | 3000 | Finance | 1 |
| 2 | mary sue | 3300 | HR | 1 |
Company:
| id | name | address |
|----|------|------------|
| 1 | ACME | St. Sesame |
现在可以说我想从某个员工那里获取数据,如果以后需要公司数据,我只希望员工数据+公司ID(也只有ID)作为参考。看起来像这样:
Session session = SessionManager.openSession();
Employee emp = session.get(Employee.class, 1);
session.close();
现在我想显示员工数据
System.out.println(emp);
这里期望的输出(或至少是我期望的)是这样的:
Employee{id=1,name='john doe',salary=3000.0,department='Finance',company=Company{id=1,name=null,address=null,employees=null}}
这很简单,问题在于它将抛出LazyInitializationException,因为在Employee的toString()
调用中,Hibernate尝试使用代理对象来获取公司的数据时,但会话已经存在关闭。
问题是,如果我在关闭会话的同时没有获取更多数据,那就没必要了,HIBERNATE!如果我打开一个会话,我将获取所有我需要的数据,并且仅获取我需要的数据,并且在我关闭它之后,这意味着我不再需要任何数据,谢谢hibernate ,但是即使在会话关闭后,Hibernate的代理对象仍然保持活动状态。
即使在StackOverflow上,我也一直在网上搜索,并且有一些所谓的“解决方案”或“解决方法”,例如:
当查询足够简单以使用HQL时,我实际上在许多用例中都使用了那些。问题在于许多用例需要复杂的查询,具有大量的联接和计算,甚至使用未映射到Hibernate Entities的表,因此我需要一个本机查询来实现。
那将意味着映射我将用在一个,也许两个用例上的数十个实体,并且再也不会映射,只是这样我就可以将HQL与Constructor一起使用。此外,其中一些查询使用本机函数,而HQL根本不支持其他复杂的计算。
NULL as columnName
在某些用例上我也这样做,问题是当该列是实体关系上的联接列时,Hibernate设置了一个代理对象,而我不希望代理对象,只是联接列中的数据如果且仅在,则该代码的另一部分需要该数据,在这里我可以打开一个新会话并检索所需的数据。
不能,那就是RESTful API,如果客户在第一次请求时要求雇员数据,然后以后再次请求该雇员的公司数据,该怎么办?在请求正文中传递公司ID?那是一个常见的用例。
不起作用。
仍然创建代理对象,它仅禁用当前会话的第一级和第二级缓存。
并为我的API的部分数据请求的每个用例创建一个新类?哈哈哈!
您看到的,我之所以使用像Hibernate这样的ORM解决方案的唯一原因是因为我不敢对ResultSet进行手动处理而死掉了。忘记HQL,Criteria API等。如果我只能让Hibernate为我映射ResultSet到对象(或对象列表),我不介意从字符串创建本机查询。我想让Hibernate知道,如果有一个我在会话关闭之前没有获取的实体关系,那意味着我不想获取它,所以没有代理对象。
我知道那里可能有另一个ORM解决方案不存在此问题,但是由于 DEADLINES ,我不能抽出时间来学习它,所以请有人帮助我。
编辑
因此,你们中的一些人将我的问题标记为Converting Hibernate proxy to a real entity object的重复项,但有区别:
该问题围绕着如何“取消代理”对象,如如何触发Hibernate代理的初始化。
我的问题是有关如何使Hibernate 不对未获取的关系对象使用代理,而是仅使用具有其ID的关系类的实例。
在我之前的示例中,我希望休眠后不使用代理来懒惰地获取与Company
相关的Employee
对象,而是希望创建一个new Company(company_id)
像这样禁止代理。
编辑2
我刚刚发现Jackson项目(用于在我的API中以JSON形式读取/写入数据)中有一个模块,该模块在序列化对象时会忽略Hibernate代理。
Maven依赖项:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate5</artifactId>
<version>${jackson-version}</version>
</dependency>
然后只需在您的Object Mapper
实例上注册该模块:
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new Hibernate5Module());
答案 0 :(得分:0)
所以看来我的 EDIT 2 确实是解决我的问题的一种方法。
jackson-datatype-hibernate5
最初会忽略未获取的代理,在序列化对象时将其设置为null
,但是类Hibernate5Module
(或您要使用的任何Hibernate模块版本)具有非常方便的配置这样杰克逊只能序列化未获取代理的ID,这正是我所寻找的。 p>
// Configuring `ObjectMapper` and module:
ObjectMapper mapper = new ObjectMapper();
Hibernate5Module module = new Hibernate5Module()
.configure(Hibernate5Module.Feature.SERIALIZE_IDENTIFIER_FOR_LAZY_NOT_LOADED_OBJECTS, true);
mapper.registerModule(module);
这样,您可以使用该ObjectMapper
为您的类创建ObjectReaders
,这些类将不会在序列化时尝试初始化代理对象,而仅序列化对象的ID(如果它是OneToOne或ManyToOne关系,当然。)
我说这是“一种”解决方案,因为Hibernate仍在使用代理对象进行未获取的关系(当我只想使用代理对象的ID值供以后使用)时,唯一的一件事情是我已将Jackson配置为保留ID值并忽略代理对象。