目前我有以下代码,它迭代参数实体列表并更新数据库中的每个名称:
public class test {
@Autowired
private ParameterJpaRepository parameterJpaRepository;
public updateParameters(List<Parameter> parameters) {
for (Parameter parameter : parameters) {
parameterJpaRepository.setNameById(parameter.getId(), parameter.getName());
}
}
}
public interface ParameterJpaRepository extends JpaRepository<Parameter, Long> {
@Modifying
@Query("UPDATE Parameter p SET p.name = :name WHERE p.id = :id")
void setNameById(@Param("id") long id, @Param("name") String name);
}
显然,这会导致 N 查询:
Hibernate: update parameter set name=? where id=?
Hibernate: update parameter set name=? where id=?
Hibernate: update parameter set name=? where id=?
Hibernate: update parameter set name=? where id=?
我想将 then 合并到与此尝试等效的单个查询中:
public interface ParameterJpaRepository extends JpaRepository<Parameter, Long> {
@Modifying
@Query("UPDATE Parameter p SET p.name = (:names) WHERE p.id = (:ids)")
void setNameById(@Param("ids") List<Long> ids, @Param("names") List<String> names);
}
这应该产生类似的结果:
Hibernate: UPDATE parameter
SET name = (case when id = ? then ?
when id = ? then ?
when id = ? then ?
when id = ? then ?
end)
WHERE id in (?, ?, ?, ?);
这可能吗?
您可能想要这样的东西
@Modifying
@Query(value = "UPDATE Parameter p SET p.name = (CASE " +
"WHEN p.id IN (:ids) THEN (CASE " +
"WHEN p.id = :ids[0] THEN :names[0] " +
"WHEN p.id = :ids[1] THEN :names[1] " +
"WHEN p.id = :ids[2] THEN :names[2] " +
// ...
"ELSE p.name " +
"END) " +
"ELSE p.name " +
"END) " +
"WHERE p.id IN (:ids)", nativeQuery = true)
void setNameById(@Param("ids") List<Long> ids, @Param("names") List<String> names);
这是一个不好的方法。不要尝试这样做。
这对于大型列表来说非常糟糕,因为查询将变得非常长并且非常难以维护。 如果 id 和名称列表的顺序不同,则该方法不起作用。 如果您需要更新大量行,或者 ids 和名称列表的顺序不固定,您可能需要考虑使用不同的方法,例如为每行执行单独的更新语句或使用临时表.
(Un)幸运的是,
spring-data-jpa
功能并不像您希望看到的那么灵活,但是它确实允许创建Spring数据存储库的自定义实现,因此您可以编写任何您想要的更新查询,一些示例:
顺便说一下,您需要记住,一般来说,通过直接更新来更新休眠实体并不是一个好主意,原因如下:
所以,有时启用批量更新并编写如下内容会更好:
@Transactional
default void setNameById(List<Long> ids, List<String> names) {
Map<Long, Parameter> data = StreamSupport.stream(findAllById(ids).spliterator(), false)
.collect(Collectors.toMap(
Customer::getId,
Function.identity()
));
for (int i = 0, n = ids.size(); i < n; i++) {
Parameter parameter = data.get(ids.get(i));
if (parameter != null) {
parameter.setName(names.get(i));
}
}
saveAll(data.values());
}
我知道它很旧,但无论如何我都会分享它。
请检查我的帖子以及我在与这个问题斗争了很长时间后的回答。
它正在使用休眠6。
其背后的想法是在数据库中创建一个自定义类型(在您的情况下)具有长和字符串 - Id,名称。
创建一个 UserType 以允许 hibernate 将您的类型转换为 db 类型。
创建存储过程以在单个语句中更新数据。
-- db script
CREATE TYPE YourCustomType AS (
id long,
name varchar(100)
);
CREATE OR REPLACE PROCEDURE updateBulk(yourData YourCustomType[]) AS $$
BEGIN
UPDATE Parameter as db
SET db.name = val.name
FROM (SELECT * from UNNEST(yourArrayOfData)) as val
WHERE db.id = val.id
END;
$$ LANGUAGE 'plpgsql';
在我的帖子中添加java中的对象+创建一个
UserType
+贡献类型+然后将其添加到存储库中
@Procedure(value= "update_bulk")
void updateBulk(Yourtype[] data);
玩得开心..