Mapping custom column type with PostgreSQL and Hibernate

#1

Hi everybody,

I’m trying to map a custom column type. But, when I get the result I have to split the column’s values as the custom type is stored as (v1, v2, v3, v4, v5, v6, v7); it works!

But, when I try to save a new row in the db withe the following short example:

	Transaction tr = session.beginTransaction();
	LoggedUser.logIn("test");
	
	Fields p = new Fields();
	CustomField cf = new CustomField();
	cf.setF_int(7);
	cf.setF_long(235L);
	p.setFieldtypeID(1);
	p.setCustomfield(cf);
	p.setId(0);
	
	session.save(p);

I have the message:

[19-12-2018 09:13:08.074] [main 1] ERROR org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions - ERROR: malformed record literal: "luminare.model.CustomField@d1d8e1a"
  Detail: Missing left parenthesis.

Any suggestion? Thanks in advance

These are the software parts:
SQL

CREATE TYPE customfield AS
(
       f_int                      INTEGER,
       f_long                     BIGINT,
       f_float                    DOUBLE PRECISION,
       f_location                 POINT,
       f_date                     DATE,
       f_text                     TEXT,
       f_boolean                  BOOLEAN);

CREATE TABLE fieldtype
(
       ID                         SERIAL PRIMARY KEY,
       tag                        VARCHAR(100) NOT NULL,
       type                       CHAR(1) NOT NULL,
       insert_date                TIMESTAMP NOT NULL DEFAULT NOW(),
       insert_user                VARCHAR(100) NOT NULL,
       update_date                TIMESTAMP NULL,
       update_user                VARCHAR(100) NULL);

CREATE TABLE fields
(
       ID                         SERIAL PRIMARY KEY,
       fieldtypeID                INTEGER NOT NULL REFERENCES fieldtype(ID) ON DELETE CASCADE ON UPDATE CASCADE,
       fieldvalue                 customfield,
       insert_date                TIMESTAMP NOT NULL DEFAULT NOW(),
       insert_user                VARCHAR(100) NOT NULL,
       update_date                TIMESTAMP NULL,
       update_user                VARCHAR(100) NULL);

Java mapping classes:

package luminare.model;

import java.io.Serializable;
import java.util.Date;

public class CustomField implements Serializable {
	private static final long serialVersionUID = 4044211232164799826L;
	private Integer f_int;
	private Long f_long;
	private Double f_float;
//	private Point f_location;
	private Date f_date;
	private String f_text;
	private Boolean f_boolean;
	
	public CustomField() {}

	// Copy constructor
	public CustomField(CustomField other) {
		this.setF_int(other.getF_int());
		this.setF_long(other.getF_long());
		this.setF_float(other.getF_float());
		this.setF_date(other.getF_date());
		this.setF_text(other.getF_text());
		this.setF_boolean(other.getF_boolean());
	}

	public CustomField(Integer f_int, Long f_long, Double f_float, Date f_date, String f_text, Boolean f_boolean) {
		this.f_int = f_int;
		this.f_long = f_long;
		this.f_float = f_float;
		this.f_date = f_date;
		this.f_text = f_text;
		this.f_boolean = f_boolean;
	}

	public Integer getF_int() {
		return f_int;
	}

	public void setF_int(Integer f_int) {
		this.f_int = f_int;
	}

	public Long getF_long() {
		return f_long;
	}

	public void setF_long(Long f_long) {
		this.f_long = f_long;
	}

	public Double getF_float() {
		return f_float;
	}

	public void setF_float(Double f_float) {
		this.f_float = f_float;
	}

/*	public Point getF_location() {
		return f_location;
	}

	public void setF_location(Point f_location) {
		this.f_location = f_location;
	} */

	public Date getF_date() {
		return f_date;
	}

	public void setF_date(Date f_date) {
		this.f_date = f_date;
	}

	public String getF_text() {
		return f_text;
	}

	public void setF_text(String f_text) {
		this.f_text = f_text;
	}

	public Boolean getF_boolean() {
		return f_boolean;
	}

	public void setF_boolean(Boolean f_boolean) {
		this.f_boolean = f_boolean;
	}
}
package luminare.model;

import java.io.Serializable;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.sql.Types;
import java.time.LocalDateTime;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.usertype.UserType;

import lt.locale.DateUtils;

public class FieldValueType implements UserType {
	private final static Logger logger = LogManager.getLogger(FieldValueType.class);

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

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

	@Override
	public int hashCode(Object x) throws HibernateException {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner)
			throws HibernateException, SQLException {
		Integer f_int = null;
		Long f_long = null;
		Double f_float = null;
		Date f_date = null;
		String f_text = null;
		Boolean f_boolean = null;
		
		logger.debug("names[0] = " + names[0] + " value = " + rs.getString(names[0]) + " length = " + names.length);
		
		String[] values = rs.getString(names[0]).replace("(", "").replace(")", "").split(",");
		logger.debug("values length = " + values.length);
		
		for(int i = 0; i < values.length; i++) {
			switch(i) {
			case 0:
				logger.debug("values[" + i + "] = " + values[i]);
				if(values[i].length() > 0)
					f_int = Integer.parseInt(values[i]);
				break;

			case 1:
				logger.debug("values[" + i + "] = " + values[i]);
				if(values[i].length() > 0)
					f_long = Long.valueOf(values[i]);
				break;

			case 2:
				logger.debug("values[" + i + "] = " + values[i]);
				if(values[i].length() > 0)
					f_float = Double.valueOf(values[i]);
				break;

			case 3:
				logger.debug("values[" + i + "] = " + values[i]);
				if(values[i].length() > 0)
					f_date = Date.valueOf(values[i]);
				break;

			case 4:
				logger.debug("values[" + i + "] = " + values[i]);
				if(values[i].length() > 0)
					f_text = values[i];
				break;

			case 5:
				logger.debug("values[" + i + "] = " + values[i]);
				if(values[i].length() > 0)
					f_boolean = Boolean.valueOf(values[i]);
				break;
			}
		}
		
		CustomField customField = new CustomField(f_int, f_long, f_float, f_date, f_text, f_boolean);
		return customField;
	}

	@Override
	public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session)
			throws HibernateException, SQLException {
		
		if(value == null) {
			logger.debug("Custom field is NULL");
			st.setNull(index, Types.OTHER);
		}
		else {
			final CustomField customField = (CustomField) value;
			
			logger.debug("Setting custom field " + customField);
			st.setObject(index, customField, Types.OTHER);
		}
	}

	@Override
	public Object deepCopy(Object value) throws HibernateException {
		return value;
	}

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

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

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

	@Override
	public Object replace(Object original, Object target, Object owner) throws HibernateException {
		return original;
	}
	
	@Override
	public boolean equals(Object x, Object y)  throws HibernateException {
		return x.equals(y);
	}
}
package luminare.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;

import org.hibernate.annotations.TypeDef;

import lt.model.LTTable;
import luminare.database.LUDBConfig;


@Entity(name = "Fields")
@Table(name = "fields", schema = LUDBConfig.LUSchema)
@TypeDef(
		name = "fieldvalue",
		typeClass = FieldValueType.class,
		defaultForType = CustomField.class
	)
public class Fields extends LTTable {
	private static final long serialVersionUID = 8439931276045890645L;

	@Column(name = "fieldtypeID", nullable = false, updatable = false)
	private Integer fieldtypeID;

	@Column(name = "fieldvalue", columnDefinition = "customfield", nullable = false, insertable = true)
	private CustomField customfield;

	
	/*	
	@ElementCollection(fetch = FetchType.LAZY)
	@CollectionTable(
	    name="luminaretags", schema = LUDBConfig.LUSchema,
	    joinColumns=@JoinColumn(name = "tag", insertable = false, updatable = false, nullable = false))
	@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
	private Set<LuminareTags> luminareTags = new HashSet<>(); */ 

	
	public Integer getFieldtypeID() {
		return fieldtypeID;
	}


	public void setFieldtypeID(Integer fieldtypeID) {
		this.fieldtypeID = fieldtypeID;
	}


	public CustomField getCustomfield() {
		return customfield;
	}


	public void setCustomfield(CustomField customfield) {
		this.customfield = customfield;
	}


	public Fields() {}
}
0 Likes

#2

It’s not clear from the error message what the problem is. You need to add the SQL code generated by Hibernate and the log at the moment the exception is thrown to see exactly what the Driver has complaint about.

0 Likes

#3

Hi Vlad, this is the logging messages, hope this helps:

[19-12-2018 09:13:08.051] [main 1] DEBUG org.hibernate.engine.spi.ActionQueue addResolvedEntityInsertAction - Executing identity-insert immediately [19-12-2018 09:13:08.052] [main 1] DEBUG org.hibernate.SQL logStatement - /* insert luminare.model.Fields */ insert into plugin_luminare3.fields (insert_user, update_date, update_user, fieldvalue, fieldtypeID) values (?, ?, ?, ?, ?) Hibernate: /* insert luminare.model.Fields */ insert into plugin_luminare3.fields (insert_user, update_date, update_user, fieldvalue, fieldtypeID) values (?, ?, ?, ?, ?) [19-12-2018 09:13:08.053] [main 1] DEBUG luminare.model.FieldValueType nullSafeSet - Setting custom field luminare.model.CustomField@d1d8e1a [19-12-2018 09:13:08.069] [main 1] DEBUG org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions - could not execute statement [n/a] org.postgresql.util.PSQLException: ERROR: malformed record literal: "luminare.model.CustomField@d1d8e1a" Detail: Missing left parenthesis. at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2440) ~[postgresql-42.2.5.jar:42.2.5] at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2183) ~[postgresql-42.2.5.jar:42.2.5] at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:308) ~[postgresql-42.2.5.jar:42.2.5] at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:441) ~[postgresql-42.2.5.jar:42.2.5] at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:365) ~[postgresql-42.2.5.jar:42.2.5] at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:143) ~[postgresql-42.2.5.jar:42.2.5] at org.postgresql.jdbc.PgPreparedStatement.executeUpdate(PgPreparedStatement.java:120) ~[postgresql-42.2.5.jar:42.2.5] at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:175) [hibernate-core-5.3.7.Final.jar:5.3.7.Final] at org.hibernate.dialect.identity.GetGeneratedKeysDelegate.executeAndExtract(GetGeneratedKeysDelegate.java:57) [hibernate-core-5.3.7.Final.jar:5.3.7.Final] at org.hibernate.id.insert.AbstractReturningDelegate.performInsert(AbstractReturningDelegate.java:42) [hibernate-core-5.3.7.Final.jar:5.3.7.Final] at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3072) [hibernate-core-5.3.7.Final.jar:5.3.7.Final] at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:3663) [hibernate-core-5.3.7.Final.jar:5.3.7.Final] at org.hibernate.action.internal.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.java:81) [hibernate-core-5.3.7.Final.jar:5.3.7.Final] at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:645) [hibernate-core-5.3.7.Final.jar:5.3.7.Final] at org.hibernate.engine.spi.ActionQueue.addResolvedEntityInsertAction(ActionQueue.java:282) [hibernate-core-5.3.7.Final.jar:5.3.7.Final] at org.hibernate.engine.spi.ActionQueue.addInsertAction(ActionQueue.java:263) [hibernate-core-5.3.7.Final.jar:5.3.7.Final] at org.hibernate.engine.spi.ActionQueue.addAction(ActionQueue.java:317) [hibernate-core-5.3.7.Final.jar:5.3.7.Final] at org.hibernate.event.internal.AbstractSaveEventListener.addInsertAction(AbstractSaveEventListener.java:359) [hibernate-core-5.3.7.Final.jar:5.3.7.Final] at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:292) [hibernate-core-5.3.7.Final.jar:5.3.7.Final] at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:200) [hibernate-core-5.3.7.Final.jar:5.3.7.Final] at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:131) [hibernate-core-5.3.7.Final.jar:5.3.7.Final] at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:192) [hibernate-core-5.3.7.Final.jar:5.3.7.Final] at org.hibernate.event.internal.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:38) [hibernate-core-5.3.7.Final.jar:5.3.7.Final] at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:177) [hibernate-core-5.3.7.Final.jar:5.3.7.Final] at org.hibernate.event.internal.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:32) [hibernate-core-5.3.7.Final.jar:5.3.7.Final] at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:73) [hibernate-core-5.3.7.Final.jar:5.3.7.Final] at org.hibernate.internal.SessionImpl.fireSave(SessionImpl.java:709) [hibernate-core-5.3.7.Final.jar:5.3.7.Final] at org.hibernate.internal.SessionImpl.save(SessionImpl.java:701) [hibernate-core-5.3.7.Final.jar:5.3.7.Final] at org.hibernate.internal.SessionImpl.save(SessionImpl.java:696) [hibernate-core-5.3.7.Final.jar:5.3.7.Final] at luminare.MainTest.test(MainTest.java:94) [bin/:?] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_191] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_191] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_191] at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_191] at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) [junit-4.12.jar:4.12] at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) [junit-4.12.jar:4.12] at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) [junit-4.12.jar:4.12] at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) [junit-4.12.jar:4.12] at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) [junit-4.12.jar:4.12] at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) [junit-4.12.jar:4.12] at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) [junit-4.12.jar:4.12] at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) [junit-4.12.jar:4.12] at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) [junit-4.12.jar:4.12] at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) [junit-4.12.jar:4.12] at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) [junit-4.12.jar:4.12] at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) [junit-4.12.jar:4.12] at org.junit.runners.ParentRunner.run(ParentRunner.java:363) [junit-4.12.jar:4.12] at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86) [.cp/:?] at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) [.cp/:?] at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:538) [.cp/:?] at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:760) [.cp/:?] at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:460) [.cp/:?] at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:206) [.cp/:?] [19-12-2018 09:13:08.074] [main 1] WARN org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions - SQL Error: 0, SQLState: 22P02 [19-12-2018 09:13:08.074] [main 1] ERROR org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions - ERROR: malformed record literal: "luminare.model.CustomField@d1d8e1a" Detail: Missing left parenthesis.
0 Likes

#4

The problem comes from this line:

st.setObject(index, customField, Types.OTHER);

PostgreSQL does not know how to save a Java Object, so it calls toString() and that’s why you get the failure.

Now, UserType is for persisting data in one column only, but your custom PostgreSQL type is a struct, and it requires persisting data in multiple columns. Therefore, you should implement the CompositeType instead and set all the underlying columns that build the custom type.

0 Likes

#5

Thank you Vlad, I’ll try to do it and I’ll let you know

0 Likes

#6

I tried to do it, but it is not working yet. Hibernate maps the column of the custom field as table’s columns.
Maybe I did some mistakes in the mapping.

CustomField

package luminare.model;

import java.io.Serializable;
import java.util.Date;

import javax.persistence.Embeddable;

@Embeddable
public class CustomField implements Serializable {
	private static final long serialVersionUID = 4044211232164799826L;
	
	private Integer f_int;
	private Long f_long;
	private Double f_float;
	private Date f_date;
	private String f_text;
	private Boolean f_boolean;
	
	public CustomField() {}

	// Copy constructor
	public CustomField(CustomField other) {
		this.setF_int(other.getF_int());
		this.setF_long(other.getF_long());
		this.setF_float(other.getF_float());
		this.setF_date(other.getF_date());
		this.setF_text(other.getF_text());
		this.setF_boolean(other.getF_boolean());
	}

	public CustomField(Integer f_int, Long f_long, Double f_float, Date f_date, String f_text, Boolean f_boolean) {
		this.f_int = f_int;
		this.f_long = f_long;
		this.f_float = f_float;
		this.f_date = f_date;
		this.f_text = f_text;
		this.f_boolean = f_boolean;
	}

	public Integer getF_int() {
		return f_int;
	}

	public void setF_int(Integer f_int) {
		this.f_int = f_int;
	}

	public Long getF_long() {
		return f_long;
	}

	public void setF_long(Long f_long) {
		this.f_long = f_long;
	}

	public Double getF_float() {
		return f_float;
	}

	public void setF_float(Double f_float) {
		this.f_float = f_float;
	}

	public Date getF_date() {
		return f_date;
	}

	public void setF_date(Date f_date) {
		this.f_date = f_date;
	}

	public String getF_text() {
		return f_text;
	}

	public void setF_text(String f_text) {
		this.f_text = f_text;
	}

	public Boolean getF_boolean() {
		return f_boolean;
	}

	public void setF_boolean(Boolean f_boolean) {
		this.f_boolean = f_boolean;
	}
}

Composite Type

package luminare.model;

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

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.type.BooleanType;
import org.hibernate.type.DateType;
import org.hibernate.type.DoubleType;
import org.hibernate.type.IntegerType;
import org.hibernate.type.LongType;
import org.hibernate.type.StringType;
import org.hibernate.type.Type;
import org.hibernate.usertype.CompositeUserType;

public class FieldValueCompositeType implements CompositeUserType {
	private final static Logger logger = LogManager.getLogger(FieldValueCompositeType.class);
	
	public static final FieldValueCompositeType INSTANCE = new FieldValueCompositeType();

	@Override
	public String[] getPropertyNames() {
		return new String[] {
				"f_int",
				"f_long",
				"f_float",
				"f_date",
				"f_text",
				"f_boolean"
		};
	}

	@Override
	public Type[] getPropertyTypes() {
		return new Type[] {
				IntegerType.INSTANCE,
				LongType.INSTANCE,
				DoubleType.INSTANCE,
				DateType.INSTANCE,
				StringType.INSTANCE,
				BooleanType.INSTANCE
		};
	}

	@Override
	public Object getPropertyValue(Object component, int property) throws HibernateException {
		logger.debug("property index " + property); 
		CustomField customField = (CustomField) component;
		
		switch(property) {
		case 0:
			return customField.getF_int();
		case 1:
			return customField.getF_long();
		case 2:
			return customField.getF_float();
		case 3:
			return customField.getF_date();
		case 4:
			return customField.getF_text();
		case 5:
			return customField.getF_boolean();
		}
		logger.error(property + " is an invalid property index for class type");
		throw new IllegalArgumentException(property + " is an invalid property index for class type");
	}

	@Override
	public void setPropertyValue(Object component, int property, Object value) throws HibernateException {
		logger.debug("property index " + property); 
		CustomField customField = (CustomField) component;
		
		switch(property) {
		case 0:
			customField.setF_int((Integer) value);
		case 1:
			customField.setF_long((Long) value);
		case 2:
			customField.setF_float((Double) value);
		case 3:
			customField.setF_date((Date) value);
		case 4:
			customField.setF_text((String) value);
		case 5:
			customField.setF_boolean((Boolean) value);
		}
		logger.error(property + " is an invalid property index for class type");
		throw new IllegalArgumentException(property + " is an invalid property index for class type");
		
	}

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

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

	@Override
	public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner)
			throws HibernateException, SQLException {
		logger.debug("names " + names);
		
		CustomField customField = null;
		
		if(!rs.wasNull()) {
			customField = new CustomField();
			customField.setF_int(rs.getInt(names[0]));
			customField.setF_long(rs.getLong(names[1]));
			customField.setF_float(rs.getDouble(names[2]));
			customField.setF_date(rs.getDate(names[3]));
			customField.setF_text(rs.getString(names[4]));
			customField.setF_boolean(rs.getBoolean(names[5]));
		}
		return customField;

	}

	@Override
	public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session)
			throws HibernateException, SQLException {
		logger.debug("index " + index);
		if(value == null) {
			st.setNull(index,  Types.INTEGER);
			st.setNull(index + 1, Types.BIGINT);
			st.setNull(index + 2, Types.DOUBLE);
			st.setNull(index + 3, Types.DATE);
			st.setNull(index + 4, Types.VARCHAR);
			st.setNull(index + 5, Types.BOOLEAN);
		}
		else {
			final CustomField customField = (CustomField) value;
			st.setInt(index, customField.getF_int());
			st.setLong(index + 1, customField.getF_long());
			st.setDouble(index + 2, customField.getF_float());
			st.setDate(index + 3,java.sql.Date.valueOf(customField.getF_date().toString()));
			st.setBoolean(index + 3, customField.getF_boolean());			
		}
		
	}

	@Override
	public Object deepCopy(Object value) throws HibernateException {
		final CustomField receivedParam = (CustomField) value;
		final CustomField customField = new CustomField(receivedParam);
		return customField;
	}

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

	@Override
	public Serializable disassemble(Object value, SharedSessionContractImplementor session) throws HibernateException {
		return (Serializable) value;
	}

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

	@Override
	public Object replace(Object original, Object target, SharedSessionContractImplementor session, Object owner)
			throws HibernateException {
		return this.deepCopy(original);
	}
	
	@Override
	public int hashCode(final Object value)  throws HibernateException {
		return value.hashCode();
	}
}

Table mapping

package luminare.model;

import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.Table;

import org.hibernate.annotations.TypeDef;

import lt.model.LTTable;
import luminare.database.LUDBConfig;


@Entity(name = "Fields")
@Table(name = "fields", schema = LUDBConfig.LUSchema)
@TypeDef(
		name = "fieldvalue",
		typeClass = FieldValueCompositeType.class,
		defaultForType = CustomField.class
	)
public class Fields extends LTTable {
	private static final long serialVersionUID = 8439931276045890645L;

	@Column(name = "fieldtypeID", nullable = false, updatable = false)
	private Integer fieldtypeID;

	//@Column(name = "fieldvalue", nullable = false, insertable = true)
	@Embedded
	private CustomField customfield;

	
	/*	
	@ElementCollection(fetch = FetchType.LAZY)
	@CollectionTable(
	    name="luminaretags", schema = LUDBConfig.LUSchema,
	    joinColumns=@JoinColumn(name = "tag", insertable = false, updatable = false, nullable = false))
	@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
	private Set<LuminareTags> luminareTags = new HashSet<>(); */ 

	
	public Integer getFieldtypeID() {
		return fieldtypeID;
	}


	public void setFieldtypeID(Integer fieldtypeID) {
		this.fieldtypeID = fieldtypeID;
	}


	public CustomField getCustomfield() {
		return customfield;
	}


	public void setCustomfield(CustomField customfield) {
		this.customfield = customfield;
	}


	public Fields() {}
}

Error log

[19-12-2018 16:42:10.568] [main 1] DEBUG org.hibernate.hql.internal.ast.QueryTranslatorImpl showHqlAst - --- HQL AST ---
 \-[QUERY] Node: 'query'
    \-[SELECT_FROM] Node: 'SELECT_FROM'
       \-[FROM] Node: 'from'
          \-[RANGE] Node: 'RANGE'
             \-[DOT] Node: '.'
                +-[DOT] Node: '.'
                |  +-[IDENT] Node: 'luminare'
                |  \-[IDENT] Node: 'model'
                \-[IDENT] Node: 'Fields'

[19-12-2018 16:42:10.589] [main 1] DEBUG org.hibernate.hql.internal.antlr.HqlSqlBaseWalker beforeStatement - select << begin [level=1, statement=select]
[19-12-2018 16:42:10.599] [main 1] DEBUG org.hibernate.hql.internal.ast.tree.FromElement doInitialize - FromClause{level=1} : luminare.model.Fields (<no alias>) -> fields0_
[19-12-2018 16:42:10.602] [main 1] DEBUG org.hibernate.hql.internal.antlr.HqlSqlBaseWalker beforeStatementCompletion - select : finishing up [level=1, statement=select]
[19-12-2018 16:42:10.602] [main 1] DEBUG org.hibernate.hql.internal.ast.HqlSqlWalker processQuery - processQuery() :  ( SELECT ( FromClause{level=1} plugin_luminare3.fields fields0_ ) )
[19-12-2018 16:42:10.608] [main 1] DEBUG org.hibernate.hql.internal.ast.HqlSqlWalker createSelectClauseFromFromClause - Derived SELECT clause created.
[19-12-2018 16:42:10.622] [main 1] DEBUG org.hibernate.hql.internal.ast.util.JoinProcessor addJoinNodes - Using FROM fragment [plugin_luminare3.fields fields0_]
[19-12-2018 16:42:10.622] [main 1] DEBUG org.hibernate.hql.internal.antlr.HqlSqlBaseWalker afterStatementCompletion - select >> end [level=1, statement=select]
[19-12-2018 16:42:10.623] [main 1] DEBUG org.hibernate.hql.internal.ast.QueryTranslatorImpl analyze - --- SQL AST ---
 \-[SELECT] QueryNode: 'SELECT'  querySpaces (plugin_luminare3.fields)
    +-[SELECT_CLAUSE] SelectClause: '{derived select clause}'
    |  +-[SELECT_EXPR] SelectExpressionImpl: 'fields0_.ID as ID1_0_' {FromElement{explicit,not a collection join,not a fetch join,fetch non-lazy properties,classAlias=null,role=null,tableName=plugin_luminare3.fields,tableAlias=fields0_,origin=null,columns={,className=luminare.model.Fields}}}
    |  \-[SQL_TOKEN] SqlFragment: 'fields0_.insert_date as insert_d2_0_, fields0_.insert_user as insert_u3_0_, fields0_.update_date as update_d4_0_, fields0_.update_user as update_u5_0_, fields0_.f_boolean as f_boolea6_0_, fields0_.f_date as f_date7_0_, fields0_.f_float as f_float8_0_, fields0_.f_int as f_int9_0_, fields0_.f_long as f_long10_0_, fields0_.f_text as f_text11_0_, fields0_.fieldtypeID as fieldty12_0_'
    \-[FROM] FromClause: 'from' FromClause{level=1, fromElementCounter=1, fromElements=1, fromElementByClassAlias=[], fromElementByTableAlias=[fields0_], fromElementsByPath=[], collectionJoinFromElementsByPath=[], impliedElements=[]}
       \-[FROM_FRAGMENT] FromElement: 'plugin_luminare3.fields fields0_' FromElement{explicit,not a collection join,not a fetch join,fetch non-lazy properties,classAlias=null,role=null,tableName=plugin_luminare3.fields,tableAlias=fields0_,origin=null,columns={,className=luminare.model.Fields}}

[19-12-2018 16:42:10.624] [main 1] DEBUG org.hibernate.hql.internal.ast.ErrorTracker throwQueryException - throwQueryException() : no errors
[19-12-2018 16:42:10.632] [main 1] DEBUG org.hibernate.hql.internal.ast.QueryTranslatorImpl generate - HQL: from luminare.model.Fields
[19-12-2018 16:42:10.632] [main 1] DEBUG org.hibernate.hql.internal.ast.QueryTranslatorImpl generate - SQL: select fields0_.ID as ID1_0_, fields0_.insert_date as insert_d2_0_, fields0_.insert_user as insert_u3_0_, fields0_.update_date as update_d4_0_, fields0_.update_user as update_u5_0_, fields0_.f_boolean as f_boolea6_0_, fields0_.f_date as f_date7_0_, fields0_.f_float as f_float8_0_, fields0_.f_int as f_int9_0_, fields0_.f_long as f_long10_0_, fields0_.f_text as f_text11_0_, fields0_.fieldtypeID as fieldty12_0_ from plugin_luminare3.fields fields0_
[19-12-2018 16:42:10.632] [main 1] DEBUG org.hibernate.hql.internal.ast.ErrorTracker throwQueryException - throwQueryException() : no errors
[19-12-2018 16:42:10.649] [main 1] DEBUG org.hibernate.SQL logStatement - 
    /* 
from
    Fields */ select
        fields0_.ID as ID1_0_,
        fields0_.insert_date as insert_d2_0_,
        fields0_.insert_user as insert_u3_0_,
        fields0_.update_date as update_d4_0_,
        fields0_.update_user as update_u5_0_,
        fields0_.f_boolean as f_boolea6_0_,
        fields0_.f_date as f_date7_0_,
        fields0_.f_float as f_float8_0_,
        fields0_.f_int as f_int9_0_,
        fields0_.f_long as f_long10_0_,
        fields0_.f_text as f_text11_0_,
        fields0_.fieldtypeID as fieldty12_0_ 
    from
        plugin_luminare3.fields fields0_
Hibernate: 
    /* 
from
    Fields */ select
        fields0_.ID as ID1_0_,
        fields0_.insert_date as insert_d2_0_,
        fields0_.insert_user as insert_u3_0_,
        fields0_.update_date as update_d4_0_,
        fields0_.update_user as update_u5_0_,
        fields0_.f_boolean as f_boolea6_0_,
        fields0_.f_date as f_date7_0_,
        fields0_.f_float as f_float8_0_,
        fields0_.f_int as f_int9_0_,
        fields0_.f_long as f_long10_0_,
        fields0_.f_text as f_text11_0_,
        fields0_.fieldtypeID as fieldty12_0_ 
    from
        plugin_luminare3.fields fields0_
[19-12-2018 16:42:10.673] [main 1] DEBUG org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions - could not extract ResultSet [n/a]
org.postgresql.util.PSQLException: ERROR: column fields0_.f_boolean does not exist
  Position: 201
	at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2440) ~[postgresql-42.2.5.jar:42.2.5]
	at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2183) ~[postgresql-42.2.5.jar:42.2.5]
	at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:308) ~[postgresql-42.2.5.jar:42.2.5]
	at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:441) ~[postgresql-42.2.5.jar:42.2.5]
	at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:365) ~[postgresql-42.2.5.jar:42.2.5]
	at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:143) ~[postgresql-42.2.5.jar:42.2.5]
	at org.postgresql.jdbc.PgPreparedStatement.executeQuery(PgPreparedStatement.java:106) ~[postgresql-42.2.5.jar:42.2.5]
	at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:60) [hibernate-core-5.3.7.Final.jar:5.3.7.Final]
	at org.hibernate.loader.Loader.getResultSet(Loader.java:2167) [hibernate-core-5.3.7.Final.jar:5.3.7.Final]
	at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1930) [hibernate-core-5.3.7.Final.jar:5.3.7.Final]
	at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1892) [hibernate-core-5.3.7.Final.jar:5.3.7.Final]
	at org.hibernate.loader.Loader.doQuery(Loader.java:937) [hibernate-core-5.3.7.Final.jar:5.3.7.Final]
	at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:340) [hibernate-core-5.3.7.Final.jar:5.3.7.Final]
	at org.hibernate.loader.Loader.doList(Loader.java:2689) [hibernate-core-5.3.7.Final.jar:5.3.7.Final]
	at org.hibernate.loader.Loader.doList(Loader.java:2672) [hibernate-core-5.3.7.Final.jar:5.3.7.Final]
	at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2506) [hibernate-core-5.3.7.Final.jar:5.3.7.Final]
	at org.hibernate.loader.Loader.list(Loader.java:2501) [hibernate-core-5.3.7.Final.jar:5.3.7.Final]
	at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:504) [hibernate-core-5.3.7.Final.jar:5.3.7.Final]
	at org.hibernate.hql.internal.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:395) [hibernate-core-5.3.7.Final.jar:5.3.7.Final]
	at org.hibernate.engine.query.spi.HQLQueryPlan.performList(HQLQueryPlan.java:220) [hibernate-core-5.3.7.Final.jar:5.3.7.Final]
	at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1508) [hibernate-core-5.3.7.Final.jar:5.3.7.Final]
	at org.hibernate.query.internal.AbstractProducedQuery.doList(AbstractProducedQuery.java:1537) [hibernate-core-5.3.7.Final.jar:5.3.7.Final]
	at org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:1505) [hibernate-core-5.3.7.Final.jar:5.3.7.Final]
	at org.hibernate.query.Query.getResultList(Query.java:135) [hibernate-core-5.3.7.Final.jar:5.3.7.Final]
	at luminare.MainTest.test(MainTest.java:79) [bin/:?]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_191]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:1.8.0_191]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:1.8.0_191]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[?:1.8.0_191]
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) [junit-4.12.jar:4.12]
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) [junit-4.12.jar:4.12]
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) [junit-4.12.jar:4.12]
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) [junit-4.12.jar:4.12]
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) [junit-4.12.jar:4.12]
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) [junit-4.12.jar:4.12]
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) [junit-4.12.jar:4.12]
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) [junit-4.12.jar:4.12]
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) [junit-4.12.jar:4.12]
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) [junit-4.12.jar:4.12]
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) [junit-4.12.jar:4.12]
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) [junit-4.12.jar:4.12]
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363) [junit-4.12.jar:4.12]
	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86) [.cp/:?]
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) [.cp/:?]
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:538) [.cp/:?]
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:760) [.cp/:?]
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:460) [.cp/:?]
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:206) [.cp/:?]
[19-12-2018 16:42:10.682] [main 1] WARN  org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions - SQL Error: 0, SQLState: 42703
[19-12-2018 16:42:10.683] [main 1] ERROR org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions - ERROR: column fields0_.f_boolean does not exist
  Position: 201
[19-12-2018 16:42:10.689] [main 1] DEBUG org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl afterTransaction - Initiating JDBC connection release from afterTransaction
[19-12-2018 16:42:10.706] [main 1] DEBUG org.hibernate.engine.transaction.internal.TransactionImpl <init> - On TransactionImpl creation, JpaCompliance#isJpaTransactionComplianceEnabled == false
0 Likes

#7

The composite type was designed for setting multiple columns, but this custom PostgreSQL type hides those columns behind a single virtual column.

Hibernate does not support custom PostgreSQL types natively, so you need to go to the PostgreSQL forum and ask them how to persist custom PostgreSQL types using JDBC. Once you know how to do it in JDBC, you could try to see what Hibernate type is more suitable.

0 Likes

#8

Hello Vlad and Happy New Year.

I found the solution, it is Postgres specific but I think it can be adapted to other DB engines like Oracle/MySQL and so on.

Hope this can help other developers!

CREATE TYPE ${dbSchema}.customfield AS
(
       f_int                      INTEGER,
       f_long                     BIGINT,
       f_float                    DOUBLE PRECISION,
       f_date                     DATE,
       f_text                     TEXT,
       f_boolean                  BOOLEAN);

Mapping the custom type to Hibernate

package luminare.database.mapping.type;

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

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.usertype.UserType;

public class FieldValueType implements UserType, Serializable {
	private static final long serialVersionUID = -4363175251635937661L;
	private final static Logger logger = LogManager.getLogger(FieldValueType.class);
	private final int[] sqlTypesSupported = new int[] { Types.OTHER };
	
	private Integer f_int;
	private Long f_long;
	private Double f_float;
	private Date f_date;
	private String f_text;
	private Boolean f_boolean;
	
	public FieldValueType() {
		this.f_int = null;
		this.f_long = null;
		this.f_float = null;
		this.f_text = null;
		this.f_boolean = null;
	}
	
	public FieldValueType(Integer f_int, Long f_long,
			Double f_float, Date f_date,
			String f_text, Boolean f_boolean) {
		this.f_int = f_int;
		this.f_long = f_long;
		this.f_float = f_float;
		this.f_text = f_text;
		this.f_boolean = f_boolean;
	}

	public Integer getF_int() {
		return f_int;
	}

	public void setF_int(Integer f_int) {
		this.f_int = f_int;
	}

	public Long getF_long() {
		return f_long;
	}

	public void setF_long(Long f_long) {
		this.f_long = f_long;
	}

	public Double getF_float() {
		return f_float;
	}

	public void setF_float(Double f_float) {
		this.f_float = f_float;
	}

	public Date getF_date() {
		return f_date;
	}

	public void setF_date(Date f_date) {
		this.f_date = f_date;
	}

	public String getF_text() {
		return f_text;
	}

	public void setF_text(String f_text) {
		this.f_text = f_text;
	}

	public Boolean getF_boolean() {
		return f_boolean;
	}

	public void setF_boolean(Boolean f_boolean) {
		this.f_boolean = f_boolean;
	}
	
    @Override
    public Object assemble(Serializable cached, Object owner)
            throws HibernateException {
        if (!(cached instanceof FieldValueType)) {
			logger.error("invalid argument");
            throw new HibernateException("invalid argument");
        }
        FieldValueType f = (FieldValueType) cached;
        return new FieldValueType(f.getF_int(), f.getF_long(),
        		f.getF_float(), f.getF_date(),
        		f.getF_text(), f.getF_boolean());
    }

    @Override
    public Serializable disassemble(Object value) throws HibernateException {
        if (!(value instanceof FieldValueType)) {
			logger.error("invalid argument");
            throw new HibernateException("invalid argument");
        }
        return (FieldValueType) value;
    }

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

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

	@Override
	public int hashCode(Object x) throws HibernateException {
		return 0;
	}

	@Override
	public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner)
			throws HibernateException, SQLException {		
		logger.debug("names[0] = " + names[0] + " value = " + rs.getString(names[0]) + " length = " + names.length);
		
		if(rs.wasNull()) {
			return null;
		}
		
		String[] values = rs.getString(names[0]).replace("(", "").replace(")", "").split(",");
		logger.debug("values length = " + values.length);
		
		for(int i = 0; i < values.length; i++) {
			switch(i) {
			case 0:
				logger.debug("values[" + i + "] = " + values[i]);
				if(values[i].length() > 0)
					f_int = Integer.parseInt(values[i]);
				break;

			case 1:
				logger.debug("values[" + i + "] = " + values[i]);
				if(values[i].length() > 0)
					f_long = Long.valueOf(values[i]);
				break;

			case 2:
				logger.debug("values[" + i + "] = " + values[i]);
				if(values[i].length() > 0)
					f_float = Double.valueOf(values[i]);
				break;

			case 3:
				logger.debug("values[" + i + "] = " + values[i]);
				if(values[i].length() > 0)
					f_date = Date.valueOf(values[i]);
				break;

			case 4:
				logger.debug("values[" + i + "] = " + values[i]);
				if(values[i].length() > 0)
					f_text = values[i];
				break;

			case 5:
				logger.debug("values[" + i + "] = " + values[i]);
				if(values[i].length() > 0)
					f_boolean = Boolean.valueOf(values[i]);
				break;
			default:
				logger.error("Index " + i + " unknown!");
				return null;
					
			}
		}
		
		FieldValueType customField = new FieldValueType(f_int, f_long, f_float, f_date, f_text, f_boolean);
		return customField;
	}

	@Override
	public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session)
			throws HibernateException, SQLException {
		String postgresSpecific;
		
		if(value == null) {
			logger.info("Custom field is NULL");
			st.setNull(index, Types.OTHER);
		}
		else if (!(value instanceof FieldValueType)) {
			logger.error("invalid argument");
            throw new HibernateException("invalid argument");
		}
		else {
			
			final FieldValueType f = (FieldValueType) value;
			postgresSpecific = "(" + (f.getF_int() == null? "" : f.getF_int()) + "," + (f.getF_long() == null? "" : f.getF_long()) + "," +
					(f.getF_float() == null? "" : f.getF_float()) + "," + (f.getF_date()== null? "" : f.getF_date()) + "," +
					(f.getF_text() == null? "" : f.getF_text()) + "," + (f.getF_boolean()== null? "" : f.getF_boolean()) + ")";
			logger.debug("Setting custom field (PostgreSQL specific) to: " + postgresSpecific);
			st.setObject(index, postgresSpecific, Types.OTHER);
		}
	}

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

		if (!(value instanceof FieldValueType)) {
			logger.error("invalid argument");
            throw new HibernateException("invalid argument");
        }
		
		FieldValueType f = (FieldValueType) value;
        return new FieldValueType(f.getF_int(), f.getF_long(),
        		f.getF_float(), f.getF_date(),
        		f.getF_text(), f.getF_boolean());
	}

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

	@Override
	public Object replace(Object original, Object target, Object owner) throws HibernateException {
		return original;
	}
	
	@Override
	public boolean equals(Object x, Object y)  throws HibernateException {
		return x.equals(y);
	}
}
0 Likes

#9

Thanks for providing a solution. I’m glad it works now.

0 Likes