Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hibernate, Postgres & Array Type

I'm stuck on a specific issue using array type in postgresql 9.3 mapped with hibernate 4.1.0. This type allows me to have really strong data model, without building lots of tables and joins.

In order to map a field stored with this particular type, I have used a UserType

Anyway, it works well with pure hibernate (hql) but I need also to send sql native query to my database. When I do it, in spite of many tries, I have not found any way to do that.

I try many syntaxes based on this

String[] values = {"value1", "value2"};
String queryString = "SELECT * FROM instances WHERE values && :values";
Query query = this.getSession().createSQLQuery(queryString).addEntity(Instance.class);
query.setParameterList("values", values);
query.list();

I got Operator does not exists : text[] && character varying

It should give following syntax in jdbc : ['value1', 'value2'] and it seems to give 'value1'...

I tried many syntaxes with

  • Collection
  • Pure Arrays
  • [ :values ] syntax : I got Syntax error near "["

I need to send native query because I use Materialized View for performance gains.

My SQL Query works in postgresql console. So it is an hibernate specific issue.

like image 339
Damien C Avatar asked Feb 21 '14 17:02

Damien C


People also ask

Can I use hibernate with PostgreSQL?

Out of the box, Hibernate works pretty well with PostgreSQL databases.

How configure hibernate PostgreSQL?

Connect Hibernate to PostgreSQL Data Switch to the Hibernate Configurations perspective: Window -> Open Perspective -> Hibernate. Right-click on the Hibernate Configurations panel and click Add Configuration. Set the Hibernate version to 5.2. Click the Browse button and select the project.

What is hibernate dialect for PostgreSQL?

Dialect allows Hibernate to generate SQL optimized for a particular relational database. Hibernate generates queries for the specific database based on the Dialect class. A hibernate dialect gives information to the framework of how to convert hibernate queries(HQL) into native SQL queries.

Can we use JPA with PostgreSQL?

Install PostgreSQL for JPA PersistenceYou'll need to have PostgreSQL installed for this tutorial. If you don't already have it installed, go to their downloads page and install it. The next thing you'll need to do is create a Postgres user and database for the project. For this, you can use the Postgres CLI, psql .


1 Answers

I tried few versions based on Array Type introduced by JDBC4 : How can I set a String[] parameter to a native query?. Problem is also Hibernate (even in last version 4.3.1.final) does not work with this new features and gave me following error message

Could not determine a type for class: org.postgresql.jdbc4.Jdbc4Array

So I had to make a Specific UserType (based on several articles in stackoverflow, and others sources)

My Model

@Type(type = "fr.mycompany.dao.hibernate.types.ArrayUserType")
private String[] values;

My ArrayUserType

public class ArrayUserType implements UserType {

/** Constante contenant le type SQL "Array".
 */
protected static final int[] SQL_TYPES = { Types.ARRAY };

/**
 * 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
 */
public final int[] sqlTypes() {
    return SQL_TYPES;
}

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

/**
 * Retrieve an instance of the mapped class from a JDBC resultset. Implementors
 * should handle possibility of null values.
 * 
 * @param resultSet a JDBC result set.
 * @param names the column names.
 * @param session SQL en cours.
 * @param owner the containing entity 
 * @return Object
 * @throws org.hibernate.HibernateException exception levée par Hibernate
 * lors de la récupération des données.
 * @throws java.sql.SQLException exception SQL 
 * levées lors de la récupération des données.
 */
@Override
public final Object nullSafeGet(
        final ResultSet resultSet, 
        final String[] names, 
        final SessionImplementor session, 
        final Object owner) throws HibernateException, SQLException {
    if (resultSet.wasNull()) {
        return null;
    }

    String[] array = (String[]) resultSet.getArray(names[0]).getArray();
    return array;
}

/**
 * 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 statement a JDBC prepared statement.
 * @param value the object to write
 * @param index statement parameter index
 * @param session sql en cours
 * @throws org.hibernate.HibernateException exception levée par Hibernate
 * lors de la récupération des données.
 * @throws java.sql.SQLException exception SQL 
 * levées lors de la récupération des données.
 */
@Override
public final void nullSafeSet(final PreparedStatement statement, final Object value, 
        final int index, final SessionImplementor session) throws HibernateException, SQLException {

    if (value == null) {
        statement.setNull(index, SQL_TYPES[0]);
    } else {
        String[] castObject = (String[]) value;
        Array array = session.connection().createArrayOf("text", castObject);
        statement.setArray(index, array);
    }
}

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

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

@Override
public final Object assemble(final Serializable arg0, final Object arg1)
        throws HibernateException {
    // TODO Auto-generated method stub
    return null;
}

@Override
public final Serializable disassemble(final Object arg0) throws HibernateException {
    // TODO Auto-generated method stub
    return null;
}

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

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

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

}

And the last, but least (that's what I missed) : when I need to run SQL Native Query, I have to force the parameter type with the following syntax

String[] values = ...
Type arrayType = new CustomType(new ArrayUserType());
query.setParameter("value", values, arrayType);
like image 154
Damien C Avatar answered Sep 20 '22 16:09

Damien C