package io.ebeaninternal.server.type; import io.ebean.config.dbplatform.DbPlatformType; import io.ebean.text.json.EJson; import io.ebeanservice.docstore.api.mapping.DocPropertyType; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; import javax.persistence.PersistenceException; import java.io.IOException; import java.sql.SQLException; import java.sql.Types; import java.util.List; /** * Types for mapping List in JSON format to DB types VARCHAR, JSON and JSONB. */ public class ScalarTypeJsonList { /** * Return the appropriate ScalarType based requested dbType and if Postgres. */ public static ScalarType<?> typeFor(boolean postgres, int dbType, DocPropertyType docType) { if (postgres) { switch (dbType) { case DbPlatformType.JSONB: return new ScalarTypeJsonList.JsonB(docType); case DbPlatformType.JSON: return new ScalarTypeJsonList.Json(docType); } } return new ScalarTypeJsonList.Varchar(docType); } /** * List mapped to DB VARCHAR. */ public static class Varchar extends ScalarTypeJsonList.Base { public Varchar(DocPropertyType docType) { super(Types.VARCHAR, docType); } } /** * List mapped to Postgres JSON. */ private static class Json extends ScalarTypeJsonList.PgBase { public Json(DocPropertyType docType) { super(DbPlatformType.JSON, PostgresHelper.JSON_TYPE, docType); } } /** * List mapped to Postgres JSONB. */ private static class JsonB extends ScalarTypeJsonList.PgBase { public JsonB(DocPropertyType docType) { super(DbPlatformType.JSONB, PostgresHelper.JSONB_TYPE, docType); } } /** * Base class for List handling. */ private abstract static class Base extends ScalarTypeJsonCollection<List> { public Base(int dbType, DocPropertyType docType) { super(List.class, dbType, docType); } @Override public List read(DataReader dataReader) throws SQLException { try { // parse JSON into modifyAware list return EJson.parseList(dataReader.getString(), true); } catch (IOException e) { throw new SQLException("Failed to parse JSON content as List: [" + dataReader.getString() + "]", e); } } @Override public void bind(DataBind b, List value) throws SQLException { if (value == null) { b.setNull(Types.VARCHAR); } else if (value.isEmpty()) { b.setString("[]"); } else { try { b.setString(EJson.write(value)); } catch (IOException e) { throw new SQLException("Failed to format List into JSON content", e); } } } @Override public String formatValue(List value) { try { return EJson.write(value); } catch (IOException e) { throw new PersistenceException("Failed to format List into JSON content", e); } } @Override public List parse(String value) { try { return EJson.parseList(value, false); } catch (IOException e) { throw new PersistenceException("Failed to parse JSON content as List: [" + value + "]", e); } } @Override public List jsonRead(JsonParser parser) throws IOException { return EJson.parseList(parser, parser.getCurrentToken()); } @Override public void jsonWrite(JsonGenerator writer, List value) throws IOException { EJson.write(value, writer); } } /** * Postgres extension to base List handling. */ private static class PgBase extends ScalarTypeJsonList.Base { final String pgType; PgBase(int jdbcType, String pgType, DocPropertyType docType) { super(jdbcType, docType); this.pgType = pgType; } @Override public void bind(DataBind bind, List value) throws SQLException { String rawJson = (value == null) ? null : formatValue(value); bind.setObject(PostgresHelper.asObject(pgType, rawJson)); } } }