javaoracle-databasehibernatecustom-type

Implementing Custom hibernate type


I am trying to implement hibernate mapping to sample schema (Order Entry) provided by Oracle default installation The orderentry schema has several table one of them is Customer table which is having few Columns as customTyped columns for example the custom type is created with the following statement ;

CREATE OR REPLACE TYPE "CUST_ADDRESS_TYP"  AS OBJECT
( street_address     VARCHAR2(40)
, postal_code        VARCHAR2(10)
, city               VARCHAR2(30)
, state_province     VARCHAR2(10)
, country_id         CHAR(2)
);

and the customer table is created with the below statement

CREATE TABLE OE.CUSTOMERS 
( 
 CUSTOMER_ID NUMBER (6)  NOT NULL , 
 CUST_FIRST_NAME VARCHAR2 (20 BYTE) 
 CONSTRAINT CUST_FNAME_NN NOT NULL , 
 CUST_LAST_NAME VARCHAR2 (20 BYTE) 
 CONSTRAINT CUST_LNAME_NN NOT NULL , 
 CUST_ADDRESS OE.CUST_ADDRESS_TYP , 
 )

I came to know that we need to implement the UserType interface from here but when I try to retrieve it was giving the error

 org.hibernate.MappingException: property mapping has wrong number of columns: com.dto.oe.Customers.cust_addressData type: com.dto.oe.CustAddressType
            at org.hibernate.mapping.PersistentClass.validate(PersistentClass.java:497)
at org.hibernate.mapping.RootClass.validate(RootClass.java:270)
at org.hibernate.cfg.Configuration.validate(Configuration.java:1360)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1851)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1930)
at com.test.connection.HibernateUtil.getSession(HibernateUtil.java:82)
at com.test.connection.HibernateUtil.getCustomer(HibernateUtil.java:70)
at com.test.connection.HibernateUtil.main(HibernateUtil.java:18)

Wheras my Customtype object pojo class is Cust_addressData and the class which implemented usertype interface is CustAddressType

    import java.io.Serializable;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.ResultSetMetaData;
    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 CustAddressType implements UserType {
    /**
     * Returns the object from the 2 level cache
     */
    public Object assemble(Serializable cached, Object owner)
            throws HibernateException {
        return cached;
    }

    /**
     * Used to create Snapshots of the object
     */
    public Object deepCopy(Object value) throws HibernateException {
        final Cust_addressData recievedParam = (Cust_addressData) value;
        final Cust_addressData addressData = new Cust_addressData(recievedParam);
        return addressData;
    }

    /**
     * method called when Hibernate puts the data in a second level cache. The
     * data is stored in a serializable form
     */
    public Serializable disassemble(final Object value)
            throws HibernateException {
        return (Serializable) value;
    }

    public boolean equals(final Object o1, final Object o2)
            throws HibernateException {
        boolean isEqual = false;
        if (o1 == o2) {
            isEqual = true;
        }
        if (null == o1 || null == o2) {
            isEqual = false;
        } else {
            isEqual = o1.equals(o2);
        }
        return isEqual;
        // for this to work correctly the equals()
        // method must be implemented correctly by Cust_addressData class
    }

    public int hashCode(final Object arg0) throws HibernateException {
        return arg0.hashCode();
    }

    public boolean isMutable() {
        return true;
    }

    public Object nullSafeGet(final ResultSet resultSet, final String[] names,
            SessionImplementor sessionImp, final Object owner)
            throws HibernateException, SQLException {

        // owner here is class from where the call to retrieve data was made.
        // In this case the Test class

        Cust_addressData addresssData = new Cust_addressData();
        // Order of columns is given by sqlTypes() method
        if (!resultSet.wasNull()) {


            final String street_address = resultSet.getString(names[0]);
            final String postal_code = resultSet.getString(names[1]);
            final String city = resultSet.getString(names[2]);
            final String state_province = resultSet.getString(names[3]);
            final String country_id = resultSet.getString(names[4]);

            addresssData.setCity(city);
            addresssData.setCountry_id(country_id);
            addresssData.setPostal_code(postal_code);
            addresssData.setState_province(state_province);
            addresssData.setStreet_address(street_address);
            System.out.println("street_address "+street_address +"  names  "+names[0]);
        } else {
            System.err.println("resultSet is null in CustAddressType");
        }
        return addresssData;
    }

    public void nullSafeSet(final PreparedStatement statement, final Object value, final int index,
            SessionImplementor arg3) throws HibernateException, SQLException {
        if (null == value) {
            statement.setNull(index, Types.VARCHAR);
            statement.setNull(index + 1, Types.VARCHAR);
            statement.setNull(index + 2, Types.VARCHAR);
            statement.setNull(index + 3,Types.VARCHAR);
            statement.setNull(index + 4,Types.VARCHAR);
        } else {
            Cust_addressData addressData = (Cust_addressData) value;

            if (null != addressData.getStreet_address()) {
                String  street_address = new String(addressData.getStreet_address());
                statement.setString(index , street_address);
            } else {
                statement.setNull(index , Types.VARCHAR);
            }

            if (null != addressData.getPostal_code()) {
                 String  postal_Code = new String(addressData.getPostal_code());
                 statement.setString(index+1 , postal_Code);
            } else {
                statement.setNull(index +1, Types.VARCHAR);
            }

            if (null != addressData.getCity()) {
             String  city = new String(addressData.getCity());
                statement.setString(index+2 , city);
           } else {
               statement.setNull(index +2, Types.VARCHAR);
           }

            if (null != addressData.getState_province()) {
             String  postal_Code = new String(addressData.getState_province());
                statement.setString(index+3 , postal_Code);
           } else {
               statement.setNull(index +3, Types.VARCHAR);
           }


            if (null != addressData.getCountry_id()) {
             String  postal_Code = new String(addressData.getCountry_id());
                statement.setString(index+4 , postal_Code);
           } else {
               statement.setNull(index +4, Types.VARCHAR);
           }

        }

    }

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

    @SuppressWarnings("rawtypes")
    public Class returnedClass() {
        return Cust_addressData.class;
    }

    public int[] sqlTypes() {
        return new int[] { Types.VARCHAR, Types.VARCHAR, Types.VARCHAR,
                Types.VARCHAR, Types.VARCHAR };
    }
}
     import java.io.Serializable;
     import javax.persistence.Column;
     import javax.persistence.Entity;
     import javax.persistence.Table;
    @SuppressWarnings("serial")
    @Entity
    @Table(name="CUST_ADDRESS_TYP")
    public class Cust_addressData implements Serializable {
    /**
     * 
     */
private static final long serialVersionUID = 1L;
    @Column(name = "street_address")
    private String street_address;
    @Column(name = "postal_code")
    private String postal_code;
    @Column(name = "city")
    private String city;
    @Column(name = "state_province")
    private String state_province;
    @Column(name = "country_id")
    private String country_id;

    public Cust_addressData() {
    }

    public Cust_addressData(Cust_addressData other) {
        this.setCity(other.getCity());
        this.setCountry_id(other.getCountry_id());
        this.setPostal_code(other.getPostal_code());
        this.setStreet_address(other.getStreet_address());
        this.setState_province(other.getState_province());

    }

    public String getStreet_address() {
        return street_address;
    }

    public void setStreet_address(String street_address) {
        this.street_address = street_address;
    }

    public String getPostal_code() {
        return postal_code;
    }

    public void setPostal_code(String postal_code) {
        this.postal_code = postal_code;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getState_province() {
        return state_province;
    }

    public void setState_province(String state_province) {
        this.state_province = state_province;
    }

    public String getCountry_id() {
        return country_id;
    }

    public void setCountry_id(String country_id) {
        this.country_id = country_id;
    }

    @Override
    public boolean equals(Object obj) {

        boolean isEqual = false;
        if (obj instanceof Cust_addressData) {
            Cust_addressData addressData = (Cust_addressData) obj;

            isEqual = addressData.getCity().equals(this.getCity())
                    && addressData.getState_province().equals(this.getState_province())
                    && addressData.getCountry_id().equals(this.getCountry_id())
                    && addressData.getPostal_code().equals(this.getPostal_code())
                    && addressData.getStreet_address().equals(this.getStreet_address())
                    ;
        }
        return isEqual;
    }

    @Override
    public int hashCode() {
        int hash = this.getCountry_id().hashCode();
        hash = hash * 17 + this.getPostal_code().hashCode();
        hash = hash * 31 + this.getStreet_address().hashCode();
        hash = hash * 13 + this.getState_province().hashCode();
        hash = hash * 14 + this.getCity().hashCode();
        return hash;
    }

    @Override
    public String toString() {

        return "[ " + this.getClass() + " { city : " + city
                + ", street_address: " + street_address + ", postal_code: "
                + postal_code + ", state_province: " + state_province + ""
                        + ",country_id :"+country_id+"}]";
    }
}

Full sources:

One more observation is the sqlTypes method which is in CustAddressType class

public int[] sqlTypes() {
    return new int[] { Types.VARCHAR, Types.VARCHAR, Types.VARCHAR,
        Types.VARCHAR, Types.VARCHAR };
}

is the one which is returning the number of columns if i make it to return only one column i'm getting the column value as null.

not sure exactly what is goin wrong,Any suggestion where I am doing wrong or any clue will be appreciated


Solution

  • Using STRUCT class in my CustAddressType class has resolved the issue the source code the CustAddressType class is as follows.

    import java.io.Serializable;
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Struct;
    import java.sql.Types;
    
    import oracle.sql.STRUCT;
    import oracle.sql.StructDescriptor;
    
    import org.hibernate.HibernateException;
    import org.hibernate.engine.spi.SessionImplementor;
    import org.hibernate.usertype.UserType;
    
    public class CustAddressType implements UserType {
        private static final int SQL_TYPE = Types.STRUCT;
        private static final String OBJECT_TYPE = "CUST_ADDRESS_TYP";
        /**
         * Returns the object from the 2 level cache
         */
        public Object assemble(Serializable cached, Object owner)
            throws HibernateException {
        return cached;
    }
    
    /**
     * Used to create Snapshots of the object
     */
    public Object deepCopy(Object value) throws HibernateException {
        if (value == null) {
            return null;
        }
        final Cust_addressData recievedParam = (Cust_addressData) value;
        final Cust_addressData addressData = new Cust_addressData(recievedParam);
    
        return addressData;
    }
    
    /**
     * method called when Hibernate puts the data in a second level cache. The
     * data is stored in a serializable form
     */
    public Serializable disassemble(final Object value)
            throws HibernateException {
        return (Serializable) value;
    }
    
    public boolean equals(final Object o1, final Object o2)
            throws HibernateException {
        boolean isEqual = false;
        if (o1 == o2) {
            isEqual = true;
        }
        if (null == o1 || null == o2) {
            isEqual = false;
        } else {
            isEqual = o1.equals(o2);
        }
        return isEqual;
        // for this to work correctly the equals()
        // method must be implemented correctly by Cust_addressData class
    }
    
    public int hashCode(final Object arg0) throws HibernateException {
        return arg0.hashCode();
    }
    
    public boolean isMutable() {
        return true;
    }
    
    public Object nullSafeGet(final ResultSet resultSet, final String[] names,
            SessionImplementor sessionImp, final Object owner)
            throws HibernateException, SQLException {
    
        // owner here is class from where the call to retrieve data was made.
        // In this case the Test class
    
        final Cust_addressData addresssData = new Cust_addressData();
    
        final Struct struct = (Struct) resultSet.getObject(names[0]);
    
        if (resultSet.wasNull()) {
            return null;
        }
        addresssData.setCity((String)struct.getAttributes()[0]);
        addresssData.setCountry_id((String)struct.getAttributes()[1]);
        addresssData.setPostal_code((String)struct.getAttributes()[2]);
        addresssData.setState_province((String)struct.getAttributes()[3]);
        addresssData.setStreet_address((String)struct.getAttributes()[4]);
    
        return addresssData;
    }
    
    public void nullSafeSet(final PreparedStatement statement, final Object value, final int index,
            SessionImplementor arg3) throws HibernateException, SQLException {
        if (value == null) {
            statement.setNull(index, SQL_TYPE, OBJECT_TYPE);
        } 
        else {
            final Cust_addressData addresssData = (Cust_addressData) value;
            final Object[] values = new Object[] { addresssData.getCity(),addresssData.getCountry_id(), addresssData.getPostal_code(),
                    addresssData.getState_province(),addresssData.getStreet_address() };
            final Connection connection = statement.getConnection();
            final STRUCT struct = new STRUCT(StructDescriptor.createDescriptor( OBJECT_TYPE,connection), connection, values);
            statement.setObject(index, struct, SQL_TYPE);
        }
    }
    
    public Object replace(final Object original, final Object target,
            final Object owner) throws HibernateException {
        return this.deepCopy(original);
    }
    
    @SuppressWarnings("rawtypes")
    public Class returnedClass() {
        return Cust_addressData.class;
    }
    
    public int[] sqlTypes() {
        return new int[] {SQL_TYPE};
    }
    }
    

    Thanks to @SteveChambers for pointing it out,