java - HibernateException:无法使用 spring-data-jpa 本机查询为 postgresql 确定类异常的类型
问题描述
我有一个带有 bean 和存储库的 Spring Boot 应用程序。我使用 postgresql 作为数据库。该数据库有一个名为 testjob 的表,有两列:id(UUID 类型)和参数(jsonb 类型)。我按照这篇文章支持 jsonb:https ://thoughts-on-java.org/persist-postgresqls-jsonb-data-type-hibernate/ 我创建了文章中提到的每个类,但是当我运行我的测试时,它只是调用存储库上的 upsert 方法,它会引发异常,原因如下:
Caused by: org.hibernate.HibernateException: Could not determine a type for class: com.test.Parameters
仅当我运行本机查询时才会引发此异常,但使用 JPQL 一切正常。遗憾的是,我需要在本机中运行它,因为将来我将不得不使用 postgresql ON_CONFLICT 语句。我会提出任何建议,谢谢。
我的课程:
TestJobBean.java
@Entity
@Table(name = "testjob")
public class TestJobBean {
@Id
private UUID id;
@NotNull @Column(name = "parameters") @Type(type = "ParametersType")
private Parameters parameters;
...
}
TestJobRepository.java
@Repository
@Transactional
public interface TestJobRepository extends JpaRepository<TestJobBean, UUID>, TestJobRepositoryCustom {
}
TestJobRepositoryCustom.java
public interface TestJobRepositoryCustom {
int upsert(UUID id, Parameters parameters);
}
TestJobRepositoryCustomImpl.java
@Transactional
public class TestJobRepositoryCustomImpl implements TestJobRepositoryCustom {
@PersistenceContext
private EntityManager entityManager;
@Override
public int upsert(UUID id, Parameters parameters) {
final Query query = entityManager.createNativeQuery("INSERT INTO testjob (id, parameters) VALUES(:id, :parameters)");
query.setParameter("id", id);
query.setParameter("parameters", parameters);
return query.executeUpdate();
}
}
package-info.java
@org.hibernate.annotations.TypeDef(name = "ParametersType", typeClass = ParametersType.class)
package com.test.job;
Parameters.java
@Immutable
public class Parameters {
private final long from;
private final long to;
private final @Nonnull String name;
public Parameters(@JsonProperty("from") long from, @JsonProperty("to") long to,
@JsonProperty("name") String name) {
this.from = from;
this.to = to;
this.name = name;
}
@JsonProperty("from")
public long getFrom() {
return from;
}
@JsonProperty("to")
public long getTo() {
return to;
}
@JsonProperty("name")
public long getName() {
return name;
}
}
TestPostgreSQL95Dialect.java
public class TestPostgreSQL95Dialect extends PostgreSQL95Dialect {
public TestPostgreSQL95Dialect() {
this.registerColumnType(Types.JAVA_OBJECT, "jsonb");
}
}
ParametersType.java
public class ParametersType implements UserType {
private final @Nonnull ObjectMapper mapper = new ObjectMapper();
@Override
public int[] sqlTypes() {
return new int[] {Types.JAVA_OBJECT};
}
@Override
public Class<ParametersType> returnedClass() {
return ParametersType.class;
}
@Override
public Object nullSafeGet(final ResultSet rs, final String[] names, final SharedSessionContractImplementor session,
final Object owner) throws HibernateException, SQLException {
final String cellContent = rs.getString(names[0]);
if (cellContent == null) {
return null;
}
try {
return mapper.readValue(cellContent, returnedClass());
} catch (final IOException ex) {
throw new RuntimeException("Failed to convert string to an instance!", ex);
}
}
@Override
public void nullSafeSet(final PreparedStatement ps, final Object value, final int idx,
final SharedSessionContractImplementor session) throws HibernateException, SQLException {
if (value == null) {
ps.setNull(idx, Types.OTHER);
return;
}
try {
final String valueAsJson = mapper.writeValueAsString(value);
ps.setObject(idx, valueAsJson, Types.OTHER);
} catch (final Exception ex) {
throw new RuntimeException("Failed to convert the instance to string!", ex);
}
}
@Override
public Object deepCopy(final Object value) throws HibernateException {
return value;
}
@Override
public boolean isMutable() {
return false;
}
@Override
public Serializable disassemble(final Object value) throws HibernateException {
try {
final String valueAsJson = mapper.writeValueAsString(value);
return valueAsJson;
} catch (final Exception ex) {
throw new RuntimeException("Failed to convert the instance to string!", ex);
}
}
@Override
public Object assemble(final Serializable cached, final Object owner) throws HibernateException {
try {
return mapper.readValue((String) cached, returnedClass());
} catch (final IOException ex) {
throw new RuntimeException("Failed to convert string to an instance!", ex);
}
}
@Override
public Object replace(final Object original, final Object target, final Object owner) throws HibernateException {
return original;
}
@Override
public boolean equals(final Object obj1, final Object obj2) throws HibernateException {
return Objects.equals(obj1, obj2);
}
@Override
public int hashCode(final Object obj) throws HibernateException {
return obj.hashCode();
}
}
解决方案
来自 coladict 的评论:“UserType 实现未添加到可在本机查询中使用的可识别类型”因此解决方案是将对象转换为字符串并将字符串转换为 jsonb。
TestJobRepositoryCustomImpl.java
@Transactional
public class TestJobRepositoryCustomImpl implements TestJobRepositoryCustom {
@PersistenceContext
private EntityManager entityManager;
@Autowired
private ObjectMapper objectMapper;
@Override
public int upsert(UUID id, Parameters parameters) {
final String parametersAsString = objectMapper.writer().writeValueAsString(parameters);
final Query query = entityManager.createNativeQuery("INSERT INTO testjob (id, parameters) VALUES(:id, cast(:parameters as jsonb))");
query.setParameter("id", id);
query.setParameter("parameters", parametersAsString);
return query.executeUpdate();
}
}
推荐阅读
- python - 子进程完成后如何删除文件/目录?
- python-2.7 - 在同一列中选择同时满足两个条件的行(熊猫)
- python - 错误 - 在不支持分叉的系统上尝试在不保护您的导入的情况下进行并行计算。
- c# - 删除 WCFDiagnosticsExample.svclog 是否安全
- javascript - 在 JSX 表达式中连接字符串和 HTML
- sql - 如何在SQL中的两行和同一列之间进行减法?
- shell - “w”命令不显示不同的终端会话 Ubuntu 16.04
- xml - 使用 Spark 加载 XML 时推断架构中的重复字段
- sql - How to update the rows of a table from another table? SQL Server
- git - 将 Visual Studio 项目添加到现有的 git 存储库