4.2. 关于 HQL 语句
HQL 和 Jakarta Persistence 查询语言都允许 SELECT、UPDATE 和 DELETE 语句。HQL 还允许 INSERT 语句,其格式类似于 SQL INSERT-SELECT。
下表显示了各种 HQL 语句的 Backus-Naur Form(BNF)表示法中的语法。
| 声明 | 描述 |
|---|---|
|
|
HQL 中用于
|
|
|
HQL
|
|
|
HQL 中的
|
|
|
HQL 中的
没有与这一点相当的 Jakarta Persistence 查询语言。 |
Hibernate 允许使用数据操作语言(DML)直接通过 Hibernate 查询语言(HQL)批量插入、更新和删除数据。
使用 DML 可能会违反对象/关系映射,并可能会影响对象状态。对象状态保留在内存中,使用 DML 时,内存中对象的状态不受影响,具体取决于对底层数据库执行的操作。如果使用 DML,则必须小心使用内存中数据。
关于 UPDATE 和 DELETE 语句
UPDATE 和 DELETE 语句的伪syntax 是:
(更新 | 从中删除 )EntityName(WHERE where_conditions)?。
FROM 关键字和 WHERE 术语是可选的。FROM 子句负责定义可供查询的其余部分使用的对象模型类型的范围。它还负责定义其余查询可用的所有标识变量。WHERE 子句允许您优化返回的实例列表。
执行 UPDATE 或 DELETE 语句的结果是实际受影响的行数(更新或删除)。
示例:批量更新语句
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
String hqlUpdate = "update Company set name = :newName where name = :oldName";
int updatedEntities = s.createQuery( hqlUpdate )
.setString( "newName", newName )
.setString( "oldName", oldName )
.executeUpdate();
tx.commit();
session.close();
示例:批量删除语句
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
String hqlDelete = "delete Company where name = :oldName";
int deletedEntities = s.createQuery( hqlDelete )
.setString( "oldName", oldName )
.executeUpdate();
tx.commit();
session.close();
Query.executeUpdate() 方法返回的 int 值指示受操作影响的数据库内实体数量。
在内部,数据库可能会使用多个 SQL 语句来执行响应 DML 更新 或删除 请求 的操作。这可能是因为表和需要更新或删除的连接表之间存在关系。
例如,如上例所示,发出删除声明可能实际上不仅对使用 oldName 命名的 公司的 Company 表执行删除,而且会对合并表执行删除。因此 , 由于 成功执行了上例,与 Employee 表的双向多对多关系也会从相应的加入表中丢弃行。
已删除的Entries 值包含操作影响的所有行的计数,包括 join 表中的行。
执行批量更新或删除操作时应小心,因为它们可能会导致数据库和活动持久化上下文中的实体不一致。通常,批量更新和删除操作应只在新持久性上下文中的事务内执行,或者在获取或访问状态可能受到此类操作影响的实体之前执行。
关于 INSERT 语句
HQL 添加了定义 INSERT 语句的功能。没有与这一点相当的 Jakarta Persistence 查询语言。HQL INSERT 语句的 Backus-Naur Form(BNF)是:
insert_statement ::= insert_clause select_statement
insert_clause ::= INSERT INTO entity_name (attribute_list)
attribute_list ::= state_field[, state_field ]*
attribute_list 与 SQL INSERT 语句中的列规格类似。对于涉及映射继承的实体,在 properties _list 中只能使用直接在指定实体上定义的属性。不允许超级类属性,子类属性并不合理。换句话说,INSERT 语句本质上是非私有的。
select_statement 可以是任何有效的 HQL 选择查询,注意返回类型必须与插入预期的类型匹配。目前,这会在查询编译过程中检查,而不是让检查与数据库相连接。这可能会导致 Hibernate 类型与 相等 的问题 。例如,这可能会导致映射为 org.hibernate.type.DateType 的属性和定义为 org.hibernate.type.TimestampType 的属性之间不匹配问题,即使数据库可能无法区分或处理转换。
对于 id 属性,插入语句为您提供两个选项:您可以在 attribute_list 中明确指定 id 属性,在这种情况下,从对应的 select 表达式获取它的值,或者从 attribute_list 中省略它,在这种情况下使用生成的值。只有使用"在数据库中操作"的 id 生成器时才可使用后一种选项;尝试将此选项用于任何"内存中"类型生成器将导致解析过程中出现异常情况。
对于不确定的锁定属性,插入语句再次为您提供两个选项:您可以在 attribute_list 中指定 属性,在这种情况下,它的值取自对应的选择表达式,或者从 attribute_list 中省略它,在这种情况下,使用了对应的 org.hibernate.type.VersionType 定义的 seed 值。
示例:INSERT 查询语句
String hqlInsert = "insert into DelinquentAccount (id, name) select c.id, c.name from Customer c where ...";
int createdEntities = s.createQuery(hqlInsert).executeUpdate();
示例: Bulkert Statement
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
String hqlInsert = "insert into Account (id, name) select c.id, c.name from Customer c where ...";
int createdEntities = s.createQuery( hqlInsert )
.executeUpdate();
tx.commit();
session.close();
如果不使用 SELECT 语句提供 id 属性的值,则会为您生成标识符,只要底层数据库支持自动生成的密钥。此批量插入操作的返回值是数据库中实际创建的条目数。