关系处理:Hibernate 与 JDBC
Posted
技术标签:
【中文标题】关系处理:Hibernate 与 JDBC【英文标题】:Relationship Handling: Hibernate vs JDBC 【发布时间】:2017-01-31 21:25:36 【问题描述】:假设我有一个 mysql 数据库,其中包含两个表 patient
和 medicine
。我在下面显示了他们的列。
患者
idPatient (int) (primary key)
first_name (varchar)
last_name (varchar)
医药
idMedicine (int) (primary key)
idPatient (int) (foreign key)
drug_name (varchar)
请注意Medicine
表确实有Patient
表的外键。
现在,如果我使用纯 JDBC,我将执行以下操作来为 Medicine
和 Patient
表创建一个 bean
PatientBean
类
public class PatientBean
private int idPatient;
private String first_name;
private String last_name;
public void setIdPatient(int idPatient)
this.idPatient = idPatient;
public int getIdPatient()
return idPatient;
public void setFirstName(String first_name)
this.first_name = first_name;
public String getFirstName()
return first_name;
public void setLastName(String last_name)
this.last_name = last_name;
public String getLastName()
return last_name;
`MedicineBean` class
public class MedicineBean
private int idMedicine;
private int idPatient;
private String drug_name;
public void setIdMedicine(int idMedicine)
this.idMedicine = idMedicine;
public int getIdMedicine()
return idMedicine;
public void setIdPatient(int idPatient)
this.idPatient = idPatient;
public int getIdPatient()
return idPatient;
public void setDrugName(String drug_name)
this.drug_name = drug_name;
public String getDrugName()
return drug_name;
但是,如果我使用 NetBeans 之类的工具对我的数据库进行反向工程以实现休眠,该工具将为休眠生成 POJO 文件、映射等,我可以期待如下所示。
PatientBean
类
public class PatientBean
private int idPatient;
private String first_name;
private String last_name;
private MedicineBean medicineBean;
public void setIdPatient(int idPatient)
this.idPatient = idPatient;
public int getIdPatient()
return idPatient;
public void setFirstName(String first_name)
this.first_name = first_name;
public String getFirstName()
return first_name;
public void setLastName(String last_name)
this.last_name = last_name;
public String getLastName()
return last_name;
public void setMedicineBean(String medicineBean)
this.medicineBean = medicineBean;
public String getMedicineBean()
return medicineBean;
MedicineBean
类
public class MedicineBean
private int idMedicine;
private int idPatient;
private String drug_name;
private Set<PatientBean> patients = new HashSet<PatientBean>(0);
public void setIdMedicine(int idMedicine)
this.idMedicine = idMedicine;
public int getIdMedicine()
return idMedicine;
public void setIdPatient(int idPatient)
this.idPatient = idPatient;
public int getIdPatient()
return idPatient;
public void setDrugName(String drug_name)
this.drug_name = drug_name;
public String getDrugName()
return drug_name;
public void setPatients(Set<PatientBean>patients)
this.patients = patients;
public Set<PatientBean> getPatients()
return patients;
不仅如此,Hibernate 还会在 xml 文件中映射关系类型(一对一、一对多、多对一)。但是在 JDBC 中我们根本不关心它们,它们只是以相同方式处理的外键。
所以我的问题是,为什么会有这种差异?我相信 Hibernate 所做的大部分操作都是无用的,只是使用 CPU。例如,当我们调用getAllMedicines()
方法时,尝试在Patient
表中检索patients
的列表。在 99% 的情况下,我们只需要所有药物而不是患者名单,如果我们需要,我们可以加入并获得它!
那么这背后的原因是什么?或者我们是否也应该为 JDBC 保持相同的行为?
【问题讨论】:
patient
列表通常会延迟加载,这意味着它只会在需要时读取数据。第二种方式包含您的数据,就像您通常在 OO 程序中编写它一样
@ScaryWombat:是的,我知道。我的问题是为什么我们不在 JDBC 中做同样的事情?我认为它根本没用。
您没有在 JDBC 中执行此操作的唯一原因是因为 您 没有在 JDBC 中执行此操作。 您可以完全控制您想要的操作方式,因此您可以通过将 MedicineBean 的 int idPatient
替换为 PatientBean patient
并将 List<MedicineBean> medicines
添加到 PatientBean 来实现。这取决于它们的具体使用方式,这是另一种方法。
@Andreas: 好了.. 使用Hibernate
会导致我的REST API 出现严重问题,出现failed to lazily initialize collection
错误,因为这种不必要的加载其他内容的尝试。我在 Hibernate 的 JBOSS 站点建议中尝试了所有内容,完全没有用。我更喜欢在MedicineBean
中使用int idPatient
而不是PatientBean patient
,因为如果需要,我总是可以使用连接。
您始终可以选择使用轻量级 O/RM 库而不是 Hibernate。这样,您仍然可以获得 O/RM 的强大功能和灵活性,而不会产生 Hibernate 的开销(以及其他开销)。
【参考方案1】:
我不认为使用 hibernate 你会因为害怕而失去完全控制。
主要区别在于hibernate会在你的代码和jdbc之间增加一个额外的层。这一层可以非常薄:您可以选择在休眠中随时使用 jdbc。所以你不会失去任何控制。
更难的部分是了解 hibernate 的工作原理,以便您可以使用其更高级别的 api 并了解 hibernate 如何将其转换为 jdbc。这是一个有点复杂的任务,因为 orm 映射是一个复杂的主题。多次阅读参考文档,以准确了解 hibernate 可以做什么,以及他们建议做什么和不做什么,是一个很好的起点。其余的将来自使用 hibernate 的经验。
对于您的示例,您说的是休眠映射关系,但事实并非如此:您的逆向工程工具做到了。您可以不映射关系而只映射外键基本类型(如果 id 是数字,则为 Long)。
至于装载的东西。如果您希望始终加载@OneToMany,只需使用FetchType.EAGER
对其进行注释。 @*ToMany
关联默认是惰性的(以避免加载太多数据),但另一方面,@*ToOne
关联默认为 EAGER
。
这可以在实体级别进行配置,使其成为查询的默认行为,但可以为每个查询重载。
你看到了吗?你并没有失去控制,你只需要了解hibernate api如何转换为jdbc。
除了向 hibernate 团队提出时已修复的 bug 外,hibernate 对性能的影响并没有那么大。在应用程序的性能关键部分,您始终可以选择使用 jdbc,其中休眠开销为 0。
使用 hibernate 有什么好处?根据我的经验,实体模型/数据库模型中的重构要容易得多,因为您更改了休眠映射,并且休眠生成的所有查询也会自动更改。您只需更新您手写的自定义查询(SQL / HQL / Criteria)。
根据我在数百个表(其中一些具有超过 10B 行)、数 TB 数据库上的经验(使用休眠 10 年),我不想回到普通 jdbc,这并不意味着我不想'当它是完美的工具时不会使用它,但它只是我编写的 orm 代码的 1% 或 2%。
希望对您有所帮助。
编辑:如果你使用的是带有 spring 的 hibernate,请查看 spring-jdbc,它在 jdbc 周围添加了一个很好的层。在那里,您几乎不需要阅读文档:您可以直接识别它将如何转换为 jdbc,但它带来了许多实用程序,从而大大减少了直接使用 jdbc 的样板(例如异常处理以关闭 Resultset
和PreparedStatement
,将ResultSet
转换为 DTO 列表等)。
当然hibernate和spring-jdbc可以在同一个应用中使用。只需将它们配置为使用相同的事务层,并且在相同的 tx 中使用时要小心。
【讨论】:
感谢您的回复。不,我不使用带有 Spring 的 Hibernate,我正在构建一个REST API
。我在这里不能同意你的看法 - If you wish to always have a @OneToMany loaded, just annotate it with FetchType.EAGER
这将是一个巨大的开销。` 无论如何,你的意思是我可以像在 Hibernate 中一样使用像 idPatient
这样的外键,而无需使其成为 SET
或完整的 bean?然后我必须更改关联的 XML 不是吗?我没有使用注释。
我所说的也代表 hbm 映射。你为什么说这是一个巨大的开销?为了加载父实体,hibernate会触发2个查询:一个是加载父实体(按id,一行,不应该很重),一个是你感兴趣的子集合。你不需要父实体吗?只需创建一个 Criteria / HQL / SQL 来加载 parent_id = parent.id 的子实体(并且不要忘记将 ManyToOne 标记为 LAZY)。 hbm 映射文件需要反映两件事:数据库模型,以及您希望如何从 java 代码访问实体。仅供参考,hbm 文件已被弃用。
我从高层次的角度回答了您的问题,因为在我看来,您想要更多的全局洞察力而不是 hibernate 的精确部分的答案。如果不是这种情况,请在您的问题中添加表的数据库模型、hbm 映射文件、您希望 hibernate 生成的 sql 查询以及您的 hibernate 加载代码,我们将看看如何修改映射和休眠代码以获得您想要的。
谢谢。我的问题与我同事在这里提出的问题非常相关,我们现在正在面对它 - ***.com/questions/39637121/…
如果他的帖子不够清楚,我可以重新发布。以上是关于关系处理:Hibernate 与 JDBC的主要内容,如果未能解决你的问题,请参考以下文章
码农小汪-Hibernate学习8-hibernate关联关系注解表示@OneToMany mappedBy @ManyToMany @JoinTable
hibernate annotation注解方式来处理映射关系