Question

I need to fetch a String from a clob Oracle column using Hibernate without lazy loading and without using a Criteria query. It currently returns a proxy class (ex. $Proxy30) but I need a String (or something I can convert to a String). The proxy is for oracle.sql.CLOB according to the debugger.

When I use a Criteria query (with just the regular Column annotation mapping for this field), this works just fine. But we have one area where we "build" a custom SQL query which uses a simple org.hibernate.Query and AliasToEntityMapResultTransformer.INSTANCE, and this is where the proxy problem arises. Since it's just an org.hibernate.Query, I assume it's not referring to my annotation mappings at all, so I don't think messing with annotations will help.

Since using a Criteria query is not an option here (we're building a query string for some advanced search reporting requirements), what should I be researching to find a workaround? Or even better, what's my path of least effort to do this?

Also this is for an Apache Flex application - I need the type to be a String (rather than CLOB) to be used in a data transfer object to the client.. perhaps other Flex devs have solved this problem before?

Query query = getSessionFactory().getCurrentSession().createSqlQuery(sql);
query.setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE);
List... resultlist = ...query.list();
resultlist.iterator();
//iterate results...
Object shouldBeAString = (Object)result.get("CLOB_COLUMN_NAME");
//The object above should be a String but is a $Proxy30
//Debugger shows an "h" property of type SerializableClobProxy

If I need a custom "ResultTransformer" - any links to decent docs for doing that would be appreciated...

Was it helpful?

Solution

I just had the same problem, various links suggested upgrading spring to 4.0.1+ and hibernate to 4.3.x but this didn't make any difference. Then I came across this link which resolved my issue. The author writes a custom ResultTransformer for the Clob and then setting this as the as the transformer for you query instead of AliasToEntityMapResultTransformer.

http://javatechtricks.blogspot.co.uk/2012/12/hibernate-clob-to-string-conversion.html

Code from the article below:

public class MyResultTransformer extends BasicTransformerAdapter {

 public final static MyResultTransformer INSTANCE;
 static {
  INSTANCE = new MyResultTransformer();
 }

 private MyResultTransformer() {

 }
 private static final long serialVersionUID = 1L;

 @Override
 public Object transformTuple(Object[] tuple, String[] aliases) {
  Map<String, Object> map = new HashMap<String, Object>();
  for (int i = 0; i < aliases.length; i++) {
   Object t = tuple[i];
   if (t != null && t instanceof Clob) {
    Clob c = (Clob) tuple[i];
    try {
     ByteArrayOutputStream bos = new ByteArrayOutputStream();
     IOUtils.copy(c.getAsciiStream(), bos);
     t = new String(bos.toByteArray());
    } catch (SQLException e) {
     e.printStackTrace();
    } catch (IOException e) {
     e.printStackTrace();
    }
   }
   map.put(aliases[i], t);
  }
  return map;
 }
}

Then in your code replace

query.setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE);

with

query.setResultTransformer(MyResultTransformer.INSTANCE);

In addition, an alternative solution could be to change the column type, I havn't tried it myself.

JDBC large objects The types blob and clob provide mappings for the Blob and Clob classes in the java.sql package. If you are dealing with truly large values, your best bet is to declare the properties as either Blob or Clob—even though this introduces an explicit JDBC dependency to your data object, it easily allows Hibernate to leverage JDBC features to lazily load the property value only if you need it.

If you are not worried that the data is too huge, you can spare yourself this direct JDBC interface, declare the property type as String or byte[ ], and map it using text or binary. These correspond to SQL column types of CLOB and VARBINARY (RAW in Oracle, BYTEA in PostgreSQL), respectively, and the values are loaded immediately into the properties when the object is loaded.

Source: http://oreilly.com/java/excerpts/harnessing-hibernate/hibernate-types.html

OTHER TIPS

Thank you for this, I modified a little bit the solution to use the clob method getSubString : there is one less exception to catch.

public Object transformTuple(final Object[] tuple, final String[] aliases) {
        final Map<String, Object> result = new LinkedHashMap<>(tuple.length);
        for (int i = 0; i < tuple.length; i++) {
            Object object = tuple[i];
            if (object instanceof Clob) {
                object = clobToString((Clob) object);
            }
            final String alias = aliases[i];
            if (alias != null) {
                result.put(alias, object);
            }
        }
        return result;
    }

    private String clobToString(final Clob clob) {
        try {
            return clob.getSubString(1, (int) clob.length());
        } catch (final SQLException e) {
            throw new RuntimeException("Error while converting clob : " + clob, e);
        }
    }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top