Jpa带注释的自定义用户类型错误属性映射列数错误

时间:2016-03-16 13:03:17

标签: java spring hibernate jpa

我最近使用自定义的hibernate UserType映射了一个类的字段。

这是我的自定义用户类型


package service.dao.hibernate;

import java.io.IOException;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Properties;

import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.usertype.ParameterizedType;
import org.hibernate.usertype.UserType;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.SimpleType;
import com.google.common.base.Objects;

public abstract class JSONUserType implements UserType { //ParameterizedType, Serializable {
    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    private static final ObjectMapper Mapper;

    private static final String CLASS_TYPE = "classType";
    private static final String TYPE = "type";

    private static final int[] SQL_TYPES = new int[] { Types.LONGVARCHAR,
            Types.CLOB, Types.BLOB };

    private Class classType;
    private int sqlType = Types.LONGVARCHAR; // before any guessing

    static {
        Mapper = new ObjectMapper();
        Mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    }

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

    @Override
    public Object deepCopy(Object value) throws HibernateException {
        Object copy = null;
        if (value != null) {

            try {
                return Mapper.readValue(Mapper.writeValueAsString(value),
                        this.classType);
            } catch (IOException e) {
                throw new HibernateException("unable to deep copy object", e);
            }
        }
        return copy;
    }

    @Override
    public Serializable disassemble(Object value) throws HibernateException {
        try {
            return Mapper.writeValueAsString(value);
        } catch (JsonProcessingException e) {
            throw new HibernateException("unable to disassemble object", e);
        }
    }

    @Override
    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);
            }
    }

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

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

    @Override
    public Object nullSafeGet(ResultSet rs, String[] names,
            SessionImplementor session, Object owner)
            throws HibernateException, SQLException {
        Object obj = null;
        if (!rs.wasNull()) {
            if (this.sqlType == Types.CLOB || this.sqlType == Types.BLOB) {
                byte[] bytes = rs.getBytes(names[0]);
                if (bytes != null) {
                    try {
                        obj = Mapper.readValue(bytes, createJavaType(Mapper));
                    } catch (IOException e) {
                        throw new HibernateException(
                                "unable to read object from result set", e);
                    }
                }
            } else {
                try {
                    String content = rs.getString(names[0]);
                    if (content != null) {
                        obj = Mapper.readValue(content, createJavaType(Mapper));
                    }
                } catch (IOException e) {
                    throw new HibernateException(
                            "unable to read object from result set", e);
                }
            }
        }
        return obj;
    }

    @Override
    public void nullSafeSet(PreparedStatement st, Object value, int index,
            SessionImplementor session) throws HibernateException, SQLException {
        if (value == null) {
            st.setNull(index, this.sqlType);
        } else {

            if (this.sqlType == Types.CLOB || this.sqlType == Types.BLOB) {
                try {
                    st.setBytes(index, Mapper.writeValueAsBytes(value));
                } catch (JsonProcessingException e) {
                    throw new HibernateException(
                            "unable to set object to result set", e);
                }
            } else {
                try {
                    st.setString(index, Mapper.writeValueAsString(value));
                } catch (JsonProcessingException e) {
                    throw new HibernateException(
                            "unable to set object to result set", e);
                }
            }
        }
    }

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

//  @Override
//  public Class returnedClass() {
//      return this.classType;
//  }

    @Override
    public int[] sqlTypes() {
        return SQL_TYPES;
    }

//  @Override
//  public void setParameterValues(Properties params) {
//      String classTypeName = params.getProperty(CLASS_TYPE);
//      try {
//          this.classType = ReflectHelper.classForName(classTypeName,
//                  this.getClass());
//      } catch (ClassNotFoundException cnfe) {
//          throw new HibernateException("classType not found", cnfe);
//      }
//      String type = params.getProperty(TYPE);
//      if (type != null) {
//          this.sqlType = Integer.decode(type).intValue();
//      }
//  }

    /**
     * By default we are expecting to use a simple object / not a collection (Set, List)
     *
     * @param mapper : instance jackson object mapper
     *
     * @return A jackson JavaType to specify wich object represent the json string representation
     *
     */
    public JavaType createJavaType (ObjectMapper mapper){
        return SimpleType.construct(returnedClass());
    }

}

这是特定的用户类型


package model.common;

import service.dao.hibernate.JSONUserType;

public class DocumentInfoType extends JSONUserType {

    @Override
    public Class returnedClass() {
        return DocumentInfo.class;
    }
}

这是我的自定义类型字段的实体


package model.common;

import model.SimpleAuditedEntity;
import model.lk.DocumentMode;
import model.lk.DocumentType;
import service.dao.hibernate.JSONUserType;

import java.io.Serializable;

import javax.persistence.*;

import org.hibernate.annotations.Parameter;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef;
import org.hibernate.annotations.TypeDefs;

import java.sql.Timestamp;

/**
 * The persistent class for the documents database table.
 * 
 */
@Entity
@Table(name = "documents")
@NamedQuery(name = "Document.findAll", query = "SELECT d FROM Document d")
public class Document extends SimpleAuditedEntity implements Serializable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @Column(name = "content_type")
    private String contentType;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "type")
    private DocumentType documentType;

    @Column
    private Timestamp created;

    @Column
    private String description;

    @Column
    private String filename;

    @Column
    private String name;

    @Column
    private String ref;

    @Type(type = "model.common.DocumentInfoType")
    @Column
    private DocumentInfo info;

    public Document() {
    }

    public int getId() {
        return this.id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getContentType() {
        return this.contentType;
    }

    public void setContentType(String contentType) {
        this.contentType = contentType;
    }

    public Timestamp getCreated() {
        return this.created;
    }

    public void setCreated(Timestamp created) {
        this.created = created;
    }

    public String getDescription() {
        return this.description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getFilename() {
        return this.filename;
    }

    public void setFilename(String filename) {
        this.filename = filename;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getRef() {
        return this.ref;
    }

    public void setRef(String ref) {
        this.ref = ref;
    }

    /**
     * @return the documentType
     */
    public DocumentType getDocumentType() {
        return documentType;
    }

    /**
     * @param documentType
     *            the documentType to set
     */
    public void setDocumentType(DocumentType documentType) {
        this.documentType = documentType;
    }

    public DocumentMode getDocumentMode() {
        return this.documentType != null ? DocumentMode
                .getType(this.documentType.getId()) : DocumentMode.UNDEFINED;
    }

    /**
     * @return the info
     */
    public DocumentInfo getInfo() {
        return info;
    }

    /**
     * @param info the info to set
     */
    public void setInfo(DocumentInfo info) {
        this.info = info;
    }
}

问题是当我启动应用程序时,我立即得到异常

Caused by: org.hibernate.MappingException: property mapping has wrong number of columns: model.common.Document.info type: model.common.DocumentInfoType
    at org.hibernate.mapping.PersistentClass.validate(PersistentClass.java:497) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
    at org.hibernate.mapping.RootClass.validate(RootClass.java:270) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
    at org.hibernate.cfg.Configuration.validate(Configuration.java:1360) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1851) [hibernate-core-4.3.7.Final.jar:4.3.7.Final]
    at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$4.perform(EntityManagerFactoryBuilderImpl.java:852) [hibernate-entitymanager-4.3.7.Final.jar:4.3.7.Final]

有什么想法吗?我已经映射了所有列,但我尝试了很多修改但没有任何修改!

提前致谢

1 个答案:

答案 0 :(得分:3)

从JSONUserType.sqlTypes()返回包含3个元素的SQLTypes数组:

private static final int[] SQL_TYPES = new int[] { Types.LONGVARCHAR,
        Types.CLOB, Types.BLOB };

这告诉hibernate你的类型映射到3列。 您应该只选择其中一种类型。

请参阅UserType.sqlTypes()的javadoc:

  

返回此类型映射的列的SQL类型代码