2015-01-12 11 views
12

私は以下のURLに記載されている例に従っていますか? Mapping postgreSQL JSON column to Hibernate value typeHibernateを使用してpostgresql jsonデータ型をマップするにはどうすればよいですか?

しかし、常に次の例外を取得:私はサーバとしてTomEEを使用しています

Caused by: org.hibernate.MappingException: No Dialect mapping for JDBC type: 2000 
    at org.hibernate.dialect.TypeNames.get(TypeNames.java:76) 
    at org.hibernate.dialect.TypeNames.get(TypeNames.java:99) 
    at org.hibernate.dialect.Dialect.getTypeName(Dialect.java:310) 
    at org.hibernate.mapping.Column.getSqlType(Column.java:226) 
    at org.hibernate.mapping.Table.validateColumns(Table.java:369) 
    at org.hibernate.cfg.Configuration.validateSchema(Configuration.java:1305) 
    at org.hibernate.tool.hbm2ddl.SchemaValidator.validate(SchemaValidator.java:155) 
    at org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:512) 

を。 Json本体をpostgresql列に格納しようとしています。エンティティのpojosをpostgresのデータ型構造にマップしようとしています。

何が問題なのでしょうか?またはシナリオなどの処理に優れた技術を持っていますか?その情報源に私を指摘してください。

エンティティテーブルを作成するために使用するスクリプトは次のとおりです。

CREATE TABLE historyentity 
(
    id character varying(255) NOT NULL, 
    userid character varying(255), 
    lastchanged timestamp without time zone, 
    type character varying(255), 
    history json [], 
    CONSTRAINT historyentity_pkey PRIMARY KEY (id), 
    CONSTRAINT historyentity_userid_fkey FOREIGN KEY (userid) 
     REFERENCES userentity (id) MATCH SIMPLE 
     ON UPDATE NO ACTION ON DELETE NO ACTION 
) 
WITH (
    OIDS=FALSE 
); 
ALTER TABLE historyentity 
    OWNER TO postgres; 
GRANT ALL ON TABLE historyentity TO postgres; 

エンティティのPOJOは、次のように次のようになります。私は、エンティティPOJOを定義するためにロンボクを使用しています

@Entity 
@Data 
@AllArgsConstructor 
@NoArgsConstructor 
@TypeDefs({ @TypeDef(name = "StringJsonObject", typeClass = StringJsonUserType.class) }) 
public class HistoryEntity { 

    @Id 
    private String id; 
    private String userid; 
    private String type; 
    @Type(type = "StringJsonObject") 
    private String history; 
    private Date lastchanged; 

} 

以下はDialect拡張クラスです: 登録されたタイプのColumnとHibenateの両方で試しました。しかし、両方がうまくいっていません。

import org.hibernate.dialect.PostgreSQL82Dialect; 

public class JsonPostgreSQLDialect extends PostgreSQL82Dialect 

    { 
     @Inject 
     public JsonPostgreSQLDialect() 
     { 
      super(); 
       this.registerColumnType(Types.JAVA_OBJECT, "json"); 
      // this.registerHibernateType(Types.JAVA_OBJECT, "json"); 
     } 
    } 

次のクラスは、ユーザータイプを定義するために使用されている:

import java.io.Serializable; 
import java.sql.PreparedStatement; 
import java.sql.ResultSet; 
import java.sql.SQLException; 
import java.sql.Types; 

import org.hibernate.HibernateException; 
import org.hibernate.engine.spi.SessionImplementor; 
import org.hibernate.usertype.UserType; 


public class StringJsonUserType implements UserType 
{ 
    private final int[] sqlTypesSupported = new int[]{ Types.JAVA_OBJECT }; 

    /** 
    * Return the SQL type codes for the columns mapped by this type. The codes are defined on <tt>java.sql.Types</tt>. 
    * 
    * @return int[] the typecodes 
    * @see java.sql.Types 
    */ 
    @Override 
    public int[] sqlTypes() 
    { 
     return sqlTypesSupported; 
    } 

    /** 
    * The class returned by <tt>nullSafeGet()</tt>. 
    * 
    * @return Class 
    */ 
    @Override 
    public Class returnedClass() 
    { 
     return String.class; 
    } 

    /** 
    * Compare two instances of the class mapped by this type for persistence "equality". Equality of the persistent 
    * state. 
    * 
    * @return boolean 
    */ 
    @Override 
    public boolean equals(Object x, Object y) throws HibernateException 
    { 

     if (x == null) 
     { 

      return y == null; 
     } 

     return x.equals(y); 
    } 

    /** 
    * Get a hashcode for the instance, consistent with persistence "equality" 
    */ 
    @Override 
    public int hashCode(Object x) throws HibernateException 
    { 

     return x.hashCode(); 
    } 

    /** 
    * Retrieve an instance of the mapped class from a JDBC resultset. Implementors should handle possibility of null 
    * values. 
    * 
    * @param rs a JDBC result set 
    * @param names the column names 
    * @param owner the containing entity @return Object 
    */ 
    @Override 
    public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) 
     throws HibernateException, SQLException 
    { 
     if (rs.getString(names[0]) == null) 
     { 
      return null; 
     } 
     return rs.getString(names[0]); 
    } 

    /** 
    * Write an instance of the mapped class to a prepared statement. Implementors should handle possibility of null 
    * values. A multi-column type should be written to parameters starting from <tt>index</tt>. 
    * 
    * @param st a JDBC prepared statement 
    * @param value the object to write 
    * @param index statement parameter index 
    */ 
    @Override 
    public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) 
     throws HibernateException, SQLException 
    { 
     if (value == null) 
     { 
      st.setNull(index, Types.OTHER); 
      return; 
     } 

     st.setObject(index, value, Types.OTHER); 
    } 

    /** 
    * Return a deep copy of the persistent state, stopping at entities and at collections. It is not necessary to copy 
    * immutable objects, or null values, in which case it is safe to simply return the argument. 
    * 
    * @param value the object to be cloned, which may be null 
    * @return Object a copy 
    */ 
    @Override 
    public Object deepCopy(Object value) throws HibernateException 
    { 

     return value; 
    } 

    /** 
    * Are objects of this type mutable? 
    * 
    * @return boolean 
    */ 
    @Override 
    public boolean isMutable() 
    { 
     return true; 
    } 

    /** 
    * Transform the object into its cacheable representation. At the very least this method should perform a deep copy 
    * if the type is mutable. That may not be enough for some implementations, however; for example, associations must 
    * be cached as identifier values. (optional operation) 
    * 
    * @param value the object to be cached 
    * @return a cachable representation of the object 
    */ 
    @Override 
    public Serializable disassemble(Object value) throws HibernateException 
    { 
     return (String) this.deepCopy(value); 
    } 

    /** 
    * Reconstruct an object from the cacheable representation. At the very least this method should perform a deep copy 
    * if the type is mutable. (optional operation) 
    * 
    * @param cached the object to be cached 
    * @param owner the owner of the cached object 
    * @return a reconstructed object from the cachable representation 
    */ 
    @Override 
    public Object assemble(Serializable cached, Object owner) throws HibernateException 
    { 
     return this.deepCopy(cached); 
    } 

    /** 
    * During merge, replace the existing (target) value in the entity we are merging to with a new (original) value 
    * from the detached entity we are merging. For immutable objects, or null values, it is safe to simply return the 
    * first parameter. For mutable objects, it is safe to return a copy of the first parameter. For objects with 
    * component values, it might make sense to recursively replace component values. 
    * 
    * @param original the value from the detached entity being merged 
    * @param target the value in the managed entity 
    * @return the value to be merged 
    */ 
    @Override 
    public Object replace(Object original, Object target, Object owner) throws HibernateException 
    { 
     return original; 
    } 
} 
+0

あなたはエンティティPOJOをマップしようとするにはどうすればよいですpostgresのデータ型構造に?あなたはコードと設定コードを表示できますか? – jfun

+0

私はちょうどコードとpostgresの構造について説明しました。私はそれが助けてくれることを望みます – chand

+0

hibernate.propertiesを更新しましたか?そこに正しい "方言"を設定しましたか? – giaffa86

答えて

0

PostgresのJSON型はPostgreSQL92DialectでHibernateに追加されています。ですから、その方言またはそのサブクラスの1つを使用するか、次の型定義を追加したカスタムの方言にする必要があり、次のいずれかの種類自体を

 this.registerColumnType(2000, "json"); 

を(Hibernateの5.xの一例)を以下のように定義することができます:

public class JsonType implements UserType { 

    public static final ObjectMapper MAPPER = new ObjectMapper(); 

    private int[] sqlTypes; 
    private com.fasterxml.jackson.databind.ObjectWriter writer; 
    private JavaType type; 
    private boolean isBinary; 
    private ObjectReader reader; 

    public JsonType() { 
     init(SimpleType.constructUnsafe(Object.class), false); 
    } 

    public JsonType(Class clazz, boolean isBinary) { 
     this(SimpleType.construct(clazz), isBinary); 
    } 

    public JsonType(JavaType type, boolean isBinary) { 
     init(type, isBinary); 
    } 

    protected void init(JavaType type, boolean isBinary) { 
     this.type = type; 
     this.isBinary = isBinary; 
     this.reader = MAPPER.readerFor(type); 
     this.writer = MAPPER.writerFor(type); 
     this.sqlTypes = new int[]{Types.JAVA_OBJECT}; 
    } 


    public boolean equals(Object x, Object y) throws HibernateException { 
     if (x == y) { 
      return true; 
     } else if (x == null || y == null) { 
      return false; 
     } else { 
      return x.equals(y); 
     } 
    } 

    public int hashCode(Object x) throws HibernateException { 
     return null == x ? 0 : x.hashCode(); 
    } 

    public boolean isMutable() { 
     return true; 
    } 

    @Override 
    public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) throws HibernateException, SQLException { 
     final Object result = rs.getObject(names[0]); 
     if (!rs.wasNull()) { 
      String content; 

      if (result instanceof String) { 
       content = (String) result; 
      } else if (result instanceof PGobject) { 
       // If we get directly the PGobject for some reason (more exactly, if a DB like H2 does the serialization directly) 
       content = ((PGobject) result).getValue(); 
      } else { 
       throw new IllegalArgumentException("Unknown object type (excepted pgobject or json string)"); 
      } 
      if (content != null) { 
       return convertJsonToObject(content); 
      } 
     } 
     return null; 
    } 

    @Override 
    public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) throws HibernateException, SQLException { 
     if (value == null) { 
      st.setObject(index, null); 
      return; 
     } 
     PGobject pg = new PGobject(); 
     pg.setType(isBinary ? "jsonb" : "json"); 
     pg.setValue(convertObjectToJson(value)); 
     st.setObject(index, pg); 
    } 


    Object convertJsonToObject(String content) { 
     try { 
      return reader.readValue(content); 
     } catch (IOException e) { 
      throw new RuntimeException(e); 
     } 
    } 

    String convertObjectToJson(Object object) { 
     try { 
      return writer.writeValueAsString(object); 
     } catch (IOException e) { 
      throw new RuntimeException(e); 
     } 
    } 

    public Object deepCopy(Object value) throws HibernateException { 
     String json = convertObjectToJson(value); 
     return convertJsonToObject(json); 
    } 


    public Object replace(Object original, Object target, Object owner) 
     throws HibernateException { 
     return deepCopy(original); 
    } 


    public Serializable disassemble(Object value) throws HibernateException { 
     return (Serializable) deepCopy(value); 
    } 


    public Object assemble(Serializable cached, Object owner) 
     throws HibernateException { 
     return deepCopy(cached); 
    } 


    public int[] sqlTypes() { 
     return sqlTypes; 
    } 


    public Class returnedClass() { 
     return type.getRawClass(); 
    } 
} 

この例では、JSON(デ)シリアライズするためのフレームワークとしてジャクソンを使用します。

次のように次に、あなたのタイプを使用することができます。

@Entity 
@TypeDefs({@TypeDef(name= "StringJsonObject", typeClass = JsonType.class)}) 
public class MyEntity { 

    @Type(type = "StringJsonObject") 
    @Column(name="visuals", columnDefinition = "json") 
    private Map<String, String> visuals; 

} 

をしかし、これはすべてあなたが(おそらくHibernateの4.xの)実装タイプと非常によく似ています。では、なぜあなたの実装は機能していませんでしたか?これは、あなたのフィールドが実際にタイプjson[](JSONオブジェクトのPostgres配列)であるためです。このマッパーは、JSONオブジェクト(タイプjson)でのみ動作します。このJSONオブジェクトは、JSONオブジェクトのJSON配列ですが、jsonである必要があります。したがって、データベーススキーマの型を変更するか、配列で動作するUserTypeを実装する必要がありますが、最初のオプションが最も可能性があります。

0

私にとってはこの作品:

あなたのエンティティ:

@Entity 
@Data 
@AllArgsConstructor 
@NoArgsConstructor 
@TypeDef(name = "json", typeClass = JSONUserType.class, parameters = { 
     @Parameter(name = JSONUserType.CLASS, value = "java.lang.String")}) 
    public class HistoryEntity { 

     @Id 
     private String id; 
     private String userid; 
     private String type; 
     @Type(type = "json") 
     private String history; 
     private Date lastchanged; 

    } 

は、2種類の間の変換を確実にするためにHibernateは、ParameterizedTypeとのUserTypeを実装(JSON < - >文字列)

public class JSONUserType implements ParameterizedType, UserType { 

    private static final ObjectMapper objectMapper = new ObjectMapper(); 
    private static final ClassLoaderService classLoaderService = new ClassLoaderServiceImpl(); 

    public static final String JSON_TYPE = "json"; 
    public static final String CLASS = "CLASS"; 

    private Class jsonClassType; 

    @Override 
    public Class<Object> returnedClass() { 
     return Object.class; 
    } 

    @Override 
    public int[] sqlTypes() { 
     return new int[]{Types.JAVA_OBJECT}; 
    } 

    @Override 
    public Object nullSafeGet(ResultSet resultSet, String[] names, SessionImplementor session, Object owner) throws HibernateException, SQLException { 
     try { 
      final String json = resultSet.getString(names[0]); 
      return json == null ? null : objectMapper.readValue(json, jsonClassType); 
     } catch (IOException e) { 
      throw new HibernateException(e); 
     } 
    } 

    @Override 
    public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) throws HibernateException, SQLException { 
     try { 
      final String json = value == null ? null : objectMapper.writeValueAsString(value); 
      PGobject pgo = new PGobject(); 
      pgo.setType(JSON_TYPE); 
      pgo.setValue(json); 
      st.setObject(index, pgo); 
     } catch (JsonProcessingException e) { 
      throw new HibernateException(e); 
     } 
    } 

    @Override 
    public void setParameterValues(Properties parameters) { 
     final String clazz = (String) parameters.get(CLASS); 
     jsonClassType = classLoaderService.classForName(clazz); 
    } 

    @SuppressWarnings("unchecked") 
    @Override 
    public Object deepCopy(Object value) throws HibernateException { 

     if (!(value instanceof Collection)) { 
      return value; 
     } 

     Collection<?> collection = (Collection) value; 
     Collection collectionClone = CollectionFactory.newInstance(collection.getClass()); 

     collectionClone.addAll(collection.stream().map(this::deepCopy).collect(Collectors.toList())); 

     return collectionClone; 
    } 

    static final class CollectionFactory { 
     @SuppressWarnings("unchecked") 
     static <E, T extends Collection<E>> T newInstance(Class<T> collectionClass) { 
      if (List.class.isAssignableFrom(collectionClass)) { 
       return (T) new ArrayList<E>(); 
      } else if (Set.class.isAssignableFrom(collectionClass)) { 
       return (T) new HashSet<E>(); 
      } else { 
       throw new IllegalArgumentException("Unsupported collection type : " + collectionClass); 
      } 
     } 
    } 

    @Override 
    public boolean isMutable() { 
     return true; 
    } 

    @Override 
    public boolean equals(Object x, Object y) throws HibernateException { 
     if (x == y) { 
      return true; 
     } 

     if ((x == null) || (y == null)) { 
      return false; 
     } 

     return x.equals(y); 
    } 

    @Override 
    public int hashCode(Object x) throws HibernateException { 
     assert (x != null); 
     return x.hashCode(); 
    } 

    @Override 
    public Object assemble(Serializable cached, Object owner) throws HibernateException { 
     return deepCopy(cached); 
    } 

    @Override 
    public Serializable disassemble(Object value) throws HibernateException { 
     Object deepCopy = deepCopy(value); 

     if (!(deepCopy instanceof Serializable)) { 
      throw new SerializationException(String.format("%s is not serializable class", value), null); 
     } 

     return (Serializable) deepCopy; 
    } 

    @Override 
    public Object replace(Object original, Object target, Object owner) throws HibernateException { 
     return deepCopy(original); 
    } 
} 

そして、PostgreSQL94Dialectクラスを拡張して、シリアライザに一致する型を伝えます:

public class JSONPostgreSQLDialect extends PostgreSQL94Dialect { 

    public JSONPostgreSQLDialect() { 
     super(); 
     registerColumnType(Types.JAVA_OBJECT, JSONUserType.JSON_TYPE); 
    } 
} 

あなたが春を使用している場合は、このようなapplication.propertiesに、この最後のクラスを宣言する必要があります。 spring.jpa.databaseプラットフォーム= com.yourpackage.JSONPostgreSQLDialect

関連する問題