使用JPA坚持jsonb

时间:2018-07-20 20:41:22

标签: java postgresql hibernate jpa jsonb

我正在尝试使用jpa将某些数据持久保存到jsonb列中。转换器似乎可以正常工作,但在内部出现此错误:

    Caused by: javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not execute statement
    at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:149)
    at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:157)
    at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:164)
    at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1460)
    at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:511)
    at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:3283)
    at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2479)
    at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:473)
    at org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorImpl.beforeCompletion(JtaTransactionCoordinatorImpl.java:352)
    at org.hibernate.resource.transaction.backend.jta.internal.synchronization.SynchronizationCallbackCoordinatorNonTrackingImpl.beforeCompletion(SynchronizationCallbackCoordinatorNonTrackingImpl.java:47)
    at org.hibernate.resource.transaction.backend.jta.internal.synchronization.RegisteredSynchronization.beforeCompletion(RegisteredSynchronization.java:37)
    at com.sun.enterprise.transaction.JavaEETransactionImpl.commit(JavaEETransactionImpl.java:452)
    ... 69 more
Caused by: org.hibernate.exception.SQLGrammarException: could not execute statement
    at org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:106)
    at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:42)
    at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:113)
    at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:99)
    at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:178)
    at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3167)
    at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3682)
    at org.hibernate.action.internal.EntityInsertAction.execute(EntityInsertAction.java:90)
    at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:604)
    at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:478)
    at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:356)
    at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39)
    at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1454)
    ... 77 more
Caused by: org.postgresql.util.PSQLException: ERROR: column "answers" is of type jsonb but expression is of type bytea
  Hint: You will need to rewrite or cast the expression.
  Position: 81
    at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2422)
    at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2167)
    at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:306)
    at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:441)
    at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:365)
    at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:155)
    at org.postgresql.jdbc.PgPreparedStatement.executeUpdate(PgPreparedStatement.java:132)
    at com.sun.gjc.spi.base.PreparedStatementWrapper.executeUpdate(PreparedStatementWrapper.java:125)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.sun.gjc.spi.jdbc40.ProfiledConnectionWrapper40$1.invoke(ProfiledConnectionWrapper40.java:437)
    at com.sun.proxy.$Proxy367.executeUpdate(Unknown Source)
    at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:175)
    ... 85 more
]]

的代码如下:

@Entity
@Table(name = "question")
@EntityListeners({ CreatedOnListener.class })
public class Question implements ICreatedOn {

@Id
@GeneratedValue
private long id;

@Column(name = "q_text", length = 600)
private String text;

@Column(name = "pic_url", length = 400)
private String picUrl;

@Enumerated(EnumType.STRING)
@Column(name = "q_type")
private QuestionType type;

@Convert(converter = AnswerJTypeConverter.class)
@Column(name = "answers", columnDefinition = "jsonb")
private List<AnswerJType> answers = new ArrayList<>();

    public class AnswerJTypeConverter implements AttributeConverter<List<AnswerJType>, PGobject> {

    private static ObjectMapper MAPPER = new ObjectMapper();

    @Override
    public PGobject convertToDatabaseColumn(List<AnswerJType> answers) {
        if (answers == null)
            return null;

        ObjectNode answersJson = MAPPER.createObjectNode();

        answers.forEach(a -> {
            ObjectNode ansNode = MAPPER.createObjectNode();
            ansNode.put("text", a.getText());
            ansNode.put("picUrl", a.getPicUrl());
            answersJson.set("" + a.getId(), ansNode);
        });

        PGobject jsonb = new PGobject();
        jsonb.setType("jsonb");
        try {
            jsonb.setValue(answersJson.toString());
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        return jsonb;
    }

    @Override
    public List<AnswerJType> convertToEntityAttribute(PGobject dbData) {
        try {
            if (dbData.getType().equals("jsonb")) {
                String colValue = dbData.getValue();
                JsonNode answersJson = MAPPER.readTree(colValue);
                Iterator<Entry<String, JsonNode>> fields = answersJson.fields();
                List<AnswerJType> answers = new ArrayList<>();
                while (fields.hasNext()) {
                    Entry<String, JsonNode> next = fields.next();
                    JsonNode answer = next.getValue();
                    AnswerJType ans = new AnswerJType();
                    ans.setId(Long.parseLong(next.getKey()));
                    if (answer.has("text")) {
                        ans.setText(answer.get("text").asText());
                    }
                    if (answer.has("picUrl")) {
                        ans.setPicUrl(answer.get("picUrl").asText());
                    }
                    answers.add(ans);
                }
                return answers;
            }
            throw new RuntimeException("PGobject type is not jsonb.");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}

我只是不知道问题出在哪里,因为我的列在数据库中的类型为jsonb,而我的columnDefinition也是jsonb。有什么想法吗?

0 个答案:

没有答案