When a *_Custom type_* (extended from _org.hibernate.usertype.UserType_) is registered as an expected parameter for entityManager.createStoredProcedureQuery the validateBinding method expects an instance object of the same type, when probably the expected one should be *_UserType.returnedClass_*:
BaseQueryImpl:957 {code:java} private static boolean isValidBindValue(Class expectedType, Object value, TemporalType temporalType) { if ( expectedType.isInstance( value ) ) { return true; }
if ( temporalType != null ) { final boolean parameterDeclarationIsTemporal = Date.class.isAssignableFrom( expectedType ) || Calendar.class.isAssignableFrom( expectedType ); final boolean bindIsTemporal = Date.class.isInstance( value ) || Calendar.class.isInstance( value );
if ( parameterDeclarationIsTemporal && bindIsTemporal ) { return true; } }
return false; } {code}
It implies that the procedure call should be made with an instance of the UserTypeDefinition and hold the real parameter inside:
{code:java} StoredProcedureQuery q = entityManager.createStoredProcedureQuery(GETXML_PROCEDURE) .registerStoredProcedureParameter("QUERY_STATEMENT", String.class, ParameterMode.IN) .registerStoredProcedureParameter("QUERY_PARAMS", BindParametersListUserType.class, ParameterMode.IN) .registerStoredProcedureParameter("RESULT_CLOB", Class.class, ParameterMode.OUT) .setParameter("QUERY_STATEMENT", queryString)
// CustomType holding the desired request parameter .setParameter("QUERY_PARAMS", new BindParametersListUserType(bindParameters)); q.execute(); {code}
and provide a workaround on implemented UserType methods to receive the UserType Object instead of the expected declared returnedClass:
{code:java} @Override public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) throws HibernateException, SQLException { if (value == null) { st.setNull(index, DB_OBJECT_LIST_TYPE, DB_OBJECT_TYPE_NAME); } else { final Connection connection = st.getConnection(); final OracleConnection oraConnection = connection.unwrap(OracleConnection.class); // Workaround final BindParametersList bindParametersList = (BindParametersList) ((BindParametersListUserType) value).getExpectedParamObject();
List<Object> elements = new ArrayList<Object>(); for(BindParameter bindParameter : bindParametersList.getBindParameters()){ Object[] attributes = new Object[] { bindParameter.getName(), bindParameter.getType(), bindParameter.getNumber(), bindParameter.getString(), convertDate(bindParameter.getDate()) }; Struct struct = oraConnection.createStruct(DB_OBJECT_TYPE_NAME, attributes); elements.add(struct); } Array array = oraConnection.createARRAY(DB_OBJECT_LIST_TYPE_NAME, elements.toArray()); st.setObject(index, array, DB_OBJECT_LIST_TYPE); } } {code}
As UserType documentation says :
{code:java} /** * This interface should be implemented by user-defined "types". * A "type" class is <em>not</em> the actual property type - it * is a class that knows how to serialize instances of another * class to and from JDBC.<br> * <br> {code}
|
|