Author: steve.ebersole(a)jboss.com
Date: 2006-11-14 18:18:34 -0500 (Tue, 14 Nov 2006)
New Revision: 10807
Added:
branches/Branch_3_2/Hibernate3/test/org/hibernate/test/lob/ClobHoldingEntity.hbm.xml
branches/Branch_3_2/Hibernate3/test/org/hibernate/test/lob/ClobHoldingEntity.java
branches/Branch_3_2/Hibernate3/test/org/hibernate/test/lob/ClobTest.java
Modified:
branches/Branch_3_2/Hibernate3/test/org/hibernate/test/AllTests.java
branches/Branch_3_2/Hibernate3/test/org/hibernate/test/TestCase.java
Log:
lob tests
Modified: branches/Branch_3_2/Hibernate3/test/org/hibernate/test/AllTests.java
===================================================================
--- branches/Branch_3_2/Hibernate3/test/org/hibernate/test/AllTests.java 2006-11-14
13:09:14 UTC (rev 10806)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/AllTests.java 2006-11-14
23:18:34 UTC (rev 10807)
@@ -131,6 +131,8 @@
import org.hibernate.test.version.sybase.SybaseTimestampVersioningTest;
import org.hibernate.test.where.WhereTest;
import org.hibernate.test.dialect.cache.SQLFunctionsInterSystemsTest;
+import org.hibernate.test.lob.LobTest;
+import org.hibernate.test.lob.ClobTest;
/**
* @author Gavin King
@@ -290,6 +292,8 @@
suite.addTest( UtilSuite.suite() );
suite.addTest( AnyTypeTest.suite() );
suite.addTest( SQLFunctionsInterSystemsTest.suite() );
+ suite.addTest( LobTest.suite() );
+ suite.addTest( ClobTest.suite() );
return filter( suite );
//return suite;
Modified: branches/Branch_3_2/Hibernate3/test/org/hibernate/test/TestCase.java
===================================================================
--- branches/Branch_3_2/Hibernate3/test/org/hibernate/test/TestCase.java 2006-11-14
13:09:14 UTC (rev 10806)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/TestCase.java 2006-11-14
23:18:34 UTC (rev 10807)
@@ -30,6 +30,7 @@
import org.hibernate.dialect.TimesTenDialect;
import org.hibernate.dialect.DerbyDialect;
import org.hibernate.dialect.Cache71Dialect;
+import org.hibernate.dialect.H2Dialect;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.PersistentClass;
@@ -563,18 +564,91 @@
}
}
+ /**
+ * Expected LOB usage pattern is such that I can perform an insert
+ * via prepared statement with a parameter binding for a LOB value
+ * without crazy casting to JDBC driver implementation-specific classes...
+ * <p/>
+ * Part of the trickiness here is the fact that this is largely
+ * driver dependent. For Oracle, which is notoriously bad with
+ * LOB support in their drivers actually does a pretty good job with
+ * LOB support as of the 10.2.x versions of their drivers...
+ *
+ * @return True if expected usage pattern is support; false otherwise.
+ */
+ protected boolean supportsExpectedLobUsagePattern() {
+ // note : For H2, the insertions get truncated...
+ // note : For Derby, the insertions get truncated...
+ Class[] exceptions = new Class[] { H2Dialect.class, DerbyDialect.class };
+ if ( dialectIsOneOf( exceptions ) ) {
+ reportSkip( "database/driver does not support expected LOB usage pattern",
"LOB support" );
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Does the current dialect support propogating changes to LOB
+ * values back to the database? Talking about mutating the
+ * underlying value as opposed to supplying a new
+ * LOB instance...
+ *
+ * @return True if the changes are propogated back to the
+ * database; false otherwise.
+ */
+ protected boolean supportsLobValueChangePropogation() {
+ // note: at least my local MySQL 5.1 install shows this not working...
+ // note: at least my local SQL Server 2005 Express shows this not working...
+ Class[] exceptions = new Class[] {
+ HSQLDialect.class, MySQLDialect.class, SQLServerDialect.class
+ };
+ if ( dialectIsOneOf( exceptions ) ) {
+ reportSkip( "database/driver does not support propogating LOB value change back
to database", "LOB support" );
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Is it supported to materialize a LOB locator outside the transaction in
+ * which it was created?
+ * <p/>
+ * Again, part of the trickiness here is the fact that this is largely
+ * driver dependent.
+ * <p/>
+ * NOTE: all database I have tested which {@link #supportsExpectedLobUsagePattern()}
+ * also support the ability to materialize a LOB outside the owning transaction...
+ *
+ * @return True if unbounded materialization is supported; false otherwise.
+ */
+ protected boolean supportsUnboundedLobLocatorMaterialization() {
+ Class[] exceptions = new Class[] { }; // none known of...
+ if ( dialectIsOneOf( exceptions ) ) {
+ reportSkip( "database/driver does not support materializing a LOB locator outside
the 'owning' transaction", "LOB support" );
+ return false;
+ }
+ return true;
+ }
+
+ private boolean dialectIs(Class dialectClass) {
+ return dialectClass.isInstance( getDialect() );
+ }
+
private boolean dialectIsNot(Class dialectClass) {
- return dialectIsNot( new Class[] { dialectClass } );
+ return ! dialectIs( dialectClass );
}
-
- private boolean dialectIsNot(Class[] dialectClasses) {
- for (int i = 0; i < dialectClasses.length; i++) {
- Class dialectClass = dialectClasses[i];
- if(dialectClass.isInstance(getDialect())) {
- return false;
+
+ private boolean dialectIsOneOf(Class[] dialectClasses) {
+ for ( int i = 0; i < dialectClasses.length; i++ ) {
+ if ( dialectClasses[i].isInstance( getDialect() ) ) {
+ return true;
}
}
- return true;
+ return false;
}
+ private boolean dialectIsNot(Class[] dialectClasses) {
+ return ! dialectIsOneOf( dialectClasses );
+ }
+
}
\ No newline at end of file
Added:
branches/Branch_3_2/Hibernate3/test/org/hibernate/test/lob/ClobHoldingEntity.hbm.xml
===================================================================
---
branches/Branch_3_2/Hibernate3/test/org/hibernate/test/lob/ClobHoldingEntity.hbm.xml 2006-11-14
13:09:14 UTC (rev 10806)
+++
branches/Branch_3_2/Hibernate3/test/org/hibernate/test/lob/ClobHoldingEntity.hbm.xml 2006-11-14
23:18:34 UTC (rev 10807)
@@ -0,0 +1,16 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 2.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.lob">
+
+ <class name="ClobHoldingEntity" table="CLOB_ENTITY">
+ <id name="id" type="long" column="ID">
+ <generator class="increment"/>
+ </id>
+ <property name="serialData" column="SER_DATA"
type="text"/>
+ <property name="clobData" column="CLOB_DATA"
type="clob" />
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: branches/Branch_3_2/Hibernate3/test/org/hibernate/test/lob/ClobHoldingEntity.java
===================================================================
---
branches/Branch_3_2/Hibernate3/test/org/hibernate/test/lob/ClobHoldingEntity.java 2006-11-14
13:09:14 UTC (rev 10806)
+++
branches/Branch_3_2/Hibernate3/test/org/hibernate/test/lob/ClobHoldingEntity.java 2006-11-14
23:18:34 UTC (rev 10807)
@@ -0,0 +1,57 @@
+package org.hibernate.test.lob;
+
+import java.sql.Clob;
+
+/**
+ * Used to test materialized and lazy-materialized CLOB data.
+ * <p/>
+ * The {@link #serialData} field is used to hold CLOB data that is
+ * materialized into a String immediately (mapped via the
+ * Hibernate text type).
+ * <p/>
+ * The {@link #clobData} field is used to hold CLOB data that is
+ * materialized lazily via a JDBC CLOB locator (mapped via
+ * the Hibernate clob type).
+ *
+ * @author Steve Ebersole
+ */
+public class ClobHoldingEntity {
+ private Long id;
+ private String serialData;
+ private Clob clobData;
+
+ public ClobHoldingEntity() {
+ }
+
+ public ClobHoldingEntity(String serialData) {
+ this.serialData = serialData;
+ }
+
+ public ClobHoldingEntity(Clob clobData) {
+ this.clobData = clobData;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getSerialData() {
+ return serialData;
+ }
+
+ public void setSerialData(String serialData) {
+ this.serialData = serialData;
+ }
+
+ public Clob getClobData() {
+ return clobData;
+ }
+
+ public void setClobData(Clob clobData) {
+ this.clobData = clobData;
+ }
+}
Added: branches/Branch_3_2/Hibernate3/test/org/hibernate/test/lob/ClobTest.java
===================================================================
--- branches/Branch_3_2/Hibernate3/test/org/hibernate/test/lob/ClobTest.java 2006-11-14
13:09:14 UTC (rev 10806)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/lob/ClobTest.java 2006-11-14
23:18:34 UTC (rev 10807)
@@ -0,0 +1,191 @@
+package org.hibernate.test.lob;
+
+import java.sql.Clob;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+import org.hibernate.test.TestCase;
+import org.hibernate.Session;
+import org.hibernate.Hibernate;
+import org.hibernate.LockMode;
+
+import org.h2.engine.Constants;
+
+/**
+ * Test various access scenarios for eager and lazy materialization
+ * of CLOB data, as well as bounded and unbounded materialization
+ * and mutation.
+ *
+ * @author Steve Ebersole
+ */
+public class ClobTest extends TestCase {
+ private static final int CLOB_SIZE = 10000;
+
+ public ClobTest(String name) {
+ super( name );
+ }
+
+ protected String[] getMappings() {
+ return new String[] { "lob/ClobHoldingEntity.hbm.xml" };
+ }
+
+ public static Test suite() {
+ return new TestSuite( ClobTest.class );
+ }
+
+ public void testBoundedMaterializedClobAccess() {
+ if ( !supportsExpectedLobUsagePattern() ) {
+ return;
+ }
+
+ String original = buildRecursively( CLOB_SIZE, 'x' );
+ String changed = buildRecursively( CLOB_SIZE, 'y' );
+
+ Session s = openSession();
+ s.beginTransaction();
+ ClobHoldingEntity entity = new ClobHoldingEntity();
+ entity.setSerialData( original );
+ s.save( entity );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ entity = ( ClobHoldingEntity ) s.get( ClobHoldingEntity.class, entity.getId() );
+ assertEquals( CLOB_SIZE, entity.getSerialData().length() );
+ assertEquals( original, entity.getSerialData() );
+ entity.setSerialData( changed );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ entity = ( ClobHoldingEntity ) s.get( ClobHoldingEntity.class, entity.getId() );
+ assertEquals( CLOB_SIZE, entity.getSerialData().length() );
+ assertEquals( changed, entity.getSerialData() );
+ s.delete( entity );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testBoundedClobLocatorAccess() throws Throwable {
+ if ( !supportsExpectedLobUsagePattern() ) {
+ return;
+ }
+
+ String original = buildRecursively( CLOB_SIZE, 'x' );
+ String changed = buildRecursively( CLOB_SIZE, 'y' );
+
+ Session s = openSession();
+ s.beginTransaction();
+ ClobHoldingEntity entity = new ClobHoldingEntity();
+ entity.setClobData( Hibernate.createClob( original ) );
+ s.save( entity );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ entity = ( ClobHoldingEntity ) s.get( ClobHoldingEntity.class, entity.getId() );
+ assertEquals( CLOB_SIZE, entity.getClobData().length() );
+ assertEquals( original, extractData( entity.getClobData() ) );
+ s.getTransaction().commit();
+ s.close();
+
+ // test mutation via setting the new clob data...
+ if ( supportsLobValueChangePropogation() ) {
+ s = openSession();
+ s.beginTransaction();
+ entity = ( ClobHoldingEntity ) s.get( ClobHoldingEntity.class, entity.getId(),
LockMode.UPGRADE );
+ entity.getClobData().truncate( 1 );
+ entity.getClobData().setString( 1, changed );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ entity = ( ClobHoldingEntity ) s.get( ClobHoldingEntity.class, entity.getId(),
LockMode.UPGRADE );
+ assertNotNull( entity.getClobData() );
+ assertEquals( CLOB_SIZE, entity.getClobData().length() );
+ assertEquals( changed, extractData( entity.getClobData() ) );
+ entity.getClobData().truncate( 1 );
+ entity.getClobData().setString( 1, original );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ // test mutation via supplying a new clob locator instance...
+ s = openSession();
+ s.beginTransaction();
+ entity = ( ClobHoldingEntity ) s.get( ClobHoldingEntity.class, entity.getId(),
LockMode.UPGRADE );
+ assertNotNull( entity.getClobData() );
+ assertEquals( CLOB_SIZE, entity.getClobData().length() );
+ assertEquals( original, extractData( entity.getClobData() ) );
+ entity.setClobData( Hibernate.createClob( changed ) );
+ s.getTransaction().commit();
+ s.close();
+
+ s = openSession();
+ s.beginTransaction();
+ entity = ( ClobHoldingEntity ) s.get( ClobHoldingEntity.class, entity.getId() );
+ assertEquals( CLOB_SIZE, entity.getClobData().length() );
+ assertEquals( changed, extractData( entity.getClobData() ) );
+ s.delete( entity );
+ s.getTransaction().commit();
+ s.close();
+
+ }
+
+ public void testUnboundedClobLocatorAccess() throws Throwable {
+ if ( !supportsExpectedLobUsagePattern() || !
supportsUnboundedLobLocatorMaterialization() ) {
+ return;
+ }
+
+ // Note: unbounded mutation of the underlying lob data is completely
+ // unsupported; most databases would not allow such a construct anyway.
+ // Thus here we are only testing materialization...
+
+ String original = buildRecursively( CLOB_SIZE, 'x' );
+
+ Session s = openSession();
+ s.beginTransaction();
+ ClobHoldingEntity entity = new ClobHoldingEntity();
+ entity.setClobData( Hibernate.createClob( original ) );
+ s.save( entity );
+ s.getTransaction().commit();
+ s.close();
+
+ // load the entity with the clob locator, and close the session/transaction;
+ // at that point it is unbounded...
+ s = openSession();
+ s.beginTransaction();
+ entity = ( ClobHoldingEntity ) s.get( ClobHoldingEntity.class, entity.getId() );
+ s.getTransaction().commit();
+ s.close();
+
+ assertEquals( CLOB_SIZE, entity.getClobData().length() );
+ assertEquals( original, extractData( entity.getClobData() ) );
+
+ s = openSession();
+ s.beginTransaction();
+ s.delete( entity );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ private String extractData(Clob clob) throws Throwable {
+ char[] data = new char[ (int) clob.length() ];
+ clob.getCharacterStream().read( data );
+ return new String( data );
+ }
+
+
+ private String buildRecursively(int size, char baseChar) {
+ StringBuffer buff = new StringBuffer();
+ for( int i = 0; i < size; i++ ) {
+ buff.append( baseChar );
+ }
+ return buff.toString();
+ }
+}