Hibernate SVN: r10782 - trunk/Hibernate3/src/org/hibernate/proxy/pojo/javassist
by hibernate-commits@lists.jboss.org
Author: scottmarlownovell
Date: 2006-11-10 23:27:51 -0500 (Fri, 10 Nov 2006)
New Revision: 10782
Modified:
trunk/Hibernate3/src/org/hibernate/proxy/pojo/javassist/JavassistLazyInitializer.java
Log:
Removed unused imports (goes with previous checkin for HHH-2229).
Modified: trunk/Hibernate3/src/org/hibernate/proxy/pojo/javassist/JavassistLazyInitializer.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/proxy/pojo/javassist/JavassistLazyInitializer.java 2006-11-11 04:07:31 UTC (rev 10781)
+++ trunk/Hibernate3/src/org/hibernate/proxy/pojo/javassist/JavassistLazyInitializer.java 2006-11-11 04:27:51 UTC (rev 10782)
@@ -3,9 +3,6 @@
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
import javassist.util.proxy.MethodFilter;
import javassist.util.proxy.MethodHandler;
18 years, 4 months
Hibernate SVN: r10781 - in trunk/Hibernate3/src/org/hibernate/proxy/pojo: cglib javassist
by hibernate-commits@lists.jboss.org
Author: scottmarlownovell
Date: 2006-11-10 23:07:31 -0500 (Fri, 10 Nov 2006)
New Revision: 10781
Modified:
trunk/Hibernate3/src/org/hibernate/proxy/pojo/cglib/CGLIBLazyInitializer.java
trunk/Hibernate3/src/org/hibernate/proxy/pojo/javassist/JavassistLazyInitializer.java
Log:
Fix for HHH-2229 "Performance issue with fix for HHH-1293, CGLIBLazyInitializer may be slower for certain Java classes"
Also fixed Javaassist which had the same recursive code.
Modified: trunk/Hibernate3/src/org/hibernate/proxy/pojo/cglib/CGLIBLazyInitializer.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/proxy/pojo/cglib/CGLIBLazyInitializer.java 2006-11-10 16:40:49 UTC (rev 10780)
+++ trunk/Hibernate3/src/org/hibernate/proxy/pojo/cglib/CGLIBLazyInitializer.java 2006-11-11 04:07:31 UTC (rev 10781)
@@ -4,11 +4,7 @@
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
-import java.util.List;
-import java.util.ArrayList;
-import java.util.Iterator;
-
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Enhancer;
@@ -137,34 +133,6 @@
this.interfaces = interfaces;
}
- private static boolean isCastable(Class caster, Class castee) {
- if ( castee.equals( caster ) ) {
- return true;
- }
- List list = addCheckingTypes( caster, new ArrayList() );
- for ( Iterator iter = list.iterator(); iter.hasNext(); ) {
- Class cl = ( Class ) iter.next();
- if ( castee.equals( cl ) ) {
- return true;
- }
- }
- return false;
- }
-
- private static List addCheckingTypes(final Class type, final List list) {
- Class superclass = type.getSuperclass();
- if ( superclass != null ) {
- list.add( superclass );
- addCheckingTypes( superclass, list );
- }
- Class[] interfaces = type.getInterfaces();
- for ( int i = 0; i < interfaces.length; ++i ) {
- list.add( interfaces[i] );
- addCheckingTypes( interfaces[i], list );
- }
- return list;
- }
-
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
if ( constructed ) {
Object result = invoke( method, args, proxy );
@@ -173,10 +141,8 @@
final Object returnValue;
try {
if ( ReflectHelper.isPublic( persistentClass, method ) ) {
- if ( !isCastable(
- target.getClass(), method
- .getDeclaringClass()
- ) ) {
+ if(! ( method.getDeclaringClass().isInstance(target) ) )
+ {
throw new ClassCastException(
target.getClass()
.getName()
Modified: trunk/Hibernate3/src/org/hibernate/proxy/pojo/javassist/JavassistLazyInitializer.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/proxy/pojo/javassist/JavassistLazyInitializer.java 2006-11-10 16:40:49 UTC (rev 10780)
+++ trunk/Hibernate3/src/org/hibernate/proxy/pojo/javassist/JavassistLazyInitializer.java 2006-11-11 04:07:31 UTC (rev 10781)
@@ -153,34 +153,6 @@
}
}
- private static boolean isCastable(Class caster, Class castee) {
- if ( castee.equals( caster ) ) {
- return true;
- }
- List list = addCheckingTypes( caster, new ArrayList() );
- for ( Iterator iter = list.iterator(); iter.hasNext(); ) {
- Class cl = ( Class ) iter.next();
- if ( castee.equals( cl ) ) {
- return true;
- }
- }
- return false;
- }
-
- private static List addCheckingTypes(final Class type, final List list) {
- Class superclass = type.getSuperclass();
- if ( superclass != null ) {
- list.add( superclass );
- addCheckingTypes( superclass, list );
- }
- Class[] interfaces = type.getInterfaces();
- for ( int i = 0; i < interfaces.length; ++i ) {
- list.add( interfaces[i] );
- addCheckingTypes( interfaces[i], list );
- }
- return list;
- }
-
public Object invoke(
final Object proxy,
final Method thisMethod,
@@ -199,7 +171,7 @@
final Object returnValue;
try {
if ( ReflectHelper.isPublic( persistentClass, thisMethod ) ) {
- if ( !isCastable( target.getClass(), thisMethod.getDeclaringClass()) ) {
+ if(! ( thisMethod.getDeclaringClass().isInstance(target) ) ) {
throw new ClassCastException( target.getClass().getName() );
}
returnValue = thisMethod.invoke( target, args );
18 years, 4 months
Hibernate SVN: r10779 - in trunk/Hibernate3/test/org/hibernate/test: . dialect dialect/cache
by hibernate-commits@lists.jboss.org
Author: steve.ebersole(a)jboss.com
Date: 2006-11-08 18:35:40 -0500 (Wed, 08 Nov 2006)
New Revision: 10779
Added:
trunk/Hibernate3/test/org/hibernate/test/dialect/
trunk/Hibernate3/test/org/hibernate/test/dialect/cache/
trunk/Hibernate3/test/org/hibernate/test/dialect/cache/SQLFunctionsInterSystemsTest.java
trunk/Hibernate3/test/org/hibernate/test/dialect/cache/TestInterSystemsFunctionsClass.hbm.xml
trunk/Hibernate3/test/org/hibernate/test/dialect/cache/TestInterSystemsFunctionsClass.java
Log:
regression tests specific for InterSystems' CacheSQL dialect
Added: trunk/Hibernate3/test/org/hibernate/test/dialect/cache/SQLFunctionsInterSystemsTest.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/dialect/cache/SQLFunctionsInterSystemsTest.java 2006-11-08 23:35:17 UTC (rev 10778)
+++ trunk/Hibernate3/test/org/hibernate/test/dialect/cache/SQLFunctionsInterSystemsTest.java 2006-11-08 23:35:40 UTC (rev 10779)
@@ -0,0 +1,741 @@
+package org.hibernate.test.dialect.cache;
+
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.hibernate.Hibernate;
+import org.hibernate.LockMode;
+import org.hibernate.Query;
+import org.hibernate.ScrollableResults;
+import org.hibernate.Transaction;
+import org.hibernate.classic.Session;
+import org.hibernate.dialect.Cache71Dialect;
+import org.hibernate.dialect.HSQLDialect;
+import org.hibernate.dialect.InterbaseDialect;
+import org.hibernate.dialect.MckoiDialect;
+import org.hibernate.dialect.MySQLDialect;
+import org.hibernate.dialect.Oracle9Dialect;
+import org.hibernate.dialect.SybaseDialect;
+import org.hibernate.dialect.TimesTenDialect;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.function.SQLFunction;
+import org.hibernate.test.TestCase;
+import org.hibernate.test.legacy.Simple;
+import org.hibernate.test.legacy.Single;
+import org.hibernate.test.legacy.Broken;
+import org.hibernate.test.legacy.Fixed;
+import org.hibernate.test.legacy.Blobber;
+
+/**
+ * Tests for function support on CacheSQL...
+ *
+ * @author Jonathan Levinson
+ */
+public class SQLFunctionsInterSystemsTest extends TestCase {
+
+ private static final Log log = LogFactory.getLog(SQLFunctionsInterSystemsTest.class);
+
+ public SQLFunctionsInterSystemsTest(String name) {
+ super(name);
+ }
+
+ public String[] getMappings() {
+ return new String[] {
+ "legacy/AltSimple.hbm.xml",
+ "legacy/Broken.hbm.xml",
+ "legacy/Blobber.hbm.xml",
+ "legacy/TestInterSystemsFunctionsClass.hbm.xml"
+ };
+ }
+
+ public static Test suite() {
+ return new TestSuite(SQLFunctionsInterSystemsTest.class);
+ }
+
+
+ public boolean appliesTo(Dialect dialect) {
+ // all these test case apply only to testing InterSystems' CacheSQL dialect
+ return dialect instanceof Cache71Dialect;
+ }
+
+ public void testDialectSQLFunctions() throws Exception {
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ Iterator iter = s.iterate("select max(s.count) from Simple s");
+
+ if ( getDialect() instanceof MySQLDialect ) assertTrue( iter.hasNext() && iter.next()==null );
+
+ Simple simple = new Simple();
+ simple.setName("Simple Dialect Function Test");
+ simple.setAddress("Simple Address");
+ simple.setPay(new Float(45.8));
+ simple.setCount(2);
+ s.save(simple, new Long(10) );
+
+ // Test to make sure allocating an specified object operates correctly.
+ assertTrue(
+ s.find("select new org.hibernate.test.legacy.S(s.count, s.address) from Simple s").size() == 1
+ );
+
+ // Quick check the base dialect functions operate correctly
+ assertTrue(
+ s.find("select max(s.count) from Simple s").size() == 1
+ );
+ assertTrue(
+ s.find("select count(*) from Simple s").size() == 1
+ );
+
+ if ( getDialect() instanceof Cache71Dialect) {
+ // Check Oracle Dialect mix of dialect functions - no args (no parenthesis and single arg functions
+ java.util.List rset = s.find("select s.name, sysdate, floor(s.pay), round(s.pay,0) from Simple s");
+ assertNotNull("Name string should have been returned",(((Object[])rset.get(0))[0]));
+ assertNotNull("Todays Date should have been returned",(((Object[])rset.get(0))[1]));
+ assertEquals("floor(45.8) result was incorrect ", new Integer(45), ( (Object[]) rset.get(0) )[2] );
+ assertEquals("round(45.8) result was incorrect ", new Float(46), ( (Object[]) rset.get(0) )[3] );
+
+ simple.setPay(new Float(-45.8));
+ s.update(simple);
+
+ // Test type conversions while using nested functions (Float to Int).
+ rset = s.find("select abs(round(s.pay,0)) from Simple s");
+ assertEquals("abs(round(-45.8)) result was incorrect ", new Float(46), rset.get(0));
+
+ // Test a larger depth 3 function example - Not a useful combo other than for testing
+ assertTrue(
+ s.find("select floor(round(sysdate,1)) from Simple s").size() == 1
+ );
+
+ // Test the oracle standard NVL funtion as a test of multi-param functions...
+ simple.setPay(null);
+ s.update(simple);
+ Double value = (Double) s.createQuery("select mod( nvl(s.pay, 5000), 2 ) from Simple as s where s.id = 10").list().get(0);
+ assertTrue( 0 == value.intValue() );
+ }
+
+ if ( (getDialect() instanceof Cache71Dialect) ) {
+ // Test the hsql standard MOD funtion as a test of multi-param functions...
+ Double value = (Double) s.find("select MOD(s.count, 2) from Simple as s where s.id = 10" ).get(0);
+ assertTrue( 0 == value.intValue() );
+ }
+
+ /*
+ if ( (getDialect() instanceof Cache71Dialect) ) {
+ // Test the hsql standard MOD funtion as a test of multi-param functions...
+ Date value = (Date) s.find("select sysdate from Simple as s where nvl(cast(null as date), sysdate)=sysdate" ).get(0);
+ assertTrue( value.equals(new java.sql.Date(System.currentTimeMillis())));
+ }
+ */
+
+ s.delete(simple);
+ t.commit();
+ s.close();
+ }
+
+ public void testSetProperties() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Simple simple = new Simple();
+ simple.setName("Simple 1");
+ s.save(simple, new Long(10) );
+ Query q = s.createQuery("from Simple s where s.name=:name and s.count=:count");
+ q.setProperties(simple);
+ assertTrue( q.list().get(0)==simple );
+ //misuse of "Single" as a propertyobject, but it was the first testclass i found with a collection ;)
+ Single single = new Single() { // trivial hack to test properties with arrays.
+ String[] getStuff() { return (String[]) getSeveral().toArray(new String[getSeveral().size()]); }
+ };
+
+ List l = new ArrayList();
+ l.add("Simple 1");
+ l.add("Slimeball");
+ single.setSeveral(l);
+ q = s.createQuery("from Simple s where s.name in (:several)");
+ q.setProperties(single);
+ assertTrue( q.list().get(0)==simple );
+
+
+ q = s.createQuery("from Simple s where s.name in (:stuff)");
+ q.setProperties(single);
+ assertTrue( q.list().get(0)==simple );
+ s.delete(simple);
+ t.commit();
+ s.close();
+ }
+
+ public void testBroken() throws Exception {
+ if (getDialect() instanceof Oracle9Dialect) return;
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Broken b = new Fixed();
+ b.setId( new Long(123));
+ b.setOtherId("foobar");
+ s.save(b);
+ s.flush();
+ b.setTimestamp( new Date() );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.update(b);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ b = (Broken) s.load( Broken.class, b );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.delete(b);
+ t.commit();
+ s.close();
+ }
+
+ public void testNothinToUpdate() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Simple simple = new Simple();
+ simple.setName("Simple 1");
+ s.save( simple, new Long(10) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.update( simple, new Long(10) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.update( simple, new Long(10) );
+ s.delete(simple);
+ t.commit();
+ s.close();
+ }
+
+ public void testCachedQuery() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Simple simple = new Simple();
+ simple.setName("Simple 1");
+ s.save( simple, new Long(10) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Query q = s.createQuery("from Simple s where s.name=?");
+ q.setCacheable(true);
+ q.setString(0, "Simple 1");
+ assertTrue( q.list().size()==1 );
+ assertTrue( q.list().size()==1 );
+ assertTrue( q.list().size()==1 );
+ q = s.createQuery("from Simple s where s.name=:name");
+ q.setCacheable(true);
+ q.setString("name", "Simple 1");
+ assertTrue( q.list().size()==1 );
+ simple = (Simple) q.list().get(0);
+
+ q.setString("name", "Simple 2");
+ assertTrue( q.list().size()==0 );
+ assertTrue( q.list().size()==0 );
+ simple.setName("Simple 2");
+ assertTrue( q.list().size()==1 );
+ assertTrue( q.list().size()==1 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ q = s.createQuery("from Simple s where s.name=:name");
+ q.setString("name", "Simple 2");
+ q.setCacheable(true);
+ assertTrue( q.list().size()==1 );
+ assertTrue( q.list().size()==1 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.update( simple, new Long(10) );
+ s.delete(simple);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ q = s.createQuery("from Simple s where s.name=?");
+ q.setCacheable(true);
+ q.setString(0, "Simple 1");
+ assertTrue( q.list().size()==0 );
+ assertTrue( q.list().size()==0 );
+ t.commit();
+ s.close();
+ }
+
+ public void testCachedQueryRegion() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Simple simple = new Simple();
+ simple.setName("Simple 1");
+ s.save( simple, new Long(10) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Query q = s.createQuery("from Simple s where s.name=?");
+ q.setCacheRegion("foo");
+ q.setCacheable(true);
+ q.setString(0, "Simple 1");
+ assertTrue( q.list().size()==1 );
+ assertTrue( q.list().size()==1 );
+ assertTrue( q.list().size()==1 );
+ q = s.createQuery("from Simple s where s.name=:name");
+ q.setCacheRegion("foo");
+ q.setCacheable(true);
+ q.setString("name", "Simple 1");
+ assertTrue( q.list().size()==1 );
+ simple = (Simple) q.list().get(0);
+
+ q.setString("name", "Simple 2");
+ assertTrue( q.list().size()==0 );
+ assertTrue( q.list().size()==0 );
+ simple.setName("Simple 2");
+ assertTrue( q.list().size()==1 );
+ assertTrue( q.list().size()==1 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.update( simple, new Long(10) );
+ s.delete(simple);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ q = s.createQuery("from Simple s where s.name=?");
+ q.setCacheRegion("foo");
+ q.setCacheable(true);
+ q.setString(0, "Simple 1");
+ assertTrue( q.list().size()==0 );
+ assertTrue( q.list().size()==0 );
+ t.commit();
+ s.close();
+ }
+
+ public void testSQLFunctions() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Simple simple = new Simple();
+ simple.setName("Simple 1");
+ s.save(simple, new Long(10) );
+
+ if ( getDialect() instanceof Cache71Dialect) {
+ s.find("from Simple s where repeat('foo', 3) = 'foofoofoo'");
+ s.find("from Simple s where repeat(s.name, 3) = 'foofoofoo'");
+ s.find("from Simple s where repeat( lower(s.name), (3 + (1-1)) / 2) = 'foofoofoo'");
+ }
+
+ assertTrue(
+ s.find("from Simple s where upper( s.name ) ='SIMPLE 1'").size()==1
+ );
+ if ( !(getDialect() instanceof HSQLDialect) ) {
+ assertTrue(
+ s.find("from Simple s where not( upper( s.name ) ='yada' or 1=2 or 'foo'='bar' or not('foo'='foo') or 'foo' like 'bar' )").size()==1
+ );
+ }
+ if ( !(getDialect() instanceof MySQLDialect) && !(getDialect() instanceof SybaseDialect) && !(getDialect() instanceof MckoiDialect) && !(getDialect() instanceof InterbaseDialect) && !(getDialect() instanceof TimesTenDialect) ) { //My SQL has a funny concatenation operator
+ assertTrue(
+ s.find("from Simple s where lower( s.name || ' foo' ) ='simple 1 foo'").size()==1
+ );
+ }
+ /* + is not concat in Cache
+ if ( (getDialect() instanceof Cache71Dialect) ) {
+ assertTrue(
+ s.find("from Simple s where lower( cons.name ' foo' ) ='simple 1 foo'").size()==1
+ );
+ }
+ */
+ if ( (getDialect() instanceof Cache71Dialect) ) {
+ assertTrue(
+ s.find("from Simple s where lower( concat(s.name, ' foo') ) ='simple 1 foo'").size()==1
+ );
+ }
+
+ Simple other = new Simple();
+ other.setName("Simple 2");
+ other.setCount(12);
+ simple.setOther(other);
+ s.save( other, new Long(20) );
+ //s.find("from Simple s where s.name ## 'cat|rat|bag'");
+ assertTrue(
+ s.find("from Simple s where upper( s.other.name ) ='SIMPLE 2'").size()==1
+ );
+ assertTrue(
+ s.find("from Simple s where not ( upper( s.other.name ) ='SIMPLE 2' )").size()==0
+ );
+ assertTrue(
+ s.find("select distinct s from Simple s where ( ( s.other.count + 3 ) = (15*2)/2 and s.count = 69) or ( ( s.other.count + 2 ) / 7 ) = 2").size()==1
+ );
+ assertTrue(
+ s.find("select s from Simple s where ( ( s.other.count + 3 ) = (15*2)/2 and s.count = 69) or ( ( s.other.count + 2 ) / 7 ) = 2 order by s.other.count").size()==1
+ );
+ Simple min = new Simple();
+ min.setCount(-1);
+ s.save(min, new Long(30) );
+ if ( ! (getDialect() instanceof MySQLDialect) && ! (getDialect() instanceof HSQLDialect) ) { //My SQL has no subqueries
+ assertTrue(
+ s.find("from Simple s where s.count > ( select min(sim.count) from Simple sim )").size()==2
+ );
+ t.commit();
+ t = s.beginTransaction();
+ assertTrue(
+ s.find("from Simple s where s = some( select sim from Simple sim where sim.count>=0 ) and s.count >= 0").size()==2
+ );
+ assertTrue(
+ s.find("from Simple s where s = some( select sim from Simple sim where sim.other.count=s.other.count ) and s.other.count > 0").size()==1
+ );
+ }
+
+ Iterator iter = s.iterate("select sum(s.count) from Simple s group by s.count having sum(s.count) > 10");
+ assertTrue( iter.hasNext() );
+ assertEquals( new Long(12), iter.next() );
+ assertTrue( !iter.hasNext() );
+ if ( ! (getDialect() instanceof MySQLDialect) ) {
+ iter = s.iterate("select s.count from Simple s group by s.count having s.count = 12");
+ assertTrue( iter.hasNext() );
+ }
+
+ s.iterate("select s.id, s.count, count(t), max(t.date) from Simple s, Simple t where s.count = t.count group by s.id, s.count order by s.count");
+
+ Query q = s.createQuery("from Simple s");
+ q.setMaxResults(10);
+ assertTrue( q.list().size()==3 );
+ q = s.createQuery("from Simple s");
+ q.setMaxResults(1);
+ assertTrue( q.list().size()==1 );
+ q = s.createQuery("from Simple s");
+ assertTrue( q.list().size()==3 );
+ q = s.createQuery("from Simple s where s.name = ?");
+ q.setString(0, "Simple 1");
+ assertTrue( q.list().size()==1 );
+ q = s.createQuery("from Simple s where s.name = ? and upper(s.name) = ?");
+ q.setString(1, "SIMPLE 1");
+ q.setString(0, "Simple 1");
+ q.setFirstResult(0);
+ assertTrue( q.iterate().hasNext() );
+ q = s.createQuery("from Simple s where s.name = :foo and upper(s.name) = :bar or s.count=:count or s.count=:count + 1");
+ q.setParameter("bar", "SIMPLE 1");
+ q.setString("foo", "Simple 1");
+ q.setInteger("count", 69);
+ q.setFirstResult(0);
+ assertTrue( q.iterate().hasNext() );
+ q = s.createQuery("select s.id from Simple s");
+ q.setFirstResult(1);
+ q.setMaxResults(2);
+ iter = q.iterate();
+ int i=0;
+ while ( iter.hasNext() ) {
+ assertTrue( iter.next() instanceof Long );
+ i++;
+ }
+ assertTrue(i==2);
+ q = s.createQuery("select all s, s.other from Simple s where s = :s");
+ q.setParameter("s", simple);
+ assertTrue( q.list().size()==1 );
+
+
+ q = s.createQuery("from Simple s where s.name in (:name_list) and s.count > :count");
+ HashSet set = new HashSet();
+ set.add("Simple 1"); set.add("foo");
+ q.setParameterList( "name_list", set );
+ q.setParameter("count", new Integer(-1) );
+ assertTrue( q.list().size()==1 );
+
+ ScrollableResults sr = s.createQuery("from Simple s").scroll();
+ sr.next();
+ sr.get(0);
+ sr.close();
+
+ s.delete(other);
+ s.delete(simple);
+ s.delete(min);
+ t.commit();
+ s.close();
+
+ }
+
+ public void testBlobClob() throws Exception {
+
+ Session s = openSession();
+ Blobber b = new Blobber();
+ b.setBlob( Hibernate.createBlob( "foo/bar/baz".getBytes() ) );
+ b.setClob( Hibernate.createClob("foo/bar/baz") );
+ s.save(b);
+ //s.refresh(b);
+ //assertTrue( b.getClob() instanceof ClobImpl );
+ s.flush();
+ s.refresh(b);
+ //b.getBlob().setBytes( 2, "abc".getBytes() );
+ log.debug("levinson: just bfore b.getClob()");
+ b.getClob().getSubString(2, 3);
+ //b.getClob().setString(2, "abc");
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ b = (Blobber) s.load( Blobber.class, new Integer( b.getId() ) );
+ Blobber b2 = new Blobber();
+ s.save(b2);
+ b2.setBlob( b.getBlob() );
+ b.setBlob(null);
+ //assertTrue( b.getClob().getSubString(1, 3).equals("fab") );
+ b.getClob().getSubString(1, 6);
+ //b.getClob().setString(1, "qwerty");
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ b = (Blobber) s.load( Blobber.class, new Integer( b.getId() ) );
+ b.setClob( Hibernate.createClob("xcvfxvc xcvbx cvbx cvbx cvbxcvbxcvbxcvb") );
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ b = (Blobber) s.load( Blobber.class, new Integer( b.getId() ) );
+ assertTrue( b.getClob().getSubString(1, 7).equals("xcvfxvc") );
+ //b.getClob().setString(5, "1234567890");
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+
+ /*InputStream is = getClass().getClassLoader().getResourceAsStream("jdbc20.pdf");
+ s = sessionsopenSession();
+ b = (Blobber) s.load( Blobber.class, new Integer( b.getId() ) );
+ System.out.println( is.available() );
+ int size = is.available();
+ b.setBlob( Hibernate.createBlob( is, is.available() ) );
+ s.flush();
+ s.connection().commit();
+ ResultSet rs = s.connection().createStatement().executeQuery("select datalength(blob_) from blobber where id=" + b.getId() );
+ rs.next();
+ assertTrue( size==rs.getInt(1) );
+ rs.close();
+ s.close();
+
+ s = sessionsopenSession();
+ b = (Blobber) s.load( Blobber.class, new Integer( b.getId() ) );
+ File f = new File("C:/foo.pdf");
+ f.createNewFile();
+ FileOutputStream fos = new FileOutputStream(f);
+ Blob blob = b.getBlob();
+ byte[] bytes = blob.getBytes( 1, (int) blob.length() );
+ System.out.println( bytes.length );
+ fos.write(bytes);
+ fos.flush();
+ fos.close();
+ s.close();*/
+
+ }
+
+ public void testSqlFunctionAsAlias() throws Exception {
+ String functionName = locateAppropriateDialectFunctionNameForAliasTest();
+ if (functionName == null) {
+ log.info("Dialect does not list any no-arg functions");
+ return;
+ }
+
+ log.info("Using function named [" + functionName + "] for 'function as alias' test");
+ String query = "select " + functionName + " from Simple as " + functionName + " where " + functionName + ".id = 10";
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Simple simple = new Simple();
+ simple.setName("Simple 1");
+ s.save( simple, new Long(10) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ List result = s.find(query);
+ assertTrue( result.size() == 1 );
+ assertTrue(result.get(0) instanceof Simple);
+ s.delete( result.get(0) );
+ t.commit();
+ s.close();
+ }
+
+ private String locateAppropriateDialectFunctionNameForAliasTest() {
+ for (Iterator itr = getDialect().getFunctions().entrySet().iterator(); itr.hasNext(); ) {
+ final Map.Entry entry = (Map.Entry) itr.next();
+ final SQLFunction function = (SQLFunction) entry.getValue();
+ if ( !function.hasArguments() && !function.hasParenthesesIfNoArguments() ) {
+ return (String) entry.getKey();
+ }
+ }
+ return null;
+ }
+
+ public void testCachedQueryOnInsert() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Simple simple = new Simple();
+ simple.setName("Simple 1");
+ s.save( simple, new Long(10) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Query q = s.createQuery("from Simple s");
+ List list = q.setCacheable(true).list();
+ assertTrue( list.size()==1 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ q = s.createQuery("from Simple s");
+ list = q.setCacheable(true).list();
+ assertTrue( list.size()==1 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Simple simple2 = new Simple();
+ simple2.setCount(133);
+ s.save( simple2, new Long(12) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ q = s.createQuery("from Simple s");
+ list = q.setCacheable(true).list();
+ assertTrue( list.size()==2 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ q = s.createQuery("from Simple s");
+ list = q.setCacheable(true).list();
+ assertTrue( list.size()==2 );
+ Iterator i = list.iterator();
+ while ( i.hasNext() ) s.delete( i.next() );
+ t.commit();
+ s.close();
+
+ }
+
+ public void testInterSystemsFunctions() throws Exception {
+ Calendar cal = new GregorianCalendar();
+ cal.set(1977,6,3,0,0,0);
+ java.sql.Timestamp testvalue = new java.sql.Timestamp(cal.getTimeInMillis());
+ testvalue.setNanos(0);
+ Calendar cal3 = new GregorianCalendar();
+ cal3.set(1976,2,3,0,0,0);
+ java.sql.Timestamp testvalue3 = new java.sql.Timestamp(cal3.getTimeInMillis());
+ testvalue3.setNanos(0);
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ try {
+ Statement stmt = s.connection().createStatement();
+ stmt.executeUpdate("DROP FUNCTION spLock FROM TestInterSystemsFunctionsClass");
+ t.commit();
+ }
+ catch (Exception ex) {
+ System.out.println("as we expected stored procedure sp does not exist when we drop it");
+
+ }
+ t = s.beginTransaction();
+ Statement stmt = s.connection().createStatement();
+ String create_function = "CREATE FUNCTION SQLUser.TestInterSystemsFunctionsClass_spLock\n" +
+ " ( INOUT pHandle %SQLProcContext, \n" +
+ " ROWID INTEGER \n" +
+ " )\n" +
+ " FOR User.TestInterSystemsFunctionsClass " +
+ " PROCEDURE\n" +
+ " RETURNS INTEGER\n" +
+ " LANGUAGE OBJECTSCRIPT\n" +
+ " {\n" +
+ " q 0\n" +
+ " }";
+ stmt.executeUpdate(create_function);
+ t.commit();
+ t = s.beginTransaction();
+
+ TestInterSystemsFunctionsClass object = new TestInterSystemsFunctionsClass();
+ object.setDateText("1977-07-03");
+ object.setDate1(testvalue);
+ object.setDate3(testvalue3);
+ s.save( object, new Long(10));
+ t.commit();
+ s.close();
+ s = openSession();
+ s.clear();
+ t = s.beginTransaction();
+ TestInterSystemsFunctionsClass test = (TestInterSystemsFunctionsClass) s.get(TestInterSystemsFunctionsClass.class, new Long(10));
+ assertTrue( test.getDate1().equals(testvalue));
+ test = (TestInterSystemsFunctionsClass) s.get(TestInterSystemsFunctionsClass.class, new Long(10), LockMode.UPGRADE);
+ assertTrue( test.getDate1().equals(testvalue));
+ Date value = (Date) s.find("select nvl(o.date,o.dateText) from TestInterSystemsFunctionsClass as o" ).get(0);
+ assertTrue( value.equals(testvalue));
+ Object nv = s.find("select nullif(o.dateText,o.dateText) from TestInterSystemsFunctionsClass as o" ).get(0);
+ assertTrue( nv == null);
+ String dateText = (String) s.find("select nvl(o.dateText,o.date) from TestInterSystemsFunctionsClass as o" ).get(0);
+ assertTrue( dateText.equals("1977-07-03"));
+ value = (Date) s.find("select ifnull(o.date,o.date1) from TestInterSystemsFunctionsClass as o" ).get(0);
+ assertTrue( value.equals(testvalue));
+ value = (Date) s.find("select ifnull(o.date3,o.date,o.date1) from TestInterSystemsFunctionsClass as o" ).get(0);
+ assertTrue( value.equals(testvalue));
+ Integer pos = (Integer) s.find("select position('07', o.dateText) from TestInterSystemsFunctionsClass as o" ).get(0);
+ assertTrue(pos.intValue() == 6);
+ String st = (String) s.find("select convert(o.date1, SQL_TIME) from TestInterSystemsFunctionsClass as o" ).get(0);
+ assertTrue( st.equals("00:00:00"));
+ java.sql.Time tm = (java.sql.Time) s.find("select cast(o.date1, time) from TestInterSystemsFunctionsClass as o" ).get(0);
+ assertTrue( tm.toString().equals("00:00:00"));
+ Double diff = (Double)s.find("select timestampdiff(SQL_TSI_FRAC_SECOND, o.date3, o.date1) from TestInterSystemsFunctionsClass as o" ).get(0);
+ assertTrue(diff.doubleValue() != 0.0);
+ diff = (Double)s.find("select timestampdiff(SQL_TSI_MONTH, o.date3, o.date1) from TestInterSystemsFunctionsClass as o" ).get(0);
+ assertTrue(diff.doubleValue() == 16.0);
+ diff = (Double)s.find("select timestampdiff(SQL_TSI_WEEK, o.date3, o.date1) from TestInterSystemsFunctionsClass as o" ).get(0);
+ assertTrue(diff.doubleValue() >= 16*4);
+ diff = (Double)s.find("select timestampdiff(SQL_TSI_YEAR, o.date3, o.date1) from TestInterSystemsFunctionsClass as o" ).get(0);
+ assertTrue(diff.doubleValue() == 1.0);
+
+ t.commit();
+ s.close();
+
+
+ }
+
+}
Added: trunk/Hibernate3/test/org/hibernate/test/dialect/cache/TestInterSystemsFunctionsClass.hbm.xml
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/dialect/cache/TestInterSystemsFunctionsClass.hbm.xml 2006-11-08 23:35:17 UTC (rev 10778)
+++ trunk/Hibernate3/test/org/hibernate/test/dialect/cache/TestInterSystemsFunctionsClass.hbm.xml 2006-11-08 23:35:40 UTC (rev 10779)
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.dialect.cache" >
+
+ <class name="TestInterSystemsFunctionsClass" table="SQLUser.TestInterSystemsFunctionsClass">
+ <id type="long" column="id_">
+ <generator class="assigned"/>
+ </id>
+ <property name="date" column="date_"/>
+ <property name="date1" column="date1_"/>
+ <property name="date3" column="date3_"/>
+ <property name="dateText" column="dateText_"/>
+ </class>
+
+</hibernate-mapping>
Added: trunk/Hibernate3/test/org/hibernate/test/dialect/cache/TestInterSystemsFunctionsClass.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/dialect/cache/TestInterSystemsFunctionsClass.java 2006-11-08 23:35:17 UTC (rev 10778)
+++ trunk/Hibernate3/test/org/hibernate/test/dialect/cache/TestInterSystemsFunctionsClass.java 2006-11-08 23:35:40 UTC (rev 10779)
@@ -0,0 +1,51 @@
+package org.hibernate.test.dialect.cache;
+
+import java.util.Date;
+
+/**
+ * Entity for testing function support of InterSystems' CacheSQL...
+ *
+ * @author Jonathan Levinson
+ */
+public class TestInterSystemsFunctionsClass {
+ private java.util.Date date3;
+ private java.util.Date date1;
+ private java.util.Date date;
+ private String dateText;
+
+ public Date getDate() {
+ return date;
+ }
+
+ public void setDate(Date date) {
+ this.date = date;
+ }
+
+
+ public String getDateText() {
+ return dateText;
+ }
+
+ public void setDateText(String dateText) {
+ this.dateText = dateText;
+ }
+
+
+ public Date getDate1() {
+ return date1;
+ }
+
+ public void setDate1(Date date1) {
+ this.date1 = date1;
+ }
+
+
+ public Date getDate3() {
+ return date3;
+ }
+
+ public void setDate3(Date date3) {
+ this.date3 = date3;
+ }
+
+}
18 years, 4 months
Hibernate SVN: r10778 - in branches/Branch_3_2/Hibernate3/test/org/hibernate/test: . dialect dialect/cache
by hibernate-commits@lists.jboss.org
Author: steve.ebersole(a)jboss.com
Date: 2006-11-08 18:35:17 -0500 (Wed, 08 Nov 2006)
New Revision: 10778
Added:
branches/Branch_3_2/Hibernate3/test/org/hibernate/test/dialect/
branches/Branch_3_2/Hibernate3/test/org/hibernate/test/dialect/cache/
branches/Branch_3_2/Hibernate3/test/org/hibernate/test/dialect/cache/SQLFunctionsInterSystemsTest.java
branches/Branch_3_2/Hibernate3/test/org/hibernate/test/dialect/cache/TestInterSystemsFunctionsClass.hbm.xml
branches/Branch_3_2/Hibernate3/test/org/hibernate/test/dialect/cache/TestInterSystemsFunctionsClass.java
Log:
regression tests specific for InterSystems' CacheSQL dialect
Added: branches/Branch_3_2/Hibernate3/test/org/hibernate/test/dialect/cache/SQLFunctionsInterSystemsTest.java
===================================================================
--- branches/Branch_3_2/Hibernate3/test/org/hibernate/test/dialect/cache/SQLFunctionsInterSystemsTest.java 2006-11-08 22:02:28 UTC (rev 10777)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/dialect/cache/SQLFunctionsInterSystemsTest.java 2006-11-08 23:35:17 UTC (rev 10778)
@@ -0,0 +1,741 @@
+package org.hibernate.test.dialect.cache;
+
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.hibernate.Hibernate;
+import org.hibernate.LockMode;
+import org.hibernate.Query;
+import org.hibernate.ScrollableResults;
+import org.hibernate.Transaction;
+import org.hibernate.classic.Session;
+import org.hibernate.dialect.Cache71Dialect;
+import org.hibernate.dialect.HSQLDialect;
+import org.hibernate.dialect.InterbaseDialect;
+import org.hibernate.dialect.MckoiDialect;
+import org.hibernate.dialect.MySQLDialect;
+import org.hibernate.dialect.Oracle9Dialect;
+import org.hibernate.dialect.SybaseDialect;
+import org.hibernate.dialect.TimesTenDialect;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.function.SQLFunction;
+import org.hibernate.test.TestCase;
+import org.hibernate.test.legacy.Simple;
+import org.hibernate.test.legacy.Single;
+import org.hibernate.test.legacy.Broken;
+import org.hibernate.test.legacy.Fixed;
+import org.hibernate.test.legacy.Blobber;
+
+/**
+ * Tests for function support on CacheSQL...
+ *
+ * @author Jonathan Levinson
+ */
+public class SQLFunctionsInterSystemsTest extends TestCase {
+
+ private static final Log log = LogFactory.getLog(SQLFunctionsInterSystemsTest.class);
+
+ public SQLFunctionsInterSystemsTest(String name) {
+ super(name);
+ }
+
+ public String[] getMappings() {
+ return new String[] {
+ "legacy/AltSimple.hbm.xml",
+ "legacy/Broken.hbm.xml",
+ "legacy/Blobber.hbm.xml",
+ "legacy/TestInterSystemsFunctionsClass.hbm.xml"
+ };
+ }
+
+ public static Test suite() {
+ return new TestSuite(SQLFunctionsInterSystemsTest.class);
+ }
+
+
+ public boolean appliesTo(Dialect dialect) {
+ // all these test case apply only to testing InterSystems' CacheSQL dialect
+ return dialect instanceof Cache71Dialect;
+ }
+
+ public void testDialectSQLFunctions() throws Exception {
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+
+ Iterator iter = s.iterate("select max(s.count) from Simple s");
+
+ if ( getDialect() instanceof MySQLDialect ) assertTrue( iter.hasNext() && iter.next()==null );
+
+ Simple simple = new Simple();
+ simple.setName("Simple Dialect Function Test");
+ simple.setAddress("Simple Address");
+ simple.setPay(new Float(45.8));
+ simple.setCount(2);
+ s.save(simple, new Long(10) );
+
+ // Test to make sure allocating an specified object operates correctly.
+ assertTrue(
+ s.find("select new org.hibernate.test.legacy.S(s.count, s.address) from Simple s").size() == 1
+ );
+
+ // Quick check the base dialect functions operate correctly
+ assertTrue(
+ s.find("select max(s.count) from Simple s").size() == 1
+ );
+ assertTrue(
+ s.find("select count(*) from Simple s").size() == 1
+ );
+
+ if ( getDialect() instanceof Cache71Dialect) {
+ // Check Oracle Dialect mix of dialect functions - no args (no parenthesis and single arg functions
+ java.util.List rset = s.find("select s.name, sysdate, floor(s.pay), round(s.pay,0) from Simple s");
+ assertNotNull("Name string should have been returned",(((Object[])rset.get(0))[0]));
+ assertNotNull("Todays Date should have been returned",(((Object[])rset.get(0))[1]));
+ assertEquals("floor(45.8) result was incorrect ", new Integer(45), ( (Object[]) rset.get(0) )[2] );
+ assertEquals("round(45.8) result was incorrect ", new Float(46), ( (Object[]) rset.get(0) )[3] );
+
+ simple.setPay(new Float(-45.8));
+ s.update(simple);
+
+ // Test type conversions while using nested functions (Float to Int).
+ rset = s.find("select abs(round(s.pay,0)) from Simple s");
+ assertEquals("abs(round(-45.8)) result was incorrect ", new Float(46), rset.get(0));
+
+ // Test a larger depth 3 function example - Not a useful combo other than for testing
+ assertTrue(
+ s.find("select floor(round(sysdate,1)) from Simple s").size() == 1
+ );
+
+ // Test the oracle standard NVL funtion as a test of multi-param functions...
+ simple.setPay(null);
+ s.update(simple);
+ Double value = (Double) s.createQuery("select mod( nvl(s.pay, 5000), 2 ) from Simple as s where s.id = 10").list().get(0);
+ assertTrue( 0 == value.intValue() );
+ }
+
+ if ( (getDialect() instanceof Cache71Dialect) ) {
+ // Test the hsql standard MOD funtion as a test of multi-param functions...
+ Double value = (Double) s.find("select MOD(s.count, 2) from Simple as s where s.id = 10" ).get(0);
+ assertTrue( 0 == value.intValue() );
+ }
+
+ /*
+ if ( (getDialect() instanceof Cache71Dialect) ) {
+ // Test the hsql standard MOD funtion as a test of multi-param functions...
+ Date value = (Date) s.find("select sysdate from Simple as s where nvl(cast(null as date), sysdate)=sysdate" ).get(0);
+ assertTrue( value.equals(new java.sql.Date(System.currentTimeMillis())));
+ }
+ */
+
+ s.delete(simple);
+ t.commit();
+ s.close();
+ }
+
+ public void testSetProperties() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Simple simple = new Simple();
+ simple.setName("Simple 1");
+ s.save(simple, new Long(10) );
+ Query q = s.createQuery("from Simple s where s.name=:name and s.count=:count");
+ q.setProperties(simple);
+ assertTrue( q.list().get(0)==simple );
+ //misuse of "Single" as a propertyobject, but it was the first testclass i found with a collection ;)
+ Single single = new Single() { // trivial hack to test properties with arrays.
+ String[] getStuff() { return (String[]) getSeveral().toArray(new String[getSeveral().size()]); }
+ };
+
+ List l = new ArrayList();
+ l.add("Simple 1");
+ l.add("Slimeball");
+ single.setSeveral(l);
+ q = s.createQuery("from Simple s where s.name in (:several)");
+ q.setProperties(single);
+ assertTrue( q.list().get(0)==simple );
+
+
+ q = s.createQuery("from Simple s where s.name in (:stuff)");
+ q.setProperties(single);
+ assertTrue( q.list().get(0)==simple );
+ s.delete(simple);
+ t.commit();
+ s.close();
+ }
+
+ public void testBroken() throws Exception {
+ if (getDialect() instanceof Oracle9Dialect) return;
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Broken b = new Fixed();
+ b.setId( new Long(123));
+ b.setOtherId("foobar");
+ s.save(b);
+ s.flush();
+ b.setTimestamp( new Date() );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.update(b);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ b = (Broken) s.load( Broken.class, b );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.delete(b);
+ t.commit();
+ s.close();
+ }
+
+ public void testNothinToUpdate() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Simple simple = new Simple();
+ simple.setName("Simple 1");
+ s.save( simple, new Long(10) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.update( simple, new Long(10) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.update( simple, new Long(10) );
+ s.delete(simple);
+ t.commit();
+ s.close();
+ }
+
+ public void testCachedQuery() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Simple simple = new Simple();
+ simple.setName("Simple 1");
+ s.save( simple, new Long(10) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Query q = s.createQuery("from Simple s where s.name=?");
+ q.setCacheable(true);
+ q.setString(0, "Simple 1");
+ assertTrue( q.list().size()==1 );
+ assertTrue( q.list().size()==1 );
+ assertTrue( q.list().size()==1 );
+ q = s.createQuery("from Simple s where s.name=:name");
+ q.setCacheable(true);
+ q.setString("name", "Simple 1");
+ assertTrue( q.list().size()==1 );
+ simple = (Simple) q.list().get(0);
+
+ q.setString("name", "Simple 2");
+ assertTrue( q.list().size()==0 );
+ assertTrue( q.list().size()==0 );
+ simple.setName("Simple 2");
+ assertTrue( q.list().size()==1 );
+ assertTrue( q.list().size()==1 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ q = s.createQuery("from Simple s where s.name=:name");
+ q.setString("name", "Simple 2");
+ q.setCacheable(true);
+ assertTrue( q.list().size()==1 );
+ assertTrue( q.list().size()==1 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.update( simple, new Long(10) );
+ s.delete(simple);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ q = s.createQuery("from Simple s where s.name=?");
+ q.setCacheable(true);
+ q.setString(0, "Simple 1");
+ assertTrue( q.list().size()==0 );
+ assertTrue( q.list().size()==0 );
+ t.commit();
+ s.close();
+ }
+
+ public void testCachedQueryRegion() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Simple simple = new Simple();
+ simple.setName("Simple 1");
+ s.save( simple, new Long(10) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Query q = s.createQuery("from Simple s where s.name=?");
+ q.setCacheRegion("foo");
+ q.setCacheable(true);
+ q.setString(0, "Simple 1");
+ assertTrue( q.list().size()==1 );
+ assertTrue( q.list().size()==1 );
+ assertTrue( q.list().size()==1 );
+ q = s.createQuery("from Simple s where s.name=:name");
+ q.setCacheRegion("foo");
+ q.setCacheable(true);
+ q.setString("name", "Simple 1");
+ assertTrue( q.list().size()==1 );
+ simple = (Simple) q.list().get(0);
+
+ q.setString("name", "Simple 2");
+ assertTrue( q.list().size()==0 );
+ assertTrue( q.list().size()==0 );
+ simple.setName("Simple 2");
+ assertTrue( q.list().size()==1 );
+ assertTrue( q.list().size()==1 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ s.update( simple, new Long(10) );
+ s.delete(simple);
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ q = s.createQuery("from Simple s where s.name=?");
+ q.setCacheRegion("foo");
+ q.setCacheable(true);
+ q.setString(0, "Simple 1");
+ assertTrue( q.list().size()==0 );
+ assertTrue( q.list().size()==0 );
+ t.commit();
+ s.close();
+ }
+
+ public void testSQLFunctions() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Simple simple = new Simple();
+ simple.setName("Simple 1");
+ s.save(simple, new Long(10) );
+
+ if ( getDialect() instanceof Cache71Dialect) {
+ s.find("from Simple s where repeat('foo', 3) = 'foofoofoo'");
+ s.find("from Simple s where repeat(s.name, 3) = 'foofoofoo'");
+ s.find("from Simple s where repeat( lower(s.name), (3 + (1-1)) / 2) = 'foofoofoo'");
+ }
+
+ assertTrue(
+ s.find("from Simple s where upper( s.name ) ='SIMPLE 1'").size()==1
+ );
+ if ( !(getDialect() instanceof HSQLDialect) ) {
+ assertTrue(
+ s.find("from Simple s where not( upper( s.name ) ='yada' or 1=2 or 'foo'='bar' or not('foo'='foo') or 'foo' like 'bar' )").size()==1
+ );
+ }
+ if ( !(getDialect() instanceof MySQLDialect) && !(getDialect() instanceof SybaseDialect) && !(getDialect() instanceof MckoiDialect) && !(getDialect() instanceof InterbaseDialect) && !(getDialect() instanceof TimesTenDialect) ) { //My SQL has a funny concatenation operator
+ assertTrue(
+ s.find("from Simple s where lower( s.name || ' foo' ) ='simple 1 foo'").size()==1
+ );
+ }
+ /* + is not concat in Cache
+ if ( (getDialect() instanceof Cache71Dialect) ) {
+ assertTrue(
+ s.find("from Simple s where lower( cons.name ' foo' ) ='simple 1 foo'").size()==1
+ );
+ }
+ */
+ if ( (getDialect() instanceof Cache71Dialect) ) {
+ assertTrue(
+ s.find("from Simple s where lower( concat(s.name, ' foo') ) ='simple 1 foo'").size()==1
+ );
+ }
+
+ Simple other = new Simple();
+ other.setName("Simple 2");
+ other.setCount(12);
+ simple.setOther(other);
+ s.save( other, new Long(20) );
+ //s.find("from Simple s where s.name ## 'cat|rat|bag'");
+ assertTrue(
+ s.find("from Simple s where upper( s.other.name ) ='SIMPLE 2'").size()==1
+ );
+ assertTrue(
+ s.find("from Simple s where not ( upper( s.other.name ) ='SIMPLE 2' )").size()==0
+ );
+ assertTrue(
+ s.find("select distinct s from Simple s where ( ( s.other.count + 3 ) = (15*2)/2 and s.count = 69) or ( ( s.other.count + 2 ) / 7 ) = 2").size()==1
+ );
+ assertTrue(
+ s.find("select s from Simple s where ( ( s.other.count + 3 ) = (15*2)/2 and s.count = 69) or ( ( s.other.count + 2 ) / 7 ) = 2 order by s.other.count").size()==1
+ );
+ Simple min = new Simple();
+ min.setCount(-1);
+ s.save(min, new Long(30) );
+ if ( ! (getDialect() instanceof MySQLDialect) && ! (getDialect() instanceof HSQLDialect) ) { //My SQL has no subqueries
+ assertTrue(
+ s.find("from Simple s where s.count > ( select min(sim.count) from Simple sim )").size()==2
+ );
+ t.commit();
+ t = s.beginTransaction();
+ assertTrue(
+ s.find("from Simple s where s = some( select sim from Simple sim where sim.count>=0 ) and s.count >= 0").size()==2
+ );
+ assertTrue(
+ s.find("from Simple s where s = some( select sim from Simple sim where sim.other.count=s.other.count ) and s.other.count > 0").size()==1
+ );
+ }
+
+ Iterator iter = s.iterate("select sum(s.count) from Simple s group by s.count having sum(s.count) > 10");
+ assertTrue( iter.hasNext() );
+ assertEquals( new Long(12), iter.next() );
+ assertTrue( !iter.hasNext() );
+ if ( ! (getDialect() instanceof MySQLDialect) ) {
+ iter = s.iterate("select s.count from Simple s group by s.count having s.count = 12");
+ assertTrue( iter.hasNext() );
+ }
+
+ s.iterate("select s.id, s.count, count(t), max(t.date) from Simple s, Simple t where s.count = t.count group by s.id, s.count order by s.count");
+
+ Query q = s.createQuery("from Simple s");
+ q.setMaxResults(10);
+ assertTrue( q.list().size()==3 );
+ q = s.createQuery("from Simple s");
+ q.setMaxResults(1);
+ assertTrue( q.list().size()==1 );
+ q = s.createQuery("from Simple s");
+ assertTrue( q.list().size()==3 );
+ q = s.createQuery("from Simple s where s.name = ?");
+ q.setString(0, "Simple 1");
+ assertTrue( q.list().size()==1 );
+ q = s.createQuery("from Simple s where s.name = ? and upper(s.name) = ?");
+ q.setString(1, "SIMPLE 1");
+ q.setString(0, "Simple 1");
+ q.setFirstResult(0);
+ assertTrue( q.iterate().hasNext() );
+ q = s.createQuery("from Simple s where s.name = :foo and upper(s.name) = :bar or s.count=:count or s.count=:count + 1");
+ q.setParameter("bar", "SIMPLE 1");
+ q.setString("foo", "Simple 1");
+ q.setInteger("count", 69);
+ q.setFirstResult(0);
+ assertTrue( q.iterate().hasNext() );
+ q = s.createQuery("select s.id from Simple s");
+ q.setFirstResult(1);
+ q.setMaxResults(2);
+ iter = q.iterate();
+ int i=0;
+ while ( iter.hasNext() ) {
+ assertTrue( iter.next() instanceof Long );
+ i++;
+ }
+ assertTrue(i==2);
+ q = s.createQuery("select all s, s.other from Simple s where s = :s");
+ q.setParameter("s", simple);
+ assertTrue( q.list().size()==1 );
+
+
+ q = s.createQuery("from Simple s where s.name in (:name_list) and s.count > :count");
+ HashSet set = new HashSet();
+ set.add("Simple 1"); set.add("foo");
+ q.setParameterList( "name_list", set );
+ q.setParameter("count", new Integer(-1) );
+ assertTrue( q.list().size()==1 );
+
+ ScrollableResults sr = s.createQuery("from Simple s").scroll();
+ sr.next();
+ sr.get(0);
+ sr.close();
+
+ s.delete(other);
+ s.delete(simple);
+ s.delete(min);
+ t.commit();
+ s.close();
+
+ }
+
+ public void testBlobClob() throws Exception {
+
+ Session s = openSession();
+ Blobber b = new Blobber();
+ b.setBlob( Hibernate.createBlob( "foo/bar/baz".getBytes() ) );
+ b.setClob( Hibernate.createClob("foo/bar/baz") );
+ s.save(b);
+ //s.refresh(b);
+ //assertTrue( b.getClob() instanceof ClobImpl );
+ s.flush();
+ s.refresh(b);
+ //b.getBlob().setBytes( 2, "abc".getBytes() );
+ log.debug("levinson: just bfore b.getClob()");
+ b.getClob().getSubString(2, 3);
+ //b.getClob().setString(2, "abc");
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ b = (Blobber) s.load( Blobber.class, new Integer( b.getId() ) );
+ Blobber b2 = new Blobber();
+ s.save(b2);
+ b2.setBlob( b.getBlob() );
+ b.setBlob(null);
+ //assertTrue( b.getClob().getSubString(1, 3).equals("fab") );
+ b.getClob().getSubString(1, 6);
+ //b.getClob().setString(1, "qwerty");
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ b = (Blobber) s.load( Blobber.class, new Integer( b.getId() ) );
+ b.setClob( Hibernate.createClob("xcvfxvc xcvbx cvbx cvbx cvbxcvbxcvbxcvb") );
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+ s = openSession();
+ b = (Blobber) s.load( Blobber.class, new Integer( b.getId() ) );
+ assertTrue( b.getClob().getSubString(1, 7).equals("xcvfxvc") );
+ //b.getClob().setString(5, "1234567890");
+ s.flush();
+ s.connection().commit();
+ s.close();
+
+
+ /*InputStream is = getClass().getClassLoader().getResourceAsStream("jdbc20.pdf");
+ s = sessionsopenSession();
+ b = (Blobber) s.load( Blobber.class, new Integer( b.getId() ) );
+ System.out.println( is.available() );
+ int size = is.available();
+ b.setBlob( Hibernate.createBlob( is, is.available() ) );
+ s.flush();
+ s.connection().commit();
+ ResultSet rs = s.connection().createStatement().executeQuery("select datalength(blob_) from blobber where id=" + b.getId() );
+ rs.next();
+ assertTrue( size==rs.getInt(1) );
+ rs.close();
+ s.close();
+
+ s = sessionsopenSession();
+ b = (Blobber) s.load( Blobber.class, new Integer( b.getId() ) );
+ File f = new File("C:/foo.pdf");
+ f.createNewFile();
+ FileOutputStream fos = new FileOutputStream(f);
+ Blob blob = b.getBlob();
+ byte[] bytes = blob.getBytes( 1, (int) blob.length() );
+ System.out.println( bytes.length );
+ fos.write(bytes);
+ fos.flush();
+ fos.close();
+ s.close();*/
+
+ }
+
+ public void testSqlFunctionAsAlias() throws Exception {
+ String functionName = locateAppropriateDialectFunctionNameForAliasTest();
+ if (functionName == null) {
+ log.info("Dialect does not list any no-arg functions");
+ return;
+ }
+
+ log.info("Using function named [" + functionName + "] for 'function as alias' test");
+ String query = "select " + functionName + " from Simple as " + functionName + " where " + functionName + ".id = 10";
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Simple simple = new Simple();
+ simple.setName("Simple 1");
+ s.save( simple, new Long(10) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ List result = s.find(query);
+ assertTrue( result.size() == 1 );
+ assertTrue(result.get(0) instanceof Simple);
+ s.delete( result.get(0) );
+ t.commit();
+ s.close();
+ }
+
+ private String locateAppropriateDialectFunctionNameForAliasTest() {
+ for (Iterator itr = getDialect().getFunctions().entrySet().iterator(); itr.hasNext(); ) {
+ final Map.Entry entry = (Map.Entry) itr.next();
+ final SQLFunction function = (SQLFunction) entry.getValue();
+ if ( !function.hasArguments() && !function.hasParenthesesIfNoArguments() ) {
+ return (String) entry.getKey();
+ }
+ }
+ return null;
+ }
+
+ public void testCachedQueryOnInsert() throws Exception {
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ Simple simple = new Simple();
+ simple.setName("Simple 1");
+ s.save( simple, new Long(10) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Query q = s.createQuery("from Simple s");
+ List list = q.setCacheable(true).list();
+ assertTrue( list.size()==1 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ q = s.createQuery("from Simple s");
+ list = q.setCacheable(true).list();
+ assertTrue( list.size()==1 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ Simple simple2 = new Simple();
+ simple2.setCount(133);
+ s.save( simple2, new Long(12) );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ q = s.createQuery("from Simple s");
+ list = q.setCacheable(true).list();
+ assertTrue( list.size()==2 );
+ t.commit();
+ s.close();
+
+ s = openSession();
+ t = s.beginTransaction();
+ q = s.createQuery("from Simple s");
+ list = q.setCacheable(true).list();
+ assertTrue( list.size()==2 );
+ Iterator i = list.iterator();
+ while ( i.hasNext() ) s.delete( i.next() );
+ t.commit();
+ s.close();
+
+ }
+
+ public void testInterSystemsFunctions() throws Exception {
+ Calendar cal = new GregorianCalendar();
+ cal.set(1977,6,3,0,0,0);
+ java.sql.Timestamp testvalue = new java.sql.Timestamp(cal.getTimeInMillis());
+ testvalue.setNanos(0);
+ Calendar cal3 = new GregorianCalendar();
+ cal3.set(1976,2,3,0,0,0);
+ java.sql.Timestamp testvalue3 = new java.sql.Timestamp(cal3.getTimeInMillis());
+ testvalue3.setNanos(0);
+
+ Session s = openSession();
+ Transaction t = s.beginTransaction();
+ try {
+ Statement stmt = s.connection().createStatement();
+ stmt.executeUpdate("DROP FUNCTION spLock FROM TestInterSystemsFunctionsClass");
+ t.commit();
+ }
+ catch (Exception ex) {
+ System.out.println("as we expected stored procedure sp does not exist when we drop it");
+
+ }
+ t = s.beginTransaction();
+ Statement stmt = s.connection().createStatement();
+ String create_function = "CREATE FUNCTION SQLUser.TestInterSystemsFunctionsClass_spLock\n" +
+ " ( INOUT pHandle %SQLProcContext, \n" +
+ " ROWID INTEGER \n" +
+ " )\n" +
+ " FOR User.TestInterSystemsFunctionsClass " +
+ " PROCEDURE\n" +
+ " RETURNS INTEGER\n" +
+ " LANGUAGE OBJECTSCRIPT\n" +
+ " {\n" +
+ " q 0\n" +
+ " }";
+ stmt.executeUpdate(create_function);
+ t.commit();
+ t = s.beginTransaction();
+
+ TestInterSystemsFunctionsClass object = new TestInterSystemsFunctionsClass();
+ object.setDateText("1977-07-03");
+ object.setDate1(testvalue);
+ object.setDate3(testvalue3);
+ s.save( object, new Long(10));
+ t.commit();
+ s.close();
+ s = openSession();
+ s.clear();
+ t = s.beginTransaction();
+ TestInterSystemsFunctionsClass test = (TestInterSystemsFunctionsClass) s.get(TestInterSystemsFunctionsClass.class, new Long(10));
+ assertTrue( test.getDate1().equals(testvalue));
+ test = (TestInterSystemsFunctionsClass) s.get(TestInterSystemsFunctionsClass.class, new Long(10), LockMode.UPGRADE);
+ assertTrue( test.getDate1().equals(testvalue));
+ Date value = (Date) s.find("select nvl(o.date,o.dateText) from TestInterSystemsFunctionsClass as o" ).get(0);
+ assertTrue( value.equals(testvalue));
+ Object nv = s.find("select nullif(o.dateText,o.dateText) from TestInterSystemsFunctionsClass as o" ).get(0);
+ assertTrue( nv == null);
+ String dateText = (String) s.find("select nvl(o.dateText,o.date) from TestInterSystemsFunctionsClass as o" ).get(0);
+ assertTrue( dateText.equals("1977-07-03"));
+ value = (Date) s.find("select ifnull(o.date,o.date1) from TestInterSystemsFunctionsClass as o" ).get(0);
+ assertTrue( value.equals(testvalue));
+ value = (Date) s.find("select ifnull(o.date3,o.date,o.date1) from TestInterSystemsFunctionsClass as o" ).get(0);
+ assertTrue( value.equals(testvalue));
+ Integer pos = (Integer) s.find("select position('07', o.dateText) from TestInterSystemsFunctionsClass as o" ).get(0);
+ assertTrue(pos.intValue() == 6);
+ String st = (String) s.find("select convert(o.date1, SQL_TIME) from TestInterSystemsFunctionsClass as o" ).get(0);
+ assertTrue( st.equals("00:00:00"));
+ java.sql.Time tm = (java.sql.Time) s.find("select cast(o.date1, time) from TestInterSystemsFunctionsClass as o" ).get(0);
+ assertTrue( tm.toString().equals("00:00:00"));
+ Double diff = (Double)s.find("select timestampdiff(SQL_TSI_FRAC_SECOND, o.date3, o.date1) from TestInterSystemsFunctionsClass as o" ).get(0);
+ assertTrue(diff.doubleValue() != 0.0);
+ diff = (Double)s.find("select timestampdiff(SQL_TSI_MONTH, o.date3, o.date1) from TestInterSystemsFunctionsClass as o" ).get(0);
+ assertTrue(diff.doubleValue() == 16.0);
+ diff = (Double)s.find("select timestampdiff(SQL_TSI_WEEK, o.date3, o.date1) from TestInterSystemsFunctionsClass as o" ).get(0);
+ assertTrue(diff.doubleValue() >= 16*4);
+ diff = (Double)s.find("select timestampdiff(SQL_TSI_YEAR, o.date3, o.date1) from TestInterSystemsFunctionsClass as o" ).get(0);
+ assertTrue(diff.doubleValue() == 1.0);
+
+ t.commit();
+ s.close();
+
+
+ }
+
+}
Added: branches/Branch_3_2/Hibernate3/test/org/hibernate/test/dialect/cache/TestInterSystemsFunctionsClass.hbm.xml
===================================================================
--- branches/Branch_3_2/Hibernate3/test/org/hibernate/test/dialect/cache/TestInterSystemsFunctionsClass.hbm.xml 2006-11-08 22:02:28 UTC (rev 10777)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/dialect/cache/TestInterSystemsFunctionsClass.hbm.xml 2006-11-08 23:35:17 UTC (rev 10778)
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<hibernate-mapping package="org.hibernate.test.dialect.cache" >
+
+ <class name="TestInterSystemsFunctionsClass" table="SQLUser.TestInterSystemsFunctionsClass">
+ <id type="long" column="id_">
+ <generator class="assigned"/>
+ </id>
+ <property name="date" column="date_"/>
+ <property name="date1" column="date1_"/>
+ <property name="date3" column="date3_"/>
+ <property name="dateText" column="dateText_"/>
+ </class>
+
+</hibernate-mapping>
Added: branches/Branch_3_2/Hibernate3/test/org/hibernate/test/dialect/cache/TestInterSystemsFunctionsClass.java
===================================================================
--- branches/Branch_3_2/Hibernate3/test/org/hibernate/test/dialect/cache/TestInterSystemsFunctionsClass.java 2006-11-08 22:02:28 UTC (rev 10777)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/dialect/cache/TestInterSystemsFunctionsClass.java 2006-11-08 23:35:17 UTC (rev 10778)
@@ -0,0 +1,51 @@
+package org.hibernate.test.dialect.cache;
+
+import java.util.Date;
+
+/**
+ * Entity for testing function support of InterSystems' CacheSQL...
+ *
+ * @author Jonathan Levinson
+ */
+public class TestInterSystemsFunctionsClass {
+ private java.util.Date date3;
+ private java.util.Date date1;
+ private java.util.Date date;
+ private String dateText;
+
+ public Date getDate() {
+ return date;
+ }
+
+ public void setDate(Date date) {
+ this.date = date;
+ }
+
+
+ public String getDateText() {
+ return dateText;
+ }
+
+ public void setDateText(String dateText) {
+ this.dateText = dateText;
+ }
+
+
+ public Date getDate1() {
+ return date1;
+ }
+
+ public void setDate1(Date date1) {
+ this.date1 = date1;
+ }
+
+
+ public Date getDate3() {
+ return date3;
+ }
+
+ public void setDate3(Date date3) {
+ this.date3 = date3;
+ }
+
+}
18 years, 4 months
Hibernate SVN: r10777 - in trunk/Hibernate3: src/org/hibernate/type test/org/hibernate/test/ops
by hibernate-commits@lists.jboss.org
Author: steve.ebersole(a)jboss.com
Date: 2006-11-08 17:02:28 -0500 (Wed, 08 Nov 2006)
New Revision: 10777
Added:
trunk/Hibernate3/test/org/hibernate/test/ops/Address.java
trunk/Hibernate3/test/org/hibernate/test/ops/OneToOne.hbm.xml
trunk/Hibernate3/test/org/hibernate/test/ops/Person.java
trunk/Hibernate3/test/org/hibernate/test/ops/PersonalDetails.java
Modified:
trunk/Hibernate3/src/org/hibernate/type/EntityType.java
trunk/Hibernate3/test/org/hibernate/test/ops/AbstractOperationTestCase.java
trunk/Hibernate3/test/org/hibernate/test/ops/MergeTest.java
Log:
HHH-2138 : merge and bidirectional one-to-one
Modified: trunk/Hibernate3/src/org/hibernate/type/EntityType.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/type/EntityType.java 2006-11-08 22:02:07 UTC (rev 10776)
+++ trunk/Hibernate3/src/org/hibernate/type/EntityType.java 2006-11-08 22:02:28 UTC (rev 10777)
@@ -27,7 +27,8 @@
import org.hibernate.util.ReflectHelper;
/**
- * A reference to an entity class
+ * Base for types which map associations to persistent entities.
+ *
* @author Gavin King
*/
public abstract class EntityType extends AbstractType implements AssociationType {
@@ -37,38 +38,27 @@
protected final boolean isEmbeddedInXML;
private final boolean eager;
private final boolean unwrapProxy;
-
- public boolean isEmbeddedInXML() {
- return isEmbeddedInXML;
- }
-
- public final boolean isEntityType() {
- return true;
- }
-
- public String getPropertyName() {
- return null;
- }
- public final String getAssociatedEntityName() {
- return associatedEntityName;
- }
+ private transient Class returnedClass;
- public final boolean isSame(Object x, Object y, EntityMode entityMode) {
- return x==y;
- }
-
- public int compare(Object x, Object y, EntityMode entityMode) {
- return 0; //TODO: entities CAN be compared, by PK, fix this!
- }
-
+ /**
+ * Constructs the requested entity type mapping.
+ *
+ * @param entityName The name of the associated entity.
+ * @param uniqueKeyPropertyName The property-ref name, or null if we
+ * reference the PK of the associated entity.
+ * @param eager Is eager fetching enabled.
+ * @param isEmbeddedInXML Should values of this mapping be embedded in XML modes?
+ * @param unwrapProxy Is unwrapping of proxies allowed for this association; unwrapping
+ * says to return the "implementation target" of lazy prooxies; typically only possible
+ * with lazy="no-proxy".
+ */
protected EntityType(
- String entityName,
- String uniqueKeyPropertyName,
- boolean eager,
+ String entityName,
+ String uniqueKeyPropertyName,
+ boolean eager,
boolean isEmbeddedInXML,
- boolean unwrapProxy
- ) {
+ boolean unwrapProxy) {
this.associatedEntityName = entityName;
this.uniqueKeyPropertyName = uniqueKeyPropertyName;
this.isEmbeddedInXML = isEmbeddedInXML;
@@ -76,322 +66,209 @@
this.unwrapProxy = unwrapProxy;
}
- public Object nullSafeGet(ResultSet rs, String name, SessionImplementor session, Object owner)
- throws HibernateException, SQLException {
- return nullSafeGet( rs, new String[] {name}, session, owner );
+ /**
+ * An entity type is a type of association type
+ *
+ * @return True.
+ */
+ public boolean isAssociationType() {
+ return true;
}
/**
- * This returns the wrong class for an entity with a proxy, or for
- * a named entity. Theoretically it should return the proxy class,
- * but it doesn't.
+ * Explicitly, an entity type is an entity type ;)
+ *
+ * @return True.
*/
- public final Class getReturnedClass() {
- try {
- return ReflectHelper.classForName(associatedEntityName);
- }
- catch (ClassNotFoundException cnfe) {
- return java.util.Map.class;
- }
+ public final boolean isEntityType() {
+ return true;
}
- /*protected final Object getActualIdentifier(Object value, SessionImplementor session) throws HibernateException {
- return session.getEntityIdentifierIfNotUnsaved(value); //tolerates nulls
- }*/
-
- protected final Object getIdentifier(Object value, SessionImplementor session)
- throws HibernateException {
-
- if ( isNotEmbedded(session) ) return value;
-
- if ( isReferenceToPrimaryKey() ) {
- return ForeignKeys.getEntityIdentifierIfNotUnsaved(associatedEntityName, value, session); //tolerates nulls
- }
- else if (value==null) {
- return null;
- }
- else {
- return session.getFactory()
- .getEntityPersister( getAssociatedEntityName() )
- .getPropertyValue( value, uniqueKeyPropertyName, session.getEntityMode() );
- }
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isMutable() {
+ return false;
}
- protected boolean isNotEmbedded(SessionImplementor session) {
- return !isEmbeddedInXML && session.getEntityMode()==EntityMode.DOM4J;
+ /**
+ * Generates a string representation of this type.
+ *
+ * @return string rep
+ */
+ public String toString() {
+ return getClass().getName() + '(' + getAssociatedEntityName() + ')';
}
/**
- * Get the identifier value of an instance or proxy
+ * For entity types, the name correlates to the associated entity name.
*/
- private static Serializable getIdentifier(Object object, EntityPersister persister, EntityMode entityMode)
- throws HibernateException {
- if (object instanceof HibernateProxy) {
- HibernateProxy proxy = (HibernateProxy) object;
- LazyInitializer li = proxy.getHibernateLazyInitializer();
- return li.getIdentifier();
- }
- else {
- return persister.getIdentifier( object, entityMode );
- }
- }
-
- public String toLoggableString(Object value, SessionFactoryImplementor factory)
- throws HibernateException {
-
- if (value==null) return "null";
-
- EntityPersister persister = factory.getEntityPersister(associatedEntityName);
- StringBuffer result = new StringBuffer()
- .append(associatedEntityName);
-
- if ( persister.hasIdentifierProperty() ) {
- //TODO: use of a guess here is bad...
- final EntityMode entityMode = persister.guessEntityMode(value);
- final Serializable id;
- if (entityMode==null) {
- if ( isEmbeddedInXML ) {
- throw new ClassCastException( value.getClass().getName() );
- }
- id = (Serializable) value;
- }
- else {
- id = getIdentifier( value, persister, entityMode );
- }
-
- result.append('#')
- .append( persister.getIdentifierType().toLoggableString(id, factory) );
- }
-
- return result.toString();
+ public String getName() {
+ return associatedEntityName;
}
-
- /*public String toXMLString(Object value, SessionFactoryImplementor factory) throws HibernateException {
- if (isEmbeddedInXML) throw new UnsupportedOperationException("entity references cannot be stringified");
- if (factory==null) throw new AssertionFailure("null factory passed to toString");
- return getIdentifierType(factory).toXMLString(value, factory);
- }
- public Object fromXMLString(String xml, Mapping factory) throws HibernateException {
- if (isEmbeddedInXML) throw new UnsupportedOperationException("entity references cannot be stringified");
- if (factory==null) throw new AssertionFailure("null factory passed to fromString");
- return getIdentifierType(factory).fromXMLString(xml, factory);
- }*/
-
- public String getName() { return associatedEntityName; }
-
- public Object deepCopy(Object value, EntityMode entityMode, SessionFactoryImplementor factory) {
- return value; //special case ... this is the leaf of the containment graph, even though not immutable
+ /**
+ * Does this association foreign key reference the primary key of the other table?
+ * Otherwise, it references a property-ref.
+ *
+ * @return True if this association reference the PK of the associated entity.
+ */
+ public boolean isReferenceToPrimaryKey() {
+ return uniqueKeyPropertyName==null;
}
- public boolean isMutable() {
- return false;
+ public String getRHSUniqueKeyPropertyName() {
+ return uniqueKeyPropertyName;
}
- public abstract boolean isOneToOne();
-
- public Object replace(Object original, Object target, SessionImplementor session, Object owner, Map copyCache)
- throws HibernateException {
- if (original==null) return null;
- Object cached = copyCache.get(original);
- if (cached!=null) {
- return cached;
- }
- else {
- if (original==target) return target;
- //TODO: can this ever get called????
- Object id = getIdentifier(original, session);
- if (id==null) throw new AssertionFailure("cannot copy a reference to an object with a null id");
- id = getIdentifierOrUniqueKeyType( session.getFactory() )
- .replace(id, null, session, owner, copyCache);
- return resolve(id, session, owner);
- }
+ public String getLHSPropertyName() {
+ return null;
}
- public boolean isAssociationType() {
- return true;
+ public String getPropertyName() {
+ return null;
}
- public final Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner)
- throws HibernateException, SQLException {
- return resolve( hydrate(rs, names, session, owner), session, owner );
+ /**
+ * The name of the associated entity.
+ *
+ * @return The associated entity name.
+ */
+ public final String getAssociatedEntityName() {
+ return associatedEntityName;
}
- public Joinable getAssociatedJoinable(SessionFactoryImplementor factory)
- throws MappingException {
- return (Joinable) factory.getEntityPersister(associatedEntityName);
+ /**
+ * The name of the associated entity.
+ *
+ * @param factory The session factory, for resolution.
+ * @return The associated entity name.
+ */
+ public String getAssociatedEntityName(SessionFactoryImplementor factory) {
+ return getAssociatedEntityName();
}
-
- Type getIdentifierType(Mapping factory) {
- return factory.getIdentifierType( getAssociatedEntityName() );
- }
- Type getIdentifierType(SessionImplementor session) throws MappingException {
- return getIdentifierType( session.getFactory() );
+ /**
+ * Retrieves the {@link Joinable} defining the associated entity.
+ *
+ * @param factory The session factory.
+ * @return The associated joinable
+ * @throws MappingException Generally indicates an invalid entity name.
+ */
+ public Joinable getAssociatedJoinable(SessionFactoryImplementor factory) throws MappingException {
+ return ( Joinable ) factory.getEntityPersister( associatedEntityName );
}
- public final Type getIdentifierOrUniqueKeyType(Mapping factory)
- throws MappingException {
- if ( isReferenceToPrimaryKey() ) {
- return getIdentifierType(factory);
+ /**
+ * This returns the wrong class for an entity with a proxy, or for a named
+ * entity. Theoretically it should return the proxy class, but it doesn't.
+ * <p/>
+ * The problem here is that we do not necessarily have a ref to the associated
+ * entity persister (nor to the session factory, to look it up) which is really
+ * needed to "do the right thing" here...
+ *
+ * @return The entiyt class.
+ */
+ public final Class getReturnedClass() {
+ if ( returnedClass == null ) {
+ returnedClass = determineAssociatedEntityClass();
}
- else {
- return factory.getReferencedPropertyType( getAssociatedEntityName(), uniqueKeyPropertyName );
- }
+ return returnedClass;
}
- public final String getIdentifierOrUniqueKeyPropertyName(Mapping factory)
- throws MappingException {
- if ( isReferenceToPrimaryKey() ) {
- return factory.getIdentifierPropertyName( getAssociatedEntityName() );
+ private Class determineAssociatedEntityClass() {
+ try {
+ return ReflectHelper.classForName( getAssociatedEntityName() );
}
- else {
- return uniqueKeyPropertyName;
+ catch ( ClassNotFoundException cnfe ) {
+ return java.util.Map.class;
}
}
-
- protected abstract boolean isNullable();
/**
- * Resolve an identifier
+ * {@inheritDoc}
*/
- protected final Object resolveIdentifier(Serializable id, SessionImplementor session)
- throws HibernateException {
-
- boolean isProxyUnwrapEnabled = unwrapProxy &&
- session.getFactory()
- .getEntityPersister( getAssociatedEntityName() )
- .isInstrumented( session.getEntityMode() );
-
- Object proxyOrEntity = session.internalLoad(
- getAssociatedEntityName(),
- id,
- eager,
- isNullable() && !isProxyUnwrapEnabled
- );
-
- if (proxyOrEntity instanceof HibernateProxy) {
- ( (HibernateProxy) proxyOrEntity ).getHibernateLazyInitializer()
- .setUnwrap(isProxyUnwrapEnabled);
- }
-
- return proxyOrEntity;
+ public Object nullSafeGet(ResultSet rs, String name, SessionImplementor session, Object owner)
+ throws HibernateException, SQLException {
+ return nullSafeGet( rs, new String[] {name}, session, owner );
}
- protected boolean isNull(Object owner, SessionImplementor session) {
- return false;
- }
-
/**
- * Resolve an identifier or unique key value
+ * {@inheritDoc}
*/
- public Object resolve(Object value, SessionImplementor session, Object owner)
- throws HibernateException {
-
- if ( isNotEmbedded(session) ) {
- return value;
- }
-
- if (value==null) {
- return null;
- }
- else {
-
- if ( isNull(owner, session) ) return null; //EARLY EXIT!
-
- if ( isReferenceToPrimaryKey() ) {
- return resolveIdentifier( (Serializable) value, session );
- }
- else {
- return loadByUniqueKey(
- getAssociatedEntityName(),
- uniqueKeyPropertyName,
- value,
- session
- );
- }
- }
+ public final Object nullSafeGet(
+ ResultSet rs,
+ String[] names,
+ SessionImplementor session,
+ Object owner) throws HibernateException, SQLException {
+ return resolve( hydrate(rs, names, session, owner), session, owner );
}
- public String getAssociatedEntityName(SessionFactoryImplementor factory) {
- return getAssociatedEntityName();
+ /**
+ * Two entities are considered the same when their instances are the same.
+ *
+ * @param x One entity instance
+ * @param y Another entity instance
+ * @param entityMode The entity mode.
+ * @return True if x == y; false otherwise.
+ */
+ public final boolean isSame(Object x, Object y, EntityMode entityMode) {
+ return x == y;
}
/**
- * Does this association foreign key reference the
- * primary key of the other table?
+ * {@inheritDoc}
*/
- public boolean isReferenceToPrimaryKey() {
- return uniqueKeyPropertyName==null;
+ public int compare(Object x, Object y, EntityMode entityMode) {
+ return 0; //TODO: entities CAN be compared, by PK, fix this! -> only if/when we can extract the id values....
}
-
- public String getRHSUniqueKeyPropertyName() {
- return uniqueKeyPropertyName;
- }
- public String toString() {
- return getClass().getName() + '(' + getAssociatedEntityName() + ')';
+ /**
+ * {@inheritDoc}
+ */
+ public Object deepCopy(Object value, EntityMode entityMode, SessionFactoryImplementor factory) {
+ return value; //special case ... this is the leaf of the containment graph, even though not immutable
}
/**
- * Load an instance by a unique key that is not the primary key.
+ * {@inheritDoc}
*/
- public Object loadByUniqueKey(
- String entityName,
- String uniqueKeyPropertyName,
- Object key,
- SessionImplementor session)
- throws HibernateException {
-
- final SessionFactoryImplementor factory = session.getFactory();
-
- UniqueKeyLoadable persister = (UniqueKeyLoadable) factory.getEntityPersister(entityName);
-
- //TODO: implement caching?! proxies?!
-
- EntityUniqueKey euk = new EntityUniqueKey(
- entityName,
- uniqueKeyPropertyName,
- key,
- getIdentifierOrUniqueKeyType( factory ),
- session.getEntityMode(),
- session.getFactory()
- );
-
- final PersistenceContext persistenceContext = session.getPersistenceContext();
- Object result = persistenceContext.getEntity(euk);
- //if ( result==null && !persistenceContext.isNonExistant(euk) ) {
- if ( result==null ) {
- result = persister.loadByUniqueKey(uniqueKeyPropertyName, key, session);
+ public Object replace(
+ Object original,
+ Object target,
+ SessionImplementor session,
+ Object owner,
+ Map copyCache) throws HibernateException {
+ if ( original == null ) {
+ return null;
}
- return result==null ? null : persistenceContext.proxyFor(result);
-
- }
-
- public String getLHSPropertyName() {
- return null;
- }
-
- public String getOnCondition(String alias, SessionFactoryImplementor factory, Map enabledFilters)
- throws MappingException {
- if ( isReferenceToPrimaryKey() ) { //TODO: this is a bit arbitrary, expose a switch to the user?
- return "";
+ Object cached = copyCache.get(original);
+ if ( cached != null ) {
+ return cached;
}
else {
- return getAssociatedJoinable(factory).filterFragment(alias, enabledFilters);
+ if ( original == target ) {
+ return target;
+ }
+ Object id = getIdentifier( original, session );
+ if ( id == null ) {
+ throw new AssertionFailure("cannot copy a reference to an object with a null id");
+ }
+ id = getIdentifierOrUniqueKeyType( session.getFactory() )
+ .replace(id, null, session, owner, copyCache);
+ return resolve( id, session, owner );
}
}
-
- public Type getSemiResolvedType(SessionFactoryImplementor factory) {
- return factory.getEntityPersister(associatedEntityName).getIdentifierType();
- }
-
+
+ /**
+ * {@inheritDoc}
+ */
public int getHashCode(Object x, EntityMode entityMode, SessionFactoryImplementor factory) {
EntityPersister persister = factory.getEntityPersister(associatedEntityName);
if ( !persister.canExtractIdOutOfEntity() ) {
return super.getHashCode(x, entityMode);
}
-
+
final Serializable id;
if (x instanceof HibernateProxy) {
id = ( (HibernateProxy) x ).getHibernateLazyInitializer().getIdentifier();
@@ -401,13 +278,16 @@
}
return persister.getIdentifierType().getHashCode(id, entityMode, factory);
}
-
+
+ /**
+ * {@inheritDoc}
+ */
public boolean isEqual(Object x, Object y, EntityMode entityMode, SessionFactoryImplementor factory) {
EntityPersister persister = factory.getEntityPersister(associatedEntityName);
if ( !persister.canExtractIdOutOfEntity() ) {
return super.isEqual(x, y, entityMode);
}
-
+
Serializable xid;
if (x instanceof HibernateProxy) {
xid = ( (HibernateProxy) x ).getHibernateLazyInitializer()
@@ -416,7 +296,7 @@
else {
xid = persister.getIdentifier(x, entityMode);
}
-
+
Serializable yid;
if (y instanceof HibernateProxy) {
yid = ( (HibernateProxy) y ).getHibernateLazyInitializer()
@@ -425,15 +305,28 @@
else {
yid = persister.getIdentifier(y, entityMode);
}
-
+
return persister.getIdentifierType()
.isEqual(xid, yid, entityMode, factory);
}
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isEmbeddedInXML() {
+ return isEmbeddedInXML;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
public boolean isXMLElement() {
return isEmbeddedInXML;
}
+ /**
+ * {@inheritDoc}
+ */
public Object fromXMLNode(Node xml, Mapping factory) throws HibernateException {
if ( !isEmbeddedInXML ) {
return getIdentifierType(factory).fromXMLNode(xml, factory);
@@ -443,8 +336,10 @@
}
}
- public void setToXMLNode(Node node, Object value, SessionFactoryImplementor factory)
- throws HibernateException {
+ /**
+ * {@inheritDoc}
+ */
+ public void setToXMLNode(Node node, Object value, SessionFactoryImplementor factory) throws HibernateException {
if ( !isEmbeddedInXML ) {
getIdentifierType(factory).setToXMLNode(node, value, factory);
}
@@ -454,4 +349,265 @@
}
}
+ public String getOnCondition(String alias, SessionFactoryImplementor factory, Map enabledFilters)
+ throws MappingException {
+ if ( isReferenceToPrimaryKey() ) { //TODO: this is a bit arbitrary, expose a switch to the user?
+ return "";
+ }
+ else {
+ return getAssociatedJoinable( factory ).filterFragment( alias, enabledFilters );
+ }
+ }
+
+ /**
+ * Resolve an identifier or unique key value
+ */
+ public Object resolve(Object value, SessionImplementor session, Object owner) throws HibernateException {
+ if ( isNotEmbedded( session ) ) {
+ return value;
+ }
+
+ if ( value == null ) {
+ return null;
+ }
+ else {
+ if ( isNull( owner, session ) ) {
+ return null; //EARLY EXIT!
+ }
+
+ if ( isReferenceToPrimaryKey() ) {
+ return resolveIdentifier( (Serializable) value, session );
+ }
+ else {
+ return loadByUniqueKey( getAssociatedEntityName(), uniqueKeyPropertyName, value, session );
+ }
+ }
+ }
+
+ public Type getSemiResolvedType(SessionFactoryImplementor factory) {
+ return factory.getEntityPersister( associatedEntityName ).getIdentifierType();
+ }
+
+ protected final Object getIdentifier(Object value, SessionImplementor session) throws HibernateException {
+ if ( isNotEmbedded(session) ) {
+ return value;
+ }
+
+ if ( isReferenceToPrimaryKey() ) {
+ return ForeignKeys.getEntityIdentifierIfNotUnsaved( getAssociatedEntityName(), value, session ); //tolerates nulls
+ }
+ else if ( value == null ) {
+ return null;
+ }
+ else {
+ EntityPersister entityPersister = session.getFactory().getEntityPersister( getAssociatedEntityName() );
+ Object propertyValue = entityPersister.getPropertyValue( value, uniqueKeyPropertyName, session.getEntityMode() );
+ // We now have the value of the property-ref we reference. However,
+ // we need to dig a little deeper, as that property might also be
+ // an entity type, in which case we need to resolve its identitifier
+ Type type = entityPersister.getPropertyType( uniqueKeyPropertyName );
+ if ( type.isEntityType() ) {
+ propertyValue = ( ( EntityType ) type ).getIdentifier( propertyValue, session );
+ }
+
+ return propertyValue;
+ }
+ }
+
+ protected boolean isNotEmbedded(SessionImplementor session) {
+ return !isEmbeddedInXML && session.getEntityMode()==EntityMode.DOM4J;
+ }
+
+ /**
+ * Get the identifier value of an instance or proxy.
+ * <p/>
+ * Intended only for loggin purposes!!!
+ *
+ * @param object The object from which to extract the identifier.
+ * @param persister The entity persister
+ * @param entityMode The entity mode
+ * @return The extracted identifier.
+ */
+ private static Serializable getIdentifier(Object object, EntityPersister persister, EntityMode entityMode) {
+ if (object instanceof HibernateProxy) {
+ HibernateProxy proxy = (HibernateProxy) object;
+ LazyInitializer li = proxy.getHibernateLazyInitializer();
+ return li.getIdentifier();
+ }
+ else {
+ return persister.getIdentifier( object, entityMode );
+ }
+ }
+
+ /**
+ * Generate a loggable representation of an instance of the value mapped by this type.
+ *
+ * @param value The instance to be logged.
+ * @param factory The session factory.
+ * @return The loggable string.
+ * @throws HibernateException Generally some form of resolution problem.
+ */
+ public String toLoggableString(Object value, SessionFactoryImplementor factory) {
+ if ( value == null ) {
+ return "null";
+ }
+
+ EntityPersister persister = factory.getEntityPersister( associatedEntityName );
+ StringBuffer result = new StringBuffer().append( associatedEntityName );
+
+ if ( persister.hasIdentifierProperty() ) {
+ final EntityMode entityMode = persister.guessEntityMode( value );
+ final Serializable id;
+ if ( entityMode == null ) {
+ if ( isEmbeddedInXML ) {
+ throw new ClassCastException( value.getClass().getName() );
+ }
+ id = ( Serializable ) value;
+ }
+ else {
+ id = getIdentifier( value, persister, entityMode );
+ }
+
+ result.append( '#' )
+ .append( persister.getIdentifierType().toLoggableString( id, factory ) );
+ }
+
+ return result.toString();
+ }
+
+ public abstract boolean isOneToOne();
+
+ /**
+ * Convenience method to locate the identifier type of the associated entity.
+ *
+ * @param factory The mappings...
+ * @return The identifier type
+ */
+ Type getIdentifierType(Mapping factory) {
+ return factory.getIdentifierType( getAssociatedEntityName() );
+ }
+
+ /**
+ * Convenience method to locate the identifier type of the associated entity.
+ *
+ * @param session The originating session
+ * @return The identifier type
+ */
+ Type getIdentifierType(SessionImplementor session) {
+ return getIdentifierType( session.getFactory() );
+ }
+
+ /**
+ * Determine the type of either (1) the identifier if we reference the
+ * associated entity's PK or (2) the unique key to which we refer (i.e.
+ * the property-ref).
+ *
+ * @param factory The mappings...
+ * @return The appropriate type.
+ * @throws MappingException Generally, if unable to resolve the associated entity name
+ * or unique key property name.
+ */
+ public final Type getIdentifierOrUniqueKeyType(Mapping factory) throws MappingException {
+ if ( isReferenceToPrimaryKey() ) {
+ return getIdentifierType(factory);
+ }
+ else {
+ Type type = factory.getReferencedPropertyType( getAssociatedEntityName(), uniqueKeyPropertyName );
+ if ( type.isEntityType() ) {
+ type = ( ( EntityType ) type).getIdentifierOrUniqueKeyType( factory );
+ }
+ return type;
+ }
+ }
+
+ /**
+ * The name of the property on the associated entity to which our FK
+ * refers
+ *
+ * @param factory The mappings...
+ * @return The appropriate property name.
+ * @throws MappingException Generally, if unable to resolve the associated entity name
+ */
+ public final String getIdentifierOrUniqueKeyPropertyName(Mapping factory)
+ throws MappingException {
+ if ( isReferenceToPrimaryKey() ) {
+ return factory.getIdentifierPropertyName( getAssociatedEntityName() );
+ }
+ else {
+ return uniqueKeyPropertyName;
+ }
+ }
+
+ protected abstract boolean isNullable();
+
+ /**
+ * Resolve an identifier via a load.
+ *
+ * @param id The entity id to resolve
+ * @param session The orginating session.
+ * @return The resolved identifier (i.e., loaded entity).
+ * @throws org.hibernate.HibernateException Indicates problems performing the load.
+ */
+ protected final Object resolveIdentifier(Serializable id, SessionImplementor session) throws HibernateException {
+ boolean isProxyUnwrapEnabled = unwrapProxy &&
+ session.getFactory()
+ .getEntityPersister( getAssociatedEntityName() )
+ .isInstrumented( session.getEntityMode() );
+
+ Object proxyOrEntity = session.internalLoad(
+ getAssociatedEntityName(),
+ id,
+ eager,
+ isNullable() && !isProxyUnwrapEnabled
+ );
+
+ if ( proxyOrEntity instanceof HibernateProxy ) {
+ ( ( HibernateProxy ) proxyOrEntity ).getHibernateLazyInitializer()
+ .setUnwrap( isProxyUnwrapEnabled );
+ }
+
+ return proxyOrEntity;
+ }
+
+ protected boolean isNull(Object owner, SessionImplementor session) {
+ return false;
+ }
+
+ /**
+ * Load an instance by a unique key that is not the primary key.
+ *
+ * @param entityName The name of the entity to load
+ * @param uniqueKeyPropertyName The name of the property defining the uniqie key.
+ * @param key The unique key property value.
+ * @param session The originating session.
+ * @return The loaded entity
+ * @throws HibernateException generally indicates problems performing the load.
+ */
+ public Object loadByUniqueKey(
+ String entityName,
+ String uniqueKeyPropertyName,
+ Object key,
+ SessionImplementor session) throws HibernateException {
+ final SessionFactoryImplementor factory = session.getFactory();
+ UniqueKeyLoadable persister = ( UniqueKeyLoadable ) factory.getEntityPersister( entityName );
+
+ //TODO: implement caching?! proxies?!
+
+ EntityUniqueKey euk = new EntityUniqueKey(
+ entityName,
+ uniqueKeyPropertyName,
+ key,
+ getIdentifierOrUniqueKeyType( factory ),
+ session.getEntityMode(),
+ session.getFactory()
+ );
+
+ final PersistenceContext persistenceContext = session.getPersistenceContext();
+ Object result = persistenceContext.getEntity( euk );
+ if ( result == null ) {
+ result = persister.loadByUniqueKey( uniqueKeyPropertyName, key, session );
+ }
+ return result == null ? null : persistenceContext.proxyFor( result );
+ }
+
}
Modified: trunk/Hibernate3/test/org/hibernate/test/ops/AbstractOperationTestCase.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/ops/AbstractOperationTestCase.java 2006-11-08 22:02:07 UTC (rev 10776)
+++ trunk/Hibernate3/test/org/hibernate/test/ops/AbstractOperationTestCase.java 2006-11-08 22:02:28 UTC (rev 10777)
@@ -1,11 +1,8 @@
package org.hibernate.test.ops;
-import java.util.Iterator;
-
import org.hibernate.test.TestCase;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
-import org.hibernate.Session;
/**
* {@inheritDoc}
@@ -23,7 +20,7 @@
}
protected String[] getMappings() {
- return new String[] { "ops/Node.hbm.xml", "ops/Employer.hbm.xml", "ops/OptLockEntity.hbm.xml" };
+ return new String[] { "ops/Node.hbm.xml", "ops/Employer.hbm.xml", "ops/OptLockEntity.hbm.xml", "ops/OneToOne.hbm.xml" };
}
public String getCacheConcurrencyStrategy() {
Added: trunk/Hibernate3/test/org/hibernate/test/ops/Address.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/ops/Address.java 2006-11-08 22:02:07 UTC (rev 10776)
+++ trunk/Hibernate3/test/org/hibernate/test/ops/Address.java 2006-11-08 22:02:28 UTC (rev 10777)
@@ -0,0 +1,65 @@
+package org.hibernate.test.ops;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class Address {
+ private Long id;
+ private String streetAddress;
+ private String city;
+ private String country;
+ private Person resident;
+
+ public Address() {
+ }
+
+ public Address(String streetAddress, String city, String country, Person resident) {
+ this.streetAddress = streetAddress;
+ this.city = city;
+ this.country = country;
+ this.resident = resident;
+ resident.setAddress( this );
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getStreetAddress() {
+ return streetAddress;
+ }
+
+ public void setStreetAddress(String streetAddress) {
+ this.streetAddress = streetAddress;
+ }
+
+ public String getCity() {
+ return city;
+ }
+
+ public void setCity(String city) {
+ this.city = city;
+ }
+
+ public String getCountry() {
+ return country;
+ }
+
+ public void setCountry(String country) {
+ this.country = country;
+ }
+
+ public Person getResident() {
+ return resident;
+ }
+
+ public void setResident(Person resident) {
+ this.resident = resident;
+ }
+}
Modified: trunk/Hibernate3/test/org/hibernate/test/ops/MergeTest.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/ops/MergeTest.java 2006-11-08 22:02:07 UTC (rev 10776)
+++ trunk/Hibernate3/test/org/hibernate/test/ops/MergeTest.java 2006-11-08 22:02:28 UTC (rev 10777)
@@ -26,6 +26,66 @@
return new TestSuite( MergeTest.class );
}
+ public void testMergeBidiPrimayKeyOneToOne() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ Person p = new Person( "steve" );
+ new PersonalDetails( "I have big feet", p );
+ s.persist( p );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ p.getDetails().setSomePersonalDetail( p.getDetails().getSomePersonalDetail() + " and big hands too" );
+ s = openSession();
+ s.beginTransaction();
+ p = ( Person ) s.merge( p );
+ s.getTransaction().commit();
+ s.close();
+
+ assertInsertCount( 0 );
+ assertUpdateCount( 1 );
+ assertDeleteCount( 0 );
+
+ s = openSession();
+ s.beginTransaction();
+ s.delete( p );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testMergeBidiForeignKeyOneToOne() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ Person p = new Person( "steve" );
+ Address a = new Address( "123 Main", "Austin", "US", p );
+ s.persist( a );
+ s.persist( p );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ p.getAddress().setStreetAddress( "321 Main" );
+ s = openSession();
+ s.beginTransaction();
+ p = ( Person ) s.merge( p );
+ s.getTransaction().commit();
+ s.close();
+
+ assertInsertCount( 0 );
+ assertUpdateCount( 0 ); // no cascade
+ assertDeleteCount( 0 );
+
+ s = openSession();
+ s.beginTransaction();
+ s.delete( a );
+ s.delete( p );
+ s.getTransaction().commit();
+ s.close();
+ }
+
public void testNoExtraUpdatesOnMerge() throws Exception {
Session s = openSession();
s.beginTransaction();
Added: trunk/Hibernate3/test/org/hibernate/test/ops/OneToOne.hbm.xml
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/ops/OneToOne.hbm.xml 2006-11-08 22:02:07 UTC (rev 10776)
+++ trunk/Hibernate3/test/org/hibernate/test/ops/OneToOne.hbm.xml 2006-11-08 22:02:28 UTC (rev 10777)
@@ -0,0 +1,43 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+ Mappings demonstrating bidirectional one-to-one mappings for testing
+ with various operations.
+
+ Person -> Address is modeled as a bidirectional one to one based on FK.
+ Person -> Details is modeled as a bidirectional one to one based on PK.
+-->
+
+<hibernate-mapping package="org.hibernate.test.ops">
+
+ <class name="Person" table="OPS_PERSON">
+ <id name="id" column="ID" type="long">
+ <generator class="increment"/>
+ </id>
+ <property name="name" column="NAME" type="string"/>
+ <one-to-one name="address" class="Address" property-ref="resident" />
+ <one-to-one name="details" class="PersonalDetails" cascade="all" />
+ </class>
+
+ <class name="Address" table="OPS_ADDRESS">
+ <id name="id" column="ID" type="long">
+ <generator class="increment"/>
+ </id>
+ <property name="streetAddress" column="STREET" type="string" />
+ <property name="city" column="CITY" type="string" />
+ <property name="country" column="CTRY" type="string" />
+ <many-to-one name="resident" column="RESIDENT" class="Person" />
+ </class>
+
+ <class name="PersonalDetails" table="OPS_PERS_DETAIL">
+ <id name="id" column="ID" type="long">
+ <generator class="increment"/>
+ </id>
+ <property name="somePersonalDetail" column="SOME_DETAIL" type="string"/>
+ <one-to-one name="person" class="Person" cascade="none" constrained="true" />
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: trunk/Hibernate3/test/org/hibernate/test/ops/Person.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/ops/Person.java 2006-11-08 22:02:07 UTC (rev 10776)
+++ trunk/Hibernate3/test/org/hibernate/test/ops/Person.java 2006-11-08 22:02:28 UTC (rev 10777)
@@ -0,0 +1,52 @@
+package org.hibernate.test.ops;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class Person {
+ private Long id;
+ private String name;
+ private Address address;
+ private PersonalDetails details;
+
+ public Person() {
+ }
+
+ public Person(String name) {
+ this.name = name;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Address getAddress() {
+ return address;
+ }
+
+ public void setAddress(Address address) {
+ this.address = address;
+ }
+
+ public PersonalDetails getDetails() {
+ return details;
+ }
+
+ public void setDetails(PersonalDetails details) {
+ this.details = details;
+ }
+}
Added: trunk/Hibernate3/test/org/hibernate/test/ops/PersonalDetails.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/ops/PersonalDetails.java 2006-11-08 22:02:07 UTC (rev 10776)
+++ trunk/Hibernate3/test/org/hibernate/test/ops/PersonalDetails.java 2006-11-08 22:02:28 UTC (rev 10777)
@@ -0,0 +1,45 @@
+package org.hibernate.test.ops;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class PersonalDetails {
+ private Long id;
+ private String somePersonalDetail;
+ private Person person;
+
+ public PersonalDetails() {
+ }
+
+ public PersonalDetails(String somePersonalDetail, Person person) {
+ this.somePersonalDetail = somePersonalDetail;
+ this.person = person;
+ person.setDetails( this );
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getSomePersonalDetail() {
+ return somePersonalDetail;
+ }
+
+ public void setSomePersonalDetail(String somePersonalDetail) {
+ this.somePersonalDetail = somePersonalDetail;
+ }
+
+ public Person getPerson() {
+ return person;
+ }
+
+ public void setPerson(Person person) {
+ this.person = person;
+ }
+}
18 years, 4 months
Hibernate SVN: r10776 - in branches/Branch_3_2/Hibernate3: src/org/hibernate/type test/org/hibernate/test/ops
by hibernate-commits@lists.jboss.org
Author: steve.ebersole(a)jboss.com
Date: 2006-11-08 17:02:07 -0500 (Wed, 08 Nov 2006)
New Revision: 10776
Added:
branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/Address.java
branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/OneToOne.hbm.xml
branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/Person.java
branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/PersonalDetails.java
Modified:
branches/Branch_3_2/Hibernate3/src/org/hibernate/type/EntityType.java
branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/AbstractOperationTestCase.java
branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/MergeTest.java
Log:
HHH-2138 : merge and bidirectional one-to-one
Modified: branches/Branch_3_2/Hibernate3/src/org/hibernate/type/EntityType.java
===================================================================
--- branches/Branch_3_2/Hibernate3/src/org/hibernate/type/EntityType.java 2006-11-08 16:56:52 UTC (rev 10775)
+++ branches/Branch_3_2/Hibernate3/src/org/hibernate/type/EntityType.java 2006-11-08 22:02:07 UTC (rev 10776)
@@ -27,7 +27,8 @@
import org.hibernate.util.ReflectHelper;
/**
- * A reference to an entity class
+ * Base for types which map associations to persistent entities.
+ *
* @author Gavin King
*/
public abstract class EntityType extends AbstractType implements AssociationType {
@@ -37,38 +38,27 @@
protected final boolean isEmbeddedInXML;
private final boolean eager;
private final boolean unwrapProxy;
-
- public boolean isEmbeddedInXML() {
- return isEmbeddedInXML;
- }
-
- public final boolean isEntityType() {
- return true;
- }
-
- public String getPropertyName() {
- return null;
- }
- public final String getAssociatedEntityName() {
- return associatedEntityName;
- }
+ private transient Class returnedClass;
- public final boolean isSame(Object x, Object y, EntityMode entityMode) {
- return x==y;
- }
-
- public int compare(Object x, Object y, EntityMode entityMode) {
- return 0; //TODO: entities CAN be compared, by PK, fix this!
- }
-
+ /**
+ * Constructs the requested entity type mapping.
+ *
+ * @param entityName The name of the associated entity.
+ * @param uniqueKeyPropertyName The property-ref name, or null if we
+ * reference the PK of the associated entity.
+ * @param eager Is eager fetching enabled.
+ * @param isEmbeddedInXML Should values of this mapping be embedded in XML modes?
+ * @param unwrapProxy Is unwrapping of proxies allowed for this association; unwrapping
+ * says to return the "implementation target" of lazy prooxies; typically only possible
+ * with lazy="no-proxy".
+ */
protected EntityType(
- String entityName,
- String uniqueKeyPropertyName,
- boolean eager,
+ String entityName,
+ String uniqueKeyPropertyName,
+ boolean eager,
boolean isEmbeddedInXML,
- boolean unwrapProxy
- ) {
+ boolean unwrapProxy) {
this.associatedEntityName = entityName;
this.uniqueKeyPropertyName = uniqueKeyPropertyName;
this.isEmbeddedInXML = isEmbeddedInXML;
@@ -76,322 +66,209 @@
this.unwrapProxy = unwrapProxy;
}
- public Object nullSafeGet(ResultSet rs, String name, SessionImplementor session, Object owner)
- throws HibernateException, SQLException {
- return nullSafeGet( rs, new String[] {name}, session, owner );
+ /**
+ * An entity type is a type of association type
+ *
+ * @return True.
+ */
+ public boolean isAssociationType() {
+ return true;
}
/**
- * This returns the wrong class for an entity with a proxy, or for
- * a named entity. Theoretically it should return the proxy class,
- * but it doesn't.
+ * Explicitly, an entity type is an entity type ;)
+ *
+ * @return True.
*/
- public final Class getReturnedClass() {
- try {
- return ReflectHelper.classForName(associatedEntityName);
- }
- catch (ClassNotFoundException cnfe) {
- return java.util.Map.class;
- }
+ public final boolean isEntityType() {
+ return true;
}
- /*protected final Object getActualIdentifier(Object value, SessionImplementor session) throws HibernateException {
- return session.getEntityIdentifierIfNotUnsaved(value); //tolerates nulls
- }*/
-
- protected final Object getIdentifier(Object value, SessionImplementor session)
- throws HibernateException {
-
- if ( isNotEmbedded(session) ) return value;
-
- if ( isReferenceToPrimaryKey() ) {
- return ForeignKeys.getEntityIdentifierIfNotUnsaved(associatedEntityName, value, session); //tolerates nulls
- }
- else if (value==null) {
- return null;
- }
- else {
- return session.getFactory()
- .getEntityPersister( getAssociatedEntityName() )
- .getPropertyValue( value, uniqueKeyPropertyName, session.getEntityMode() );
- }
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isMutable() {
+ return false;
}
- protected boolean isNotEmbedded(SessionImplementor session) {
- return !isEmbeddedInXML && session.getEntityMode()==EntityMode.DOM4J;
+ /**
+ * Generates a string representation of this type.
+ *
+ * @return string rep
+ */
+ public String toString() {
+ return getClass().getName() + '(' + getAssociatedEntityName() + ')';
}
/**
- * Get the identifier value of an instance or proxy
+ * For entity types, the name correlates to the associated entity name.
*/
- private static Serializable getIdentifier(Object object, EntityPersister persister, EntityMode entityMode)
- throws HibernateException {
- if (object instanceof HibernateProxy) {
- HibernateProxy proxy = (HibernateProxy) object;
- LazyInitializer li = proxy.getHibernateLazyInitializer();
- return li.getIdentifier();
- }
- else {
- return persister.getIdentifier( object, entityMode );
- }
- }
-
- public String toLoggableString(Object value, SessionFactoryImplementor factory)
- throws HibernateException {
-
- if (value==null) return "null";
-
- EntityPersister persister = factory.getEntityPersister(associatedEntityName);
- StringBuffer result = new StringBuffer()
- .append(associatedEntityName);
-
- if ( persister.hasIdentifierProperty() ) {
- //TODO: use of a guess here is bad...
- final EntityMode entityMode = persister.guessEntityMode(value);
- final Serializable id;
- if (entityMode==null) {
- if ( isEmbeddedInXML ) {
- throw new ClassCastException( value.getClass().getName() );
- }
- id = (Serializable) value;
- }
- else {
- id = getIdentifier( value, persister, entityMode );
- }
-
- result.append('#')
- .append( persister.getIdentifierType().toLoggableString(id, factory) );
- }
-
- return result.toString();
+ public String getName() {
+ return associatedEntityName;
}
-
- /*public String toXMLString(Object value, SessionFactoryImplementor factory) throws HibernateException {
- if (isEmbeddedInXML) throw new UnsupportedOperationException("entity references cannot be stringified");
- if (factory==null) throw new AssertionFailure("null factory passed to toString");
- return getIdentifierType(factory).toXMLString(value, factory);
- }
- public Object fromXMLString(String xml, Mapping factory) throws HibernateException {
- if (isEmbeddedInXML) throw new UnsupportedOperationException("entity references cannot be stringified");
- if (factory==null) throw new AssertionFailure("null factory passed to fromString");
- return getIdentifierType(factory).fromXMLString(xml, factory);
- }*/
-
- public String getName() { return associatedEntityName; }
-
- public Object deepCopy(Object value, EntityMode entityMode, SessionFactoryImplementor factory) {
- return value; //special case ... this is the leaf of the containment graph, even though not immutable
+ /**
+ * Does this association foreign key reference the primary key of the other table?
+ * Otherwise, it references a property-ref.
+ *
+ * @return True if this association reference the PK of the associated entity.
+ */
+ public boolean isReferenceToPrimaryKey() {
+ return uniqueKeyPropertyName==null;
}
- public boolean isMutable() {
- return false;
+ public String getRHSUniqueKeyPropertyName() {
+ return uniqueKeyPropertyName;
}
- public abstract boolean isOneToOne();
-
- public Object replace(Object original, Object target, SessionImplementor session, Object owner, Map copyCache)
- throws HibernateException {
- if (original==null) return null;
- Object cached = copyCache.get(original);
- if (cached!=null) {
- return cached;
- }
- else {
- if (original==target) return target;
- //TODO: can this ever get called????
- Object id = getIdentifier(original, session);
- if (id==null) throw new AssertionFailure("cannot copy a reference to an object with a null id");
- id = getIdentifierOrUniqueKeyType( session.getFactory() )
- .replace(id, null, session, owner, copyCache);
- return resolve(id, session, owner);
- }
+ public String getLHSPropertyName() {
+ return null;
}
- public boolean isAssociationType() {
- return true;
+ public String getPropertyName() {
+ return null;
}
- public final Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner)
- throws HibernateException, SQLException {
- return resolve( hydrate(rs, names, session, owner), session, owner );
+ /**
+ * The name of the associated entity.
+ *
+ * @return The associated entity name.
+ */
+ public final String getAssociatedEntityName() {
+ return associatedEntityName;
}
- public Joinable getAssociatedJoinable(SessionFactoryImplementor factory)
- throws MappingException {
- return (Joinable) factory.getEntityPersister(associatedEntityName);
+ /**
+ * The name of the associated entity.
+ *
+ * @param factory The session factory, for resolution.
+ * @return The associated entity name.
+ */
+ public String getAssociatedEntityName(SessionFactoryImplementor factory) {
+ return getAssociatedEntityName();
}
-
- Type getIdentifierType(Mapping factory) {
- return factory.getIdentifierType( getAssociatedEntityName() );
- }
- Type getIdentifierType(SessionImplementor session) throws MappingException {
- return getIdentifierType( session.getFactory() );
+ /**
+ * Retrieves the {@link Joinable} defining the associated entity.
+ *
+ * @param factory The session factory.
+ * @return The associated joinable
+ * @throws MappingException Generally indicates an invalid entity name.
+ */
+ public Joinable getAssociatedJoinable(SessionFactoryImplementor factory) throws MappingException {
+ return ( Joinable ) factory.getEntityPersister( associatedEntityName );
}
- public final Type getIdentifierOrUniqueKeyType(Mapping factory)
- throws MappingException {
- if ( isReferenceToPrimaryKey() ) {
- return getIdentifierType(factory);
+ /**
+ * This returns the wrong class for an entity with a proxy, or for a named
+ * entity. Theoretically it should return the proxy class, but it doesn't.
+ * <p/>
+ * The problem here is that we do not necessarily have a ref to the associated
+ * entity persister (nor to the session factory, to look it up) which is really
+ * needed to "do the right thing" here...
+ *
+ * @return The entiyt class.
+ */
+ public final Class getReturnedClass() {
+ if ( returnedClass == null ) {
+ returnedClass = determineAssociatedEntityClass();
}
- else {
- return factory.getReferencedPropertyType( getAssociatedEntityName(), uniqueKeyPropertyName );
- }
+ return returnedClass;
}
- public final String getIdentifierOrUniqueKeyPropertyName(Mapping factory)
- throws MappingException {
- if ( isReferenceToPrimaryKey() ) {
- return factory.getIdentifierPropertyName( getAssociatedEntityName() );
+ private Class determineAssociatedEntityClass() {
+ try {
+ return ReflectHelper.classForName( getAssociatedEntityName() );
}
- else {
- return uniqueKeyPropertyName;
+ catch ( ClassNotFoundException cnfe ) {
+ return java.util.Map.class;
}
}
-
- protected abstract boolean isNullable();
/**
- * Resolve an identifier
+ * {@inheritDoc}
*/
- protected final Object resolveIdentifier(Serializable id, SessionImplementor session)
- throws HibernateException {
-
- boolean isProxyUnwrapEnabled = unwrapProxy &&
- session.getFactory()
- .getEntityPersister( getAssociatedEntityName() )
- .isInstrumented( session.getEntityMode() );
-
- Object proxyOrEntity = session.internalLoad(
- getAssociatedEntityName(),
- id,
- eager,
- isNullable() && !isProxyUnwrapEnabled
- );
-
- if (proxyOrEntity instanceof HibernateProxy) {
- ( (HibernateProxy) proxyOrEntity ).getHibernateLazyInitializer()
- .setUnwrap(isProxyUnwrapEnabled);
- }
-
- return proxyOrEntity;
+ public Object nullSafeGet(ResultSet rs, String name, SessionImplementor session, Object owner)
+ throws HibernateException, SQLException {
+ return nullSafeGet( rs, new String[] {name}, session, owner );
}
- protected boolean isNull(Object owner, SessionImplementor session) {
- return false;
- }
-
/**
- * Resolve an identifier or unique key value
+ * {@inheritDoc}
*/
- public Object resolve(Object value, SessionImplementor session, Object owner)
- throws HibernateException {
-
- if ( isNotEmbedded(session) ) {
- return value;
- }
-
- if (value==null) {
- return null;
- }
- else {
-
- if ( isNull(owner, session) ) return null; //EARLY EXIT!
-
- if ( isReferenceToPrimaryKey() ) {
- return resolveIdentifier( (Serializable) value, session );
- }
- else {
- return loadByUniqueKey(
- getAssociatedEntityName(),
- uniqueKeyPropertyName,
- value,
- session
- );
- }
- }
+ public final Object nullSafeGet(
+ ResultSet rs,
+ String[] names,
+ SessionImplementor session,
+ Object owner) throws HibernateException, SQLException {
+ return resolve( hydrate(rs, names, session, owner), session, owner );
}
- public String getAssociatedEntityName(SessionFactoryImplementor factory) {
- return getAssociatedEntityName();
+ /**
+ * Two entities are considered the same when their instances are the same.
+ *
+ * @param x One entity instance
+ * @param y Another entity instance
+ * @param entityMode The entity mode.
+ * @return True if x == y; false otherwise.
+ */
+ public final boolean isSame(Object x, Object y, EntityMode entityMode) {
+ return x == y;
}
/**
- * Does this association foreign key reference the
- * primary key of the other table?
+ * {@inheritDoc}
*/
- public boolean isReferenceToPrimaryKey() {
- return uniqueKeyPropertyName==null;
+ public int compare(Object x, Object y, EntityMode entityMode) {
+ return 0; //TODO: entities CAN be compared, by PK, fix this! -> only if/when we can extract the id values....
}
-
- public String getRHSUniqueKeyPropertyName() {
- return uniqueKeyPropertyName;
- }
- public String toString() {
- return getClass().getName() + '(' + getAssociatedEntityName() + ')';
+ /**
+ * {@inheritDoc}
+ */
+ public Object deepCopy(Object value, EntityMode entityMode, SessionFactoryImplementor factory) {
+ return value; //special case ... this is the leaf of the containment graph, even though not immutable
}
/**
- * Load an instance by a unique key that is not the primary key.
+ * {@inheritDoc}
*/
- public Object loadByUniqueKey(
- String entityName,
- String uniqueKeyPropertyName,
- Object key,
- SessionImplementor session)
- throws HibernateException {
-
- final SessionFactoryImplementor factory = session.getFactory();
-
- UniqueKeyLoadable persister = (UniqueKeyLoadable) factory.getEntityPersister(entityName);
-
- //TODO: implement caching?! proxies?!
-
- EntityUniqueKey euk = new EntityUniqueKey(
- entityName,
- uniqueKeyPropertyName,
- key,
- getIdentifierOrUniqueKeyType( factory ),
- session.getEntityMode(),
- session.getFactory()
- );
-
- final PersistenceContext persistenceContext = session.getPersistenceContext();
- Object result = persistenceContext.getEntity(euk);
- //if ( result==null && !persistenceContext.isNonExistant(euk) ) {
- if ( result==null ) {
- result = persister.loadByUniqueKey(uniqueKeyPropertyName, key, session);
+ public Object replace(
+ Object original,
+ Object target,
+ SessionImplementor session,
+ Object owner,
+ Map copyCache) throws HibernateException {
+ if ( original == null ) {
+ return null;
}
- return result==null ? null : persistenceContext.proxyFor(result);
-
- }
-
- public String getLHSPropertyName() {
- return null;
- }
-
- public String getOnCondition(String alias, SessionFactoryImplementor factory, Map enabledFilters)
- throws MappingException {
- if ( isReferenceToPrimaryKey() ) { //TODO: this is a bit arbitrary, expose a switch to the user?
- return "";
+ Object cached = copyCache.get(original);
+ if ( cached != null ) {
+ return cached;
}
else {
- return getAssociatedJoinable(factory).filterFragment(alias, enabledFilters);
+ if ( original == target ) {
+ return target;
+ }
+ Object id = getIdentifier( original, session );
+ if ( id == null ) {
+ throw new AssertionFailure("cannot copy a reference to an object with a null id");
+ }
+ id = getIdentifierOrUniqueKeyType( session.getFactory() )
+ .replace(id, null, session, owner, copyCache);
+ return resolve( id, session, owner );
}
}
-
- public Type getSemiResolvedType(SessionFactoryImplementor factory) {
- return factory.getEntityPersister(associatedEntityName).getIdentifierType();
- }
-
+
+ /**
+ * {@inheritDoc}
+ */
public int getHashCode(Object x, EntityMode entityMode, SessionFactoryImplementor factory) {
EntityPersister persister = factory.getEntityPersister(associatedEntityName);
if ( !persister.canExtractIdOutOfEntity() ) {
return super.getHashCode(x, entityMode);
}
-
+
final Serializable id;
if (x instanceof HibernateProxy) {
id = ( (HibernateProxy) x ).getHibernateLazyInitializer().getIdentifier();
@@ -401,13 +278,16 @@
}
return persister.getIdentifierType().getHashCode(id, entityMode, factory);
}
-
+
+ /**
+ * {@inheritDoc}
+ */
public boolean isEqual(Object x, Object y, EntityMode entityMode, SessionFactoryImplementor factory) {
EntityPersister persister = factory.getEntityPersister(associatedEntityName);
if ( !persister.canExtractIdOutOfEntity() ) {
return super.isEqual(x, y, entityMode);
}
-
+
Serializable xid;
if (x instanceof HibernateProxy) {
xid = ( (HibernateProxy) x ).getHibernateLazyInitializer()
@@ -416,7 +296,7 @@
else {
xid = persister.getIdentifier(x, entityMode);
}
-
+
Serializable yid;
if (y instanceof HibernateProxy) {
yid = ( (HibernateProxy) y ).getHibernateLazyInitializer()
@@ -425,15 +305,28 @@
else {
yid = persister.getIdentifier(y, entityMode);
}
-
+
return persister.getIdentifierType()
.isEqual(xid, yid, entityMode, factory);
}
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isEmbeddedInXML() {
+ return isEmbeddedInXML;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
public boolean isXMLElement() {
return isEmbeddedInXML;
}
+ /**
+ * {@inheritDoc}
+ */
public Object fromXMLNode(Node xml, Mapping factory) throws HibernateException {
if ( !isEmbeddedInXML ) {
return getIdentifierType(factory).fromXMLNode(xml, factory);
@@ -443,8 +336,10 @@
}
}
- public void setToXMLNode(Node node, Object value, SessionFactoryImplementor factory)
- throws HibernateException {
+ /**
+ * {@inheritDoc}
+ */
+ public void setToXMLNode(Node node, Object value, SessionFactoryImplementor factory) throws HibernateException {
if ( !isEmbeddedInXML ) {
getIdentifierType(factory).setToXMLNode(node, value, factory);
}
@@ -454,4 +349,265 @@
}
}
+ public String getOnCondition(String alias, SessionFactoryImplementor factory, Map enabledFilters)
+ throws MappingException {
+ if ( isReferenceToPrimaryKey() ) { //TODO: this is a bit arbitrary, expose a switch to the user?
+ return "";
+ }
+ else {
+ return getAssociatedJoinable( factory ).filterFragment( alias, enabledFilters );
+ }
+ }
+
+ /**
+ * Resolve an identifier or unique key value
+ */
+ public Object resolve(Object value, SessionImplementor session, Object owner) throws HibernateException {
+ if ( isNotEmbedded( session ) ) {
+ return value;
+ }
+
+ if ( value == null ) {
+ return null;
+ }
+ else {
+ if ( isNull( owner, session ) ) {
+ return null; //EARLY EXIT!
+ }
+
+ if ( isReferenceToPrimaryKey() ) {
+ return resolveIdentifier( (Serializable) value, session );
+ }
+ else {
+ return loadByUniqueKey( getAssociatedEntityName(), uniqueKeyPropertyName, value, session );
+ }
+ }
+ }
+
+ public Type getSemiResolvedType(SessionFactoryImplementor factory) {
+ return factory.getEntityPersister( associatedEntityName ).getIdentifierType();
+ }
+
+ protected final Object getIdentifier(Object value, SessionImplementor session) throws HibernateException {
+ if ( isNotEmbedded(session) ) {
+ return value;
+ }
+
+ if ( isReferenceToPrimaryKey() ) {
+ return ForeignKeys.getEntityIdentifierIfNotUnsaved( getAssociatedEntityName(), value, session ); //tolerates nulls
+ }
+ else if ( value == null ) {
+ return null;
+ }
+ else {
+ EntityPersister entityPersister = session.getFactory().getEntityPersister( getAssociatedEntityName() );
+ Object propertyValue = entityPersister.getPropertyValue( value, uniqueKeyPropertyName, session.getEntityMode() );
+ // We now have the value of the property-ref we reference. However,
+ // we need to dig a little deeper, as that property might also be
+ // an entity type, in which case we need to resolve its identitifier
+ Type type = entityPersister.getPropertyType( uniqueKeyPropertyName );
+ if ( type.isEntityType() ) {
+ propertyValue = ( ( EntityType ) type ).getIdentifier( propertyValue, session );
+ }
+
+ return propertyValue;
+ }
+ }
+
+ protected boolean isNotEmbedded(SessionImplementor session) {
+ return !isEmbeddedInXML && session.getEntityMode()==EntityMode.DOM4J;
+ }
+
+ /**
+ * Get the identifier value of an instance or proxy.
+ * <p/>
+ * Intended only for loggin purposes!!!
+ *
+ * @param object The object from which to extract the identifier.
+ * @param persister The entity persister
+ * @param entityMode The entity mode
+ * @return The extracted identifier.
+ */
+ private static Serializable getIdentifier(Object object, EntityPersister persister, EntityMode entityMode) {
+ if (object instanceof HibernateProxy) {
+ HibernateProxy proxy = (HibernateProxy) object;
+ LazyInitializer li = proxy.getHibernateLazyInitializer();
+ return li.getIdentifier();
+ }
+ else {
+ return persister.getIdentifier( object, entityMode );
+ }
+ }
+
+ /**
+ * Generate a loggable representation of an instance of the value mapped by this type.
+ *
+ * @param value The instance to be logged.
+ * @param factory The session factory.
+ * @return The loggable string.
+ * @throws HibernateException Generally some form of resolution problem.
+ */
+ public String toLoggableString(Object value, SessionFactoryImplementor factory) {
+ if ( value == null ) {
+ return "null";
+ }
+
+ EntityPersister persister = factory.getEntityPersister( associatedEntityName );
+ StringBuffer result = new StringBuffer().append( associatedEntityName );
+
+ if ( persister.hasIdentifierProperty() ) {
+ final EntityMode entityMode = persister.guessEntityMode( value );
+ final Serializable id;
+ if ( entityMode == null ) {
+ if ( isEmbeddedInXML ) {
+ throw new ClassCastException( value.getClass().getName() );
+ }
+ id = ( Serializable ) value;
+ }
+ else {
+ id = getIdentifier( value, persister, entityMode );
+ }
+
+ result.append( '#' )
+ .append( persister.getIdentifierType().toLoggableString( id, factory ) );
+ }
+
+ return result.toString();
+ }
+
+ public abstract boolean isOneToOne();
+
+ /**
+ * Convenience method to locate the identifier type of the associated entity.
+ *
+ * @param factory The mappings...
+ * @return The identifier type
+ */
+ Type getIdentifierType(Mapping factory) {
+ return factory.getIdentifierType( getAssociatedEntityName() );
+ }
+
+ /**
+ * Convenience method to locate the identifier type of the associated entity.
+ *
+ * @param session The originating session
+ * @return The identifier type
+ */
+ Type getIdentifierType(SessionImplementor session) {
+ return getIdentifierType( session.getFactory() );
+ }
+
+ /**
+ * Determine the type of either (1) the identifier if we reference the
+ * associated entity's PK or (2) the unique key to which we refer (i.e.
+ * the property-ref).
+ *
+ * @param factory The mappings...
+ * @return The appropriate type.
+ * @throws MappingException Generally, if unable to resolve the associated entity name
+ * or unique key property name.
+ */
+ public final Type getIdentifierOrUniqueKeyType(Mapping factory) throws MappingException {
+ if ( isReferenceToPrimaryKey() ) {
+ return getIdentifierType(factory);
+ }
+ else {
+ Type type = factory.getReferencedPropertyType( getAssociatedEntityName(), uniqueKeyPropertyName );
+ if ( type.isEntityType() ) {
+ type = ( ( EntityType ) type).getIdentifierOrUniqueKeyType( factory );
+ }
+ return type;
+ }
+ }
+
+ /**
+ * The name of the property on the associated entity to which our FK
+ * refers
+ *
+ * @param factory The mappings...
+ * @return The appropriate property name.
+ * @throws MappingException Generally, if unable to resolve the associated entity name
+ */
+ public final String getIdentifierOrUniqueKeyPropertyName(Mapping factory)
+ throws MappingException {
+ if ( isReferenceToPrimaryKey() ) {
+ return factory.getIdentifierPropertyName( getAssociatedEntityName() );
+ }
+ else {
+ return uniqueKeyPropertyName;
+ }
+ }
+
+ protected abstract boolean isNullable();
+
+ /**
+ * Resolve an identifier via a load.
+ *
+ * @param id The entity id to resolve
+ * @param session The orginating session.
+ * @return The resolved identifier (i.e., loaded entity).
+ * @throws org.hibernate.HibernateException Indicates problems performing the load.
+ */
+ protected final Object resolveIdentifier(Serializable id, SessionImplementor session) throws HibernateException {
+ boolean isProxyUnwrapEnabled = unwrapProxy &&
+ session.getFactory()
+ .getEntityPersister( getAssociatedEntityName() )
+ .isInstrumented( session.getEntityMode() );
+
+ Object proxyOrEntity = session.internalLoad(
+ getAssociatedEntityName(),
+ id,
+ eager,
+ isNullable() && !isProxyUnwrapEnabled
+ );
+
+ if ( proxyOrEntity instanceof HibernateProxy ) {
+ ( ( HibernateProxy ) proxyOrEntity ).getHibernateLazyInitializer()
+ .setUnwrap( isProxyUnwrapEnabled );
+ }
+
+ return proxyOrEntity;
+ }
+
+ protected boolean isNull(Object owner, SessionImplementor session) {
+ return false;
+ }
+
+ /**
+ * Load an instance by a unique key that is not the primary key.
+ *
+ * @param entityName The name of the entity to load
+ * @param uniqueKeyPropertyName The name of the property defining the uniqie key.
+ * @param key The unique key property value.
+ * @param session The originating session.
+ * @return The loaded entity
+ * @throws HibernateException generally indicates problems performing the load.
+ */
+ public Object loadByUniqueKey(
+ String entityName,
+ String uniqueKeyPropertyName,
+ Object key,
+ SessionImplementor session) throws HibernateException {
+ final SessionFactoryImplementor factory = session.getFactory();
+ UniqueKeyLoadable persister = ( UniqueKeyLoadable ) factory.getEntityPersister( entityName );
+
+ //TODO: implement caching?! proxies?!
+
+ EntityUniqueKey euk = new EntityUniqueKey(
+ entityName,
+ uniqueKeyPropertyName,
+ key,
+ getIdentifierOrUniqueKeyType( factory ),
+ session.getEntityMode(),
+ session.getFactory()
+ );
+
+ final PersistenceContext persistenceContext = session.getPersistenceContext();
+ Object result = persistenceContext.getEntity( euk );
+ if ( result == null ) {
+ result = persister.loadByUniqueKey( uniqueKeyPropertyName, key, session );
+ }
+ return result == null ? null : persistenceContext.proxyFor( result );
+ }
+
}
Modified: branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/AbstractOperationTestCase.java
===================================================================
--- branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/AbstractOperationTestCase.java 2006-11-08 16:56:52 UTC (rev 10775)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/AbstractOperationTestCase.java 2006-11-08 22:02:07 UTC (rev 10776)
@@ -1,11 +1,8 @@
package org.hibernate.test.ops;
-import java.util.Iterator;
-
import org.hibernate.test.TestCase;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
-import org.hibernate.Session;
/**
* {@inheritDoc}
@@ -23,7 +20,7 @@
}
protected String[] getMappings() {
- return new String[] { "ops/Node.hbm.xml", "ops/Employer.hbm.xml", "ops/OptLockEntity.hbm.xml" };
+ return new String[] { "ops/Node.hbm.xml", "ops/Employer.hbm.xml", "ops/OptLockEntity.hbm.xml", "ops/OneToOne.hbm.xml" };
}
public String getCacheConcurrencyStrategy() {
Added: branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/Address.java
===================================================================
--- branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/Address.java 2006-11-08 16:56:52 UTC (rev 10775)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/Address.java 2006-11-08 22:02:07 UTC (rev 10776)
@@ -0,0 +1,65 @@
+package org.hibernate.test.ops;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class Address {
+ private Long id;
+ private String streetAddress;
+ private String city;
+ private String country;
+ private Person resident;
+
+ public Address() {
+ }
+
+ public Address(String streetAddress, String city, String country, Person resident) {
+ this.streetAddress = streetAddress;
+ this.city = city;
+ this.country = country;
+ this.resident = resident;
+ resident.setAddress( this );
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getStreetAddress() {
+ return streetAddress;
+ }
+
+ public void setStreetAddress(String streetAddress) {
+ this.streetAddress = streetAddress;
+ }
+
+ public String getCity() {
+ return city;
+ }
+
+ public void setCity(String city) {
+ this.city = city;
+ }
+
+ public String getCountry() {
+ return country;
+ }
+
+ public void setCountry(String country) {
+ this.country = country;
+ }
+
+ public Person getResident() {
+ return resident;
+ }
+
+ public void setResident(Person resident) {
+ this.resident = resident;
+ }
+}
Modified: branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/MergeTest.java
===================================================================
--- branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/MergeTest.java 2006-11-08 16:56:52 UTC (rev 10775)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/MergeTest.java 2006-11-08 22:02:07 UTC (rev 10776)
@@ -26,9 +26,69 @@
return new TestSuite( MergeTest.class );
}
- public void testNoExtraUpdatesOnMerge() throws Exception {
+ public void testMergeBidiPrimayKeyOneToOne() throws Exception {
Session s = openSession();
s.beginTransaction();
+ Person p = new Person( "steve" );
+ new PersonalDetails( "I have big feet", p );
+ s.persist( p );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ p.getDetails().setSomePersonalDetail( p.getDetails().getSomePersonalDetail() + " and big hands too" );
+ s = openSession();
+ s.beginTransaction();
+ p = ( Person ) s.merge( p );
+ s.getTransaction().commit();
+ s.close();
+
+ assertInsertCount( 0 );
+ assertUpdateCount( 1 );
+ assertDeleteCount( 0 );
+
+ s = openSession();
+ s.beginTransaction();
+ s.delete( p );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testMergeBidiForeignKeyOneToOne() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
+ Person p = new Person( "steve" );
+ Address a = new Address( "123 Main", "Austin", "US", p );
+ s.persist( a );
+ s.persist( p );
+ s.getTransaction().commit();
+ s.close();
+
+ clearCounts();
+
+ p.getAddress().setStreetAddress( "321 Main" );
+ s = openSession();
+ s.beginTransaction();
+ p = ( Person ) s.merge( p );
+ s.getTransaction().commit();
+ s.close();
+
+ assertInsertCount( 0 );
+ assertUpdateCount( 0 ); // no cascade
+ assertDeleteCount( 0 );
+
+ s = openSession();
+ s.beginTransaction();
+ s.delete( a );
+ s.delete( p );
+ s.getTransaction().commit();
+ s.close();
+ }
+
+ public void testNoExtraUpdatesOnMerge() throws Exception {
+ Session s = openSession();
+ s.beginTransaction();
Node node = new Node( "test" );
s.persist( node );
s.getTransaction().commit();
Added: branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/OneToOne.hbm.xml
===================================================================
--- branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/OneToOne.hbm.xml 2006-11-08 16:56:52 UTC (rev 10775)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/OneToOne.hbm.xml 2006-11-08 22:02:07 UTC (rev 10776)
@@ -0,0 +1,43 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+ "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+ "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
+
+<!--
+ Mappings demonstrating bidirectional one-to-one mappings for testing
+ with various operations.
+
+ Person -> Address is modeled as a bidirectional one to one based on FK.
+ Person -> Details is modeled as a bidirectional one to one based on PK.
+-->
+
+<hibernate-mapping package="org.hibernate.test.ops">
+
+ <class name="Person" table="OPS_PERSON">
+ <id name="id" column="ID" type="long">
+ <generator class="increment"/>
+ </id>
+ <property name="name" column="NAME" type="string"/>
+ <one-to-one name="address" class="Address" property-ref="resident" />
+ <one-to-one name="details" class="PersonalDetails" cascade="all" />
+ </class>
+
+ <class name="Address" table="OPS_ADDRESS">
+ <id name="id" column="ID" type="long">
+ <generator class="increment"/>
+ </id>
+ <property name="streetAddress" column="STREET" type="string" />
+ <property name="city" column="CITY" type="string" />
+ <property name="country" column="CTRY" type="string" />
+ <many-to-one name="resident" column="RESIDENT" class="Person" />
+ </class>
+
+ <class name="PersonalDetails" table="OPS_PERS_DETAIL">
+ <id name="id" column="ID" type="long">
+ <generator class="increment"/>
+ </id>
+ <property name="somePersonalDetail" column="SOME_DETAIL" type="string"/>
+ <one-to-one name="person" class="Person" cascade="none" constrained="true" />
+ </class>
+
+</hibernate-mapping>
\ No newline at end of file
Added: branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/Person.java
===================================================================
--- branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/Person.java 2006-11-08 16:56:52 UTC (rev 10775)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/Person.java 2006-11-08 22:02:07 UTC (rev 10776)
@@ -0,0 +1,52 @@
+package org.hibernate.test.ops;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class Person {
+ private Long id;
+ private String name;
+ private Address address;
+ private PersonalDetails details;
+
+ public Person() {
+ }
+
+ public Person(String name) {
+ this.name = name;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Address getAddress() {
+ return address;
+ }
+
+ public void setAddress(Address address) {
+ this.address = address;
+ }
+
+ public PersonalDetails getDetails() {
+ return details;
+ }
+
+ public void setDetails(PersonalDetails details) {
+ this.details = details;
+ }
+}
Added: branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/PersonalDetails.java
===================================================================
--- branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/PersonalDetails.java 2006-11-08 16:56:52 UTC (rev 10775)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/ops/PersonalDetails.java 2006-11-08 22:02:07 UTC (rev 10776)
@@ -0,0 +1,45 @@
+package org.hibernate.test.ops;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author Steve Ebersole
+ */
+public class PersonalDetails {
+ private Long id;
+ private String somePersonalDetail;
+ private Person person;
+
+ public PersonalDetails() {
+ }
+
+ public PersonalDetails(String somePersonalDetail, Person person) {
+ this.somePersonalDetail = somePersonalDetail;
+ this.person = person;
+ person.setDetails( this );
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getSomePersonalDetail() {
+ return somePersonalDetail;
+ }
+
+ public void setSomePersonalDetail(String somePersonalDetail) {
+ this.somePersonalDetail = somePersonalDetail;
+ }
+
+ public Person getPerson() {
+ return person;
+ }
+
+ public void setPerson(Person person) {
+ this.person = person;
+ }
+}
18 years, 4 months
Hibernate SVN: r10775 - in branches/Branch_3_2/Hibernate3: src/org/hibernate/dialect src/org/hibernate/dialect/function src/org/hibernate/exception src/org/hibernate/sql test/org/hibernate/test test/org/hibernate/test/hql test/org/hibernate/test/legacy
by hibernate-commits@lists.jboss.org
Author: steve.ebersole(a)jboss.com
Date: 2006-11-08 11:56:52 -0500 (Wed, 08 Nov 2006)
New Revision: 10775
Added:
branches/Branch_3_2/Hibernate3/src/org/hibernate/dialect/Cache71Dialect.java
branches/Branch_3_2/Hibernate3/src/org/hibernate/dialect/function/ConditionalParenthesisFunction.java
branches/Branch_3_2/Hibernate3/src/org/hibernate/dialect/function/ConvertFunction.java
branches/Branch_3_2/Hibernate3/src/org/hibernate/dialect/function/StandardJDBCEscapeFunction.java
branches/Branch_3_2/Hibernate3/src/org/hibernate/exception/CacheSQLStateConverter.java
branches/Branch_3_2/Hibernate3/src/org/hibernate/sql/CacheJoinFragment.java
Modified:
branches/Branch_3_2/Hibernate3/src/org/hibernate/dialect/Dialect.java
branches/Branch_3_2/Hibernate3/src/org/hibernate/dialect/function/SQLFunction.java
branches/Branch_3_2/Hibernate3/src/org/hibernate/dialect/function/StandardSQLFunction.java
branches/Branch_3_2/Hibernate3/test/org/hibernate/test/TestCase.java
branches/Branch_3_2/Hibernate3/test/org/hibernate/test/hql/FooBarCopy.hbm.xml
branches/Branch_3_2/Hibernate3/test/org/hibernate/test/legacy/Baz.hbm.xml
branches/Branch_3_2/Hibernate3/test/org/hibernate/test/legacy/FooBar.hbm.xml
branches/Branch_3_2/Hibernate3/test/org/hibernate/test/legacy/Glarch.hbm.xml
Log:
HHH-2205 : dialect for InterSystems' CacheSQL
Added: branches/Branch_3_2/Hibernate3/src/org/hibernate/dialect/Cache71Dialect.java
===================================================================
--- branches/Branch_3_2/Hibernate3/src/org/hibernate/dialect/Cache71Dialect.java 2006-11-08 16:54:55 UTC (rev 10774)
+++ branches/Branch_3_2/Hibernate3/src/org/hibernate/dialect/Cache71Dialect.java 2006-11-08 16:56:52 UTC (rev 10775)
@@ -0,0 +1,844 @@
+//$Id: $
+package org.hibernate.dialect;
+
+import java.sql.CallableStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+
+import org.hibernate.Hibernate;
+import org.hibernate.LockMode;
+import org.hibernate.MappingException;
+import org.hibernate.cfg.Environment;
+import org.hibernate.dialect.function.NoArgSQLFunction;
+import org.hibernate.dialect.function.NvlFunction;
+import org.hibernate.dialect.function.SQLFunctionTemplate;
+import org.hibernate.dialect.function.StandardSQLFunction;
+import org.hibernate.dialect.function.VarArgsSQLFunction;
+import org.hibernate.dialect.function.StandardJDBCEscapeFunction;
+import org.hibernate.dialect.function.ConvertFunction;
+import org.hibernate.dialect.function.ConditionalParenthesisFunction;
+import org.hibernate.dialect.lock.LockingStrategy;
+import org.hibernate.dialect.lock.SelectLockingStrategy;
+import org.hibernate.dialect.lock.UpdateLockingStrategy;
+import org.hibernate.exception.CacheSQLStateConverter;
+import org.hibernate.exception.SQLExceptionConverter;
+import org.hibernate.exception.TemplatedViolatedConstraintNameExtracter;
+import org.hibernate.exception.ViolatedConstraintNameExtracter;
+import org.hibernate.id.IdentityGenerator;
+import org.hibernate.persister.entity.Lockable;
+import org.hibernate.sql.CacheJoinFragment;
+import org.hibernate.sql.JoinFragment;
+import org.hibernate.util.StringHelper;
+
+/**
+ * Caché 2007.1 dialect. This class is required in order to use Hibernate with Intersystems Cach� SQL.<br>
+ * <br>
+ * Compatible with Cach� 2007.1.
+ * Cache JDBC driver version ?.?.
+ * <br>
+ * <head>
+ * <title>Caché and Hibernate</title>
+ * </head>
+ * <body>
+ * <h1>Caché and Hibernate</h1>
+ * <h2>PREREQUISITES</h2>
+ * These setup instructions assume that both Caché and Hibernate are installed and operational.
+ * <br>
+ * <h2>HIBERNATE DIRECTORIES AND FILES</h2>
+ * InterSystems support for Hibernate 2.1.8 and Hibernate 3.0.5
+ * requires different dialect files from those distributed with Hibernate 3.2.
+ * Also Hibernate 2.1.8 has a different directory and Java package structure which is reflected in the InterSystems sources.
+ * JBoss distributes the InterSystems Cache' dialect for Hibernate 3.2.
+ * For earlier versions of Hibernate please contact
+ * <a href="http://www.intersystems.com/support/cache-support.html">InterSystems Worldwide Response Center</A> (WRC)
+ * for the appropriate source files.
+ * <br>
+ * <h2>CACHÉ DOCUMENTATION</h2>
+ * Documentation for Caché is available online when Caché is running.
+ * It can also be obtained from the
+ * <a href="http://www.intersystems.com/cache/downloads/documentation.html">InterSystems</A> website.
+ * The book, "Object-oriented Application Development Using the Caché Post-relational Database:
+ * is also available from Springer-Verlag.
+ * <br>
+ * <h2>HIBERNATE DOCUMENTATION</h2>
+ * Hibernate comes with extensive electronic documentation.
+ * In addition, several books on Hibernate are available from
+ * <a href="http://www.manning.com">Manning Publications Co</a>.
+ * Three available titles are "Hibernate Quickly", "Hibernate in Action", and "Java Persistence with Hibernate".
+ * <br>
+ * <h2>TO SET UP HIBERNATE FOR USE WITH CACHÉ</h2>
+ * The following steps assume that the directory where Caché was installed is C:\CacheSys.
+ * This is the default installation directory for Caché.
+ * The default installation directory for Hibernate is assumed to be C:\Hibernate.
+ * <p/>
+ * If either product is installed in a different location, the pathnames that follow should be modified appropriately.
+ * <p/>
+ * Caché version 2007.1 and above is recommended for use with
+ * Hibernate. The next step depends on the location of your
+ * CacheDB.jar depending on your version of Caché.
+ * <ol>
+ * <li>Copy C:\CacheSys\dev\java\lib\CacheDB.jar to C:\Hibernate\lib\CacheDB.jar.</li>
+ * <li>Copy C:\CacheSys\dev\java\lib\JDK15\CacheDB.jar to C:\Hibernate\lib\CacheDB.jar.</li>
+ * <p/>
+ * <li>Insert the following files into your Java classpath:
+ * <p/>
+ * <ul>
+ * <li>All jar files in the directory C:\Hibernate\lib</li>
+ * <li>The directory (or directories) where hibernate.properties and/or hibernate.cfg.xml are kept.</li>
+ * </ul>
+ * </li>
+ * <p/>
+ * <li>In the file, hibernate.properties (or hibernate.cfg.xml),
+ * specify the Caché dialect and the Caché version URL settings.</li>
+ * </ol>
+ * <p/>
+ * For example, in Hibernate 3.2, typical entries in hibernate.properties would have the following
+ * "name=value" pairs:
+ * <p/>
+ * <table cols=3 border cellpadding=5 cellspacing=0>
+ * <tr>
+ * <th>Property Name</th>
+ * <th>Property Value 3.2</th>
+ * <th>Property Value 2.1.8</th>
+ * </tr>
+ * <tr>
+ * <td>hibernate.dialect</td>
+ * <td>org.hibernate.dialect.Cache71Dialect</td>
+ * <td>net.sf.hibernate.dialect.Cache50Dialect</td>
+ * </tr>
+ * <tr>
+ * <td>hibernate.connection.driver_class</td>
+ * <td>com.intersys.jdbc.CacheDriver</td>
+ * <td>com.intersys.jdbc.CacheDriver</td>
+ * </tr>
+ * <tr>
+ * <td>hibernate.connection.username</td>
+ * <td>(see note 1)</td>
+ * <td>(see note 1)</td>
+ * </tr>
+ * <tr>
+ * <td>hibernate.connection.password</td>
+ * <td>(see note 1)</td>
+ * <td>(see note 1)</td>
+ * </tr>
+ * <tr>
+ * <td>hibernate.connection.url</td>
+ * <td>jdbc:Cache://127.0.0.1:1972/USER</td>
+ * <td>jdbc:Cache://127.0.0.1:1972/USER</td>
+ * </tr>
+ * </table>
+ * <p/>
+ * <dl>
+ * <dt><b>Note 1</b></dt>
+ * <dd>Please contact your administrator for the userid and password you should use when attempting access via JDBC.
+ * By default, these are chosen to be "_SYSTEM" and "SYS" respectively as noted in the SQL standard.</dd>
+ * </dl>
+ * <br>
+ * <h2>CACHÉ VERSION URL</h2>
+ * This is the standard URL for the JDBC driver.
+ * For a JDBC driver on the machine hosting Caché, use the IP "loopback" address, 127.0.0.1.
+ * For 1972, the default port, specify the super server port of your Caché instance.
+ * For USER, substitute the NAMESPACE which contains your Caché database data.
+ * <br>
+ * <h2>CACHÉ DIALECTS</h2>
+ * Choices for Dialect are:
+ * <br>
+ * <p/>
+ * <ol>
+ * <li>
+ * org.hibernate.dialect.Cache50Dialect (requires Caché
+ * 5.0 or above)
+ * </li>
+ * <p/>
+ * <li>org.hibernate.dialect.Cache51Dialect (requires Caché 5.1 or
+ * above)</li>
+ * <p/>
+ * <li>org.hibernate.dialect.Cache51SequenceDialect (requires Caché 5.1 or
+ * above)</li>
+ * <p/>
+ * <li>org.hibernate.dialect.Cache71Dialect (requires Caché
+ * 2007.1 or above)</li>
+ * <p/>
+ * <li>org.hibernate.dialect.Cache71SequenceDialect (requires Caché
+ * 2007.1 or above)</li>
+ * </ol>
+ * <p/>
+ * Setting up for Caché 5.0 is similar except that the hibernate
+ * dialect package is "org.hibernate.dialect.Cache50Dialect"
+ * <br>
+ * <h2>SETTING UP CACHÉ DIALECT FROM INTERSYSTEMS SOURCES</h2>
+ * <p/>
+ * InterSystems provides source code for the Hibernate Dialect classes.
+ * Therefore, you must first place the source code in the proper locations
+ * and compile it into Java class files.
+ * Doing this will eliminate possible version mismatches in the compiled Java code.
+ * <p/>
+ * To begin, unzip the InterSystems source into the PARENT directory of the location where you installed Hibernate.
+ * The InterSystems zip file that contains InterSystems Hibernate support for
+ * Hibernate 2.1.8 and Hibernate 3.0.5 already contains hibernate-2.1
+ * and hibernate-3.0 in its directory structure. This is why you must
+ * unzip to the directory CONTAINING your Hibernate install directory.
+ * <p/>
+ * If you are using Hibernate 3.2, JBoss has already built the right JAR file for you.
+ * If you are NOT using Hibernate 3.2, then you must do the following:
+ * <p/>
+ * <ol>
+ * <li>In your Hibernate install directory, after the unzip we described above,
+ * type the following:
+ * <br>
+ * <pre>
+ * build
+ * </pre></li>
+ * <li>Copy the JAR file to the proper place.
+ * <p/>
+ * For Hibernate 3.0.5, copy
+ * <pre>
+ * ..\hibernate\hibernate3.jar
+ * </pre>
+ * to the Hibernate install directory.
+ * <p/>
+ * For Hibernate 2.1.8, copy
+ * <pre>
+ * ..\hibernate\hibernate.2jar
+ * </pre>
+ * to the Hibernate install directory.</li>
+ * <p/>
+ * <li>Test your installation by configuring etc\hibernate.properties for your Caché database,
+ * and then running the following:
+ * <pre>
+ * build eg
+ * </pre>
+ * The build process reports its success or failure. If you see,
+ * <pre>
+ * BUILD FAILED
+ * </pre>
+ * please contact the
+ * <a href="http://www.intersystems.com/support/cache-support.html">InterSystems Worldwide Response Center</A>
+ * for assistance.
+ * </li>
+ * </ol>
+ * <br>
+ * <h2>SUPPORT FOR IDENTITY COLUMNS</h2>
+ * Caché 5.1 or later supports identity columns. This includes Cache' 2007.1. To cause
+ * Hibernate to use identity columns, specify "native" as the
+ * generator. Also make sure that
+ * hibernate.jdbc.use_get_generated_keys is set to true, which is the
+ * default. In the Cache' 2007.1 release, with the
+ * Cache71Dialect, you can set hibernate.jdbc.use_get_generated_keys to
+ * false, and still use identity columns.
+ * <br>
+ * <h2>SEQUENCE DIALECTS SUPPORT SEQUENCES</h2>
+ * You do not have to use the sequence dialects with Caché.
+ * These are Cache50SequenceDialect, Cache51SequenceDialect and Cache71SequenceDialect.
+ * But if you choose to use them, set them up as follows:
+ * <p/>
+ * To use Hibernate sequence support with Caché in a namespace, you must FIRST load the following file into that namespace:
+ * <pre>
+ * src\org\hibernate\dialect\CacheSequences.xml
+ * </pre>
+ * In your Hibernate mapping you can specify sequence use.
+ * When you are using a Cache' sequence dialect, the type "native" maps to sequence.
+ * <p/>
+ * For example, the following shows the use of a sequence generator in a Hibernate mapping:
+ * <pre>
+ * <id name="id" column="uid" type="long" unsaved-value="null">
+ * <generator class="seqhilo">
+ * <param name="sequence">EVENTS_SEQ</param>
+ * <param name="max_lo">0</param>
+ * </generator>
+ * </id>
+ * </pre>
+ * <br>
+ * <p/>
+ * Some versions of Hibernate under some circumstances call
+ * getSelectSequenceNextValString() in the dialect. If this happens
+ * you will receive the error message: new MappingException( "Dialect
+ * does not support sequences" ).
+ * <h2>BIGINT SUPPORT</h2>
+ * Caché 5.1 and above supports BIGINT. 2007.1 supports BIGINT.
+ * <p/>
+ * Caché 5.0.x does not have direct BIGINT support.
+ * To imitate BIGINT support in Cache 5.0.x, in the SQL configuration,
+ * remap %INTEGER as follows, to be used by Caché 5.0.x dialects:
+ * <p/>
+ * <pre>
+ * %Library.Integer(MAXVAL=99999999999999999999,MINVAL=-999999999999999999)
+ * </pre>
+ * <p/>
+ * To change SQL settings:
+ * <p/>
+ * <ol>
+ * <li>In Caché 2007.1, use the System Management Portal.</li>
+ * <p/>
+ * <li>In Caché 5.0, use the Configuration Manager.</li>
+ * </ol>
+ * <p/>
+ * Set Caché SQL to allow:
+ * <p/>
+ * <ol>
+ * <li>delimited identifiers</li>
+ * <li>drop of non-existent tables</li>
+ * <li>drop of non-existent constraints</li>
+ * </ol>
+ * <p/>
+ * <h2>HIBERNATE 2.1.8</h2>
+ * Hibernate 2.1.8 requires different source files from InterSystems reflecting
+ * the different directory and Java package structure of Hibernate 2.1.8.
+ * Please contact
+ * <a href="http://www.intersystems.com/support/cache-support.html">InterSystems Worldwide Response Center</A> (WRC)
+ * for these source files if you need to use Hibernate 2.1.8.
+ * <p/>
+ * To run a Cach� application with Hibernate 2.1.8, set the following flag when starting your JVM:
+ * <pre>
+ * -Dhibernate.jdbc.use_streams_for_binary=false
+ * </pre>
+ * <p/>
+ * In Hibernate 3.0.5, this flag is not necessary;
+ * it is taken care of by the Caché dialect itself.
+ * <br>
+ * <h2>HIBERNATE FILES ASSOCIATED WITH CACHÉ DIALECT</h2>
+ * The following files are associated with Caché dialect:
+ * <p/>
+ * <ol>
+ * <li>src\org\hibernate\dialect\Cache71Dialect.java</li>
+ * <li>src\org\hibernate\dialect\function\ConditionalParenthesisFunction.java</li>
+ * <li>src\org\hibernate\dialect\function\ConvertFunction.java</li>
+ * <li>src\org\hibernate\exception\CacheSQLStateConverter.java</li>
+ * <li>src\org\hibernate\sql\CacheJoinFragment.java</li>
+ * </ol>
+ * Cache71Dialect ships with Hibernate 3.2. All other dialects are distributed by InterSystems and subclass Cache71Dialect.
+ * <h2>Limitations</h2>
+ * The following is a list of the currently known limitations of using Cache' with Hibernate. Please check with InterSystems on the latest status as these are server side issues.
+ * <p/>
+ * The following also lists known issues with running the full Hibernate regression tests against InterSystems Cache'.
+ * <p/>
+ * - Delete of self-referential foreign keys.
+ * <p/>
+ * We do not current support deletion of a table row when the table has a foreign key that references itself and the foreign key in the table row references that table row. (self-referential foreign keys)
+ * <p/>
+ * - Support for "SELECT FOR UPDATE."
+ * <p/>
+ * The default locking mode strategy for Cache' is Hibernate's UpdateLockingStrategy, which you use with Hibernate's versioning capability.
+ * <p/>
+ * To use Concurrency modes that requires other locking strategies you can subclass your Cache' dialect and define a locking strategy.
+ * <p/>
+ * Cache' does not currently support "SELECT FOR UPDATE." While grammatically correct, it does no locking.
+ * <p/>
+ * In versions prior to 2007.1, there are limitations in support for outer joins (see Cache' documentation).
+ * <p/>
+ * For example, Cache' prior to 2007.1 does support "NOT NULL" clauses in the ON conditions.
+ * <p/>
+ * Cache' does not support using longvarbinary or longvarchar in a where clause.
+ * <p/>
+ * In Hibernate regression tests, Baz.hbm.xml, FooBar.hbm.xml, Glarch.hbm.xml and XY.hbm.xml have to be edited to replace "!" by BANG and "^" by CARET. Our own identifier translation is not enough since Hibernate uses names containing these in its own mapping files and Hibernate (on the client-side) does not know about our server-side mapping.
+ * <p/>
+ * There are some tests that involve batch operations where the test
+ * will succeed if you increase the size of the lock table from
+ * 786432 (the default) to 1786432. You can use the System Management
+ * Portal in 2007.1 or the configuration manager in 5.0.X to increase the
+ * size of the lock table. This requirement of increasing the lock
+ * table size is specific to running the Hibernate regression tests.
+ * <p/>
+ * Cache' fails tests that attempt to COUNT stream fields
+ * <p/>
+ * Here is an example of such an error message, SQL ERROR -37: Aggregate function COUNT not supported for Stream fields
+ * <p/>
+ * We fail on tests that use mappings like the following
+ * <p/>
+ * <pre>
+ * <property name="yob" formula="year(dob)"/>
+ * </pre>
+ * and the error is:
+ * <p/>
+ * ERROR #359: User defined SQL Function 'SQLUSER.YEAR' does not exist
+ * <p/>
+ * If the mapping is changed to
+ * <p/>
+ * <pre>
+ * <property name="yob" formula="{fn year(dob)}"/>
+ * </pre>
+ * then such tests succeed.
+ * <p/>
+ * Cache' 2007.1 supports temp tables. Prior versions do not.
+ * <p/>
+ * We do not support row valued expression. So WHERE clauses like "WHERE (a,b,c) = (d,e,f)" give syntax errors. The Hibernate regression tests use row valued expressions and we fail on these.
+ * <p/>
+ * For example:
+ * <p/>
+ * <pre>
+ * result = s.createQuery("from Transaction txn where txn.value = (1.5, 'AUD')").list();
+ * </pre>
+ * will not generate valid Cache' SQL because of the parenthesis around (1.5, 'AUD')
+ * <p/>
+ * Queries like the following:
+ * <p/>
+ * <pre>
+ * session.createQuery("select extract(second from current_timestamp()), extract(minute from current_timestamp()), extract(hour from current_timestamp()) from Mammal m").list();
+ * </pre>
+ * <p/>
+ * will not work, because we do support "extract" function.
+ * <p/>
+ * We do not support in () (empty parenthesis)
+ * <p/>
+ * We do not guarantee the case of a column returned using a group by clause.
+ * <p/>
+ * For instance in the regression test,
+ * <p/>
+ * <pre>
+ * list = s.find("select new Result( baz.name, max(foo.long), count(foo) ) from Baz baz join baz.fooArray foo group by baz.name");
+ * </pre>
+ * we return baz.name in lower case, and the test that generates this SQL fails.
+ *
+ * @author Jonathan Levinson
+ */
+
+public class Cache71Dialect extends Dialect {
+
+ /**
+ * Creates new <code>Cach�71Dialect</code> instance. Sets up the JDBC /
+ * Cach� type mappings.
+ */
+ public Cache71Dialect() {
+ super();
+ commonRegistration();
+ register71Functions();
+ }
+
+ protected final void commonRegistration() {
+ // Note: For object <-> SQL datatype mappings see:
+ // Configuration Manager | Advanced | SQL | System DDL Datatype Mappings
+ //
+ // TBD registerColumnType(Types.BINARY, "binary($1)");
+ // changed 08-11-2005, jsl
+ registerColumnType( Types.BINARY, "varbinary($1)" );
+ registerColumnType( Types.BIGINT, "BigInt" );
+ registerColumnType( Types.BIT, "bit" );
+ registerColumnType( Types.CHAR, "char(1)" );
+ registerColumnType( Types.DATE, "date" );
+ registerColumnType( Types.DECIMAL, "decimal" );
+ registerColumnType( Types.DOUBLE, "double" );
+ registerColumnType( Types.FLOAT, "float" );
+ registerColumnType( Types.INTEGER, "integer" );
+ registerColumnType( Types.LONGVARBINARY, "longvarbinary" ); // binary %Stream
+ registerColumnType( Types.LONGVARCHAR, "longvarchar" ); // character %Stream
+ registerColumnType( Types.NUMERIC, "numeric($p,$s)" );
+ registerColumnType( Types.REAL, "real" );
+ registerColumnType( Types.SMALLINT, "smallint" );
+ registerColumnType( Types.TIMESTAMP, "timestamp" );
+ registerColumnType( Types.TIME, "time" );
+ registerColumnType( Types.TINYINT, "tinyint" );
+ // TBD should this be varbinary($1)?
+ // registerColumnType(Types.VARBINARY, "binary($1)");
+ registerColumnType( Types.VARBINARY, "longvarbinary" );
+ registerColumnType( Types.VARCHAR, "varchar($l)" );
+ registerColumnType( Types.BLOB, "longvarbinary" );
+ registerColumnType( Types.CLOB, "longvarchar" );
+
+ getDefaultProperties().setProperty( Environment.USE_STREAMS_FOR_BINARY, "false" );
+ getDefaultProperties().setProperty( Environment.STATEMENT_BATCH_SIZE, DEFAULT_BATCH_SIZE );
+ //getDefaultProperties().setProperty(Environment.STATEMENT_BATCH_SIZE, NO_BATCH);
+
+ getDefaultProperties().setProperty( Environment.USE_SQL_COMMENTS, "false" );
+
+ registerFunction( "abs", new StandardSQLFunction( "abs" ) );
+ registerFunction( "acos", new StandardJDBCEscapeFunction( "acos", Hibernate.DOUBLE ) );
+ registerFunction( "%alphaup", new StandardSQLFunction( "%alphaup", Hibernate.STRING ) );
+ registerFunction( "ascii", new StandardSQLFunction( "ascii", Hibernate.STRING ) );
+ registerFunction( "asin", new StandardJDBCEscapeFunction( "asin", Hibernate.DOUBLE ) );
+ registerFunction( "atan", new StandardJDBCEscapeFunction( "atan", Hibernate.DOUBLE ) );
+ registerFunction( "bit_length", new SQLFunctionTemplate( Hibernate.INTEGER, "($length(?1)*8)" ) );
+ // hibernate impelemnts cast in Dialect.java
+ registerFunction( "ceiling", new StandardSQLFunction( "ceiling", Hibernate.INTEGER ) );
+ registerFunction( "char", new StandardJDBCEscapeFunction( "char", Hibernate.CHARACTER ) );
+ registerFunction( "character_length", new StandardSQLFunction( "character_length", Hibernate.INTEGER ) );
+ registerFunction( "char_length", new StandardSQLFunction( "char_length", Hibernate.INTEGER ) );
+ registerFunction( "cos", new StandardJDBCEscapeFunction( "cos", Hibernate.DOUBLE ) );
+ registerFunction( "cot", new StandardJDBCEscapeFunction( "cot", Hibernate.DOUBLE ) );
+ registerFunction( "coalesce", new VarArgsSQLFunction( "coalesce(", ",", ")" ) );
+ registerFunction( "concat", new VarArgsSQLFunction( Hibernate.STRING, "", "||", "" ) );
+ registerFunction( "convert", new ConvertFunction() );
+ registerFunction( "curdate", new StandardJDBCEscapeFunction( "curdate", Hibernate.DATE ) );
+ registerFunction( "current_date", new NoArgSQLFunction( "current_date", Hibernate.DATE, false ) );
+ registerFunction( "current_time", new NoArgSQLFunction( "current_time", Hibernate.TIME, false ) );
+ registerFunction(
+ "current_timestamp", new ConditionalParenthesisFunction( "current_timestamp", Hibernate.TIMESTAMP )
+ );
+ registerFunction( "curtime", new StandardJDBCEscapeFunction( "curtime", Hibernate.TIME ) );
+ registerFunction( "database", new StandardJDBCEscapeFunction( "database", Hibernate.STRING ) );
+ registerFunction( "dateadd", new VarArgsSQLFunction( Hibernate.TIMESTAMP, "dateadd(", ",", ")" ) );
+ registerFunction( "datediff", new VarArgsSQLFunction( Hibernate.INTEGER, "datediff(", ",", ")" ) );
+ registerFunction( "datename", new VarArgsSQLFunction( Hibernate.STRING, "datename(", ",", ")" ) );
+ registerFunction( "datepart", new VarArgsSQLFunction( Hibernate.INTEGER, "datepart(", ",", ")" ) );
+ registerFunction( "day", new StandardSQLFunction( "day", Hibernate.INTEGER ) );
+ registerFunction( "dayname", new StandardJDBCEscapeFunction( "dayname", Hibernate.STRING ) );
+ registerFunction( "dayofmonth", new StandardJDBCEscapeFunction( "dayofmonth", Hibernate.INTEGER ) );
+ registerFunction( "dayofweek", new StandardJDBCEscapeFunction( "dayofweek", Hibernate.INTEGER ) );
+ registerFunction( "dayofyear", new StandardJDBCEscapeFunction( "dayofyear", Hibernate.INTEGER ) );
+ // is it necessary to register %exact since it can only appear in a where clause?
+ registerFunction( "%exact", new StandardSQLFunction( "%exact", Hibernate.STRING ) );
+ registerFunction( "exp", new StandardJDBCEscapeFunction( "exp", Hibernate.DOUBLE ) );
+ registerFunction( "%external", new StandardSQLFunction( "%external", Hibernate.STRING ) );
+ registerFunction( "$extract", new VarArgsSQLFunction( Hibernate.INTEGER, "$extract(", ",", ")" ) );
+ registerFunction( "$find", new VarArgsSQLFunction( Hibernate.INTEGER, "$find(", ",", ")" ) );
+ registerFunction( "floor", new StandardSQLFunction( "floor", Hibernate.INTEGER ) );
+ registerFunction( "getdate", new StandardSQLFunction( "getdate", Hibernate.TIMESTAMP ) );
+ registerFunction( "hour", new StandardJDBCEscapeFunction( "hour", Hibernate.INTEGER ) );
+ registerFunction( "ifnull", new VarArgsSQLFunction( "ifnull(", ",", ")" ) );
+ registerFunction( "%internal", new StandardSQLFunction( "%internal" ) );
+ registerFunction( "isnull", new VarArgsSQLFunction( "isnull(", ",", ")" ) );
+ registerFunction( "isnumeric", new StandardSQLFunction( "isnumeric", Hibernate.INTEGER ) );
+ registerFunction( "lcase", new StandardJDBCEscapeFunction( "lcase", Hibernate.STRING ) );
+ registerFunction( "left", new StandardJDBCEscapeFunction( "left", Hibernate.STRING ) );
+ registerFunction( "len", new StandardSQLFunction( "len", Hibernate.INTEGER ) );
+ registerFunction( "length", new StandardSQLFunction( "length", Hibernate.INTEGER ) );
+ registerFunction( "$length", new VarArgsSQLFunction( "$length(", ",", ")" ) );
+ // aggregate functions shouldn't be registered, right?
+ //registerFunction( "list", new StandardSQLFunction("list",Hibernate.STRING) );
+ // stopped on $list
+ registerFunction( "$list", new VarArgsSQLFunction( "$list(", ",", ")" ) );
+ registerFunction( "$listdata", new VarArgsSQLFunction( "$listdata(", ",", ")" ) );
+ registerFunction( "$listfind", new VarArgsSQLFunction( "$listfind(", ",", ")" ) );
+ registerFunction( "$listget", new VarArgsSQLFunction( "$listget(", ",", ")" ) );
+ registerFunction( "$listlength", new StandardSQLFunction( "$listlength", Hibernate.INTEGER ) );
+ registerFunction( "locate", new StandardSQLFunction( "$FIND", Hibernate.INTEGER ) );
+ registerFunction( "log", new StandardJDBCEscapeFunction( "log", Hibernate.DOUBLE ) );
+ registerFunction( "log10", new StandardJDBCEscapeFunction( "log", Hibernate.DOUBLE ) );
+ registerFunction( "lower", new StandardSQLFunction( "lower" ) );
+ registerFunction( "ltrim", new StandardSQLFunction( "ltrim" ) );
+ registerFunction( "minute", new StandardJDBCEscapeFunction( "minute", Hibernate.INTEGER ) );
+ registerFunction( "mod", new StandardJDBCEscapeFunction( "mod", Hibernate.DOUBLE ) );
+ registerFunction( "month", new StandardJDBCEscapeFunction( "month", Hibernate.INTEGER ) );
+ registerFunction( "monthname", new StandardJDBCEscapeFunction( "monthname", Hibernate.STRING ) );
+ registerFunction( "now", new StandardJDBCEscapeFunction( "monthname", Hibernate.TIMESTAMP ) );
+ registerFunction( "nullif", new VarArgsSQLFunction( "nullif(", ",", ")" ) );
+ registerFunction( "nvl", new NvlFunction() );
+ registerFunction( "%odbcin", new StandardSQLFunction( "%odbcin" ) );
+ registerFunction( "%odbcout", new StandardSQLFunction( "%odbcin" ) );
+ registerFunction( "%pattern", new VarArgsSQLFunction( Hibernate.STRING, "", "%pattern", "" ) );
+ registerFunction( "pi", new StandardJDBCEscapeFunction( "pi", Hibernate.DOUBLE ) );
+ registerFunction( "$piece", new VarArgsSQLFunction( Hibernate.STRING, "$piece(", ",", ")" ) );
+ registerFunction( "position", new VarArgsSQLFunction( Hibernate.INTEGER, "position(", " in ", ")" ) );
+ registerFunction( "power", new VarArgsSQLFunction( Hibernate.STRING, "power(", ",", ")" ) );
+ registerFunction( "quarter", new StandardJDBCEscapeFunction( "quarter", Hibernate.INTEGER ) );
+ registerFunction( "repeat", new VarArgsSQLFunction( Hibernate.STRING, "repeat(", ",", ")" ) );
+ registerFunction( "replicate", new VarArgsSQLFunction( Hibernate.STRING, "replicate(", ",", ")" ) );
+ registerFunction( "right", new StandardJDBCEscapeFunction( "right", Hibernate.STRING ) );
+ registerFunction( "round", new VarArgsSQLFunction( Hibernate.FLOAT, "round(", ",", ")" ) );
+ registerFunction( "rtrim", new StandardSQLFunction( "rtrim", Hibernate.STRING ) );
+ registerFunction( "second", new StandardJDBCEscapeFunction( "second", Hibernate.INTEGER ) );
+ registerFunction( "sign", new StandardSQLFunction( "sign", Hibernate.INTEGER ) );
+ registerFunction( "sin", new StandardJDBCEscapeFunction( "sin", Hibernate.DOUBLE ) );
+ registerFunction( "space", new StandardSQLFunction( "space", Hibernate.STRING ) );
+ registerFunction( "%sqlstring", new VarArgsSQLFunction( Hibernate.STRING, "%sqlstring(", ",", ")" ) );
+ registerFunction( "%sqlupper", new VarArgsSQLFunction( Hibernate.STRING, "%sqlupper(", ",", ")" ) );
+ registerFunction( "sqrt", new StandardJDBCEscapeFunction( "SQRT", Hibernate.DOUBLE ) );
+ registerFunction( "%startswith", new VarArgsSQLFunction( Hibernate.STRING, "", "%startswith", "" ) );
+ // below is for Cache' that don't have str in 2007.1 there is str and we register str directly
+ registerFunction( "str", new SQLFunctionTemplate( Hibernate.STRING, "cast(?1 as char varying)" ) );
+ registerFunction( "string", new VarArgsSQLFunction( Hibernate.STRING, "string(", ",", ")" ) );
+ // note that %string is deprecated
+ registerFunction( "%string", new VarArgsSQLFunction( Hibernate.STRING, "%string(", ",", ")" ) );
+ registerFunction( "substr", new VarArgsSQLFunction( Hibernate.STRING, "substr(", ",", ")" ) );
+ registerFunction( "substring", new VarArgsSQLFunction( Hibernate.STRING, "substring(", ",", ")" ) );
+ registerFunction( "sysdate", new NoArgSQLFunction( "sysdate", Hibernate.TIMESTAMP, false ) );
+ registerFunction( "tan", new StandardJDBCEscapeFunction( "tan", Hibernate.DOUBLE ) );
+ registerFunction( "timestampadd", new StandardJDBCEscapeFunction( "timestampadd", Hibernate.DOUBLE ) );
+ registerFunction( "timestampdiff", new StandardJDBCEscapeFunction( "timestampdiff", Hibernate.DOUBLE ) );
+ registerFunction( "tochar", new VarArgsSQLFunction( Hibernate.STRING, "tochar(", ",", ")" ) );
+ registerFunction( "to_char", new VarArgsSQLFunction( Hibernate.STRING, "to_char(", ",", ")" ) );
+ registerFunction( "todate", new VarArgsSQLFunction( Hibernate.STRING, "todate(", ",", ")" ) );
+ registerFunction( "to_date", new VarArgsSQLFunction( Hibernate.STRING, "todate(", ",", ")" ) );
+ registerFunction( "tonumber", new StandardSQLFunction( "tonumber" ) );
+ registerFunction( "to_number", new StandardSQLFunction( "tonumber" ) );
+ // TRIM(end_keyword string-expression-1 FROM string-expression-2)
+ // use Hibernate implementation "From" is one of the parameters they pass in position ?3
+ //registerFunction( "trim", new SQLFunctionTemplate(Hibernate.STRING, "trim(?1 ?2 from ?3)") );
+ registerFunction( "truncate", new StandardJDBCEscapeFunction( "truncate", Hibernate.STRING ) );
+ registerFunction( "ucase", new StandardJDBCEscapeFunction( "ucase", Hibernate.STRING ) );
+ registerFunction( "upper", new StandardSQLFunction( "upper" ) );
+ // %upper is deprecated
+ registerFunction( "%upper", new StandardSQLFunction( "%upper" ) );
+ registerFunction( "user", new StandardJDBCEscapeFunction( "user", Hibernate.STRING ) );
+ registerFunction( "week", new StandardJDBCEscapeFunction( "user", Hibernate.INTEGER ) );
+ registerFunction( "xmlconcat", new VarArgsSQLFunction( Hibernate.STRING, "xmlconcat(", ",", ")" ) );
+ registerFunction( "xmlelement", new VarArgsSQLFunction( Hibernate.STRING, "xmlelement(", ",", ")" ) );
+ // xmlforest requires a new kind of function constructor
+ registerFunction( "year", new StandardJDBCEscapeFunction( "year", Hibernate.INTEGER ) );
+ }
+
+ protected final void register71Functions() {
+ this.registerFunction( "str", new VarArgsSQLFunction( Hibernate.STRING, "str(", ",", ")" ) );
+ }
+
+ // DDL support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public boolean hasAlterTable() {
+ // Does this dialect support the ALTER TABLE syntax?
+ return true;
+ }
+
+ public boolean qualifyIndexName() {
+ // Do we need to qualify index names with the schema name?
+ return false;
+ }
+
+ public boolean supportsUnique() {
+ // Does this dialect support the UNIQUE column syntax?
+ return true;
+ }
+
+ /**
+ * The syntax used to add a foreign key constraint to a table.
+ *
+ * @return String
+ */
+ public String getAddForeignKeyConstraintString(
+ String constraintName,
+ String[] foreignKey,
+ String referencedTable,
+ String[] primaryKey,
+ boolean referencesPrimaryKey) {
+ // The syntax used to add a foreign key constraint to a table.
+ return new StringBuffer( 300 )
+ .append( " ADD CONSTRAINT " )
+ .append( constraintName )
+ .append( " FOREIGN KEY " )
+ .append( constraintName )
+ .append( " (" )
+ .append( StringHelper.join( ", ", foreignKey ) ) // identifier-commalist
+ .append( ") REFERENCES " )
+ .append( referencedTable )
+ .append( " (" )
+ .append( StringHelper.join( ", ", primaryKey ) ) // identifier-commalist
+ .append( ") " )
+ .toString();
+ }
+
+ public boolean supportsCheck() {
+ // Does this dialect support check constraints?
+ return false;
+ }
+
+ public String getAddColumnString() {
+ // The syntax used to add a column to a table
+ return " add column";
+ }
+
+ public String getCascadeConstraintsString() {
+ // Completely optional cascading drop clause.
+ return "";
+ }
+
+ public boolean dropConstraints() {
+ // Do we need to drop constraints before dropping tables in this dialect?
+ return true;
+ }
+
+ public boolean supportsCascadeDelete() {
+ return true;
+ }
+
+ public boolean hasSelfReferentialForeignKeyBug() {
+ return true;
+ }
+
+ // temporary table support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public boolean supportsTemporaryTables() {
+ return true;
+ }
+
+ public String generateTemporaryTableName(String baseTableName) {
+ String name = super.generateTemporaryTableName( baseTableName );
+ return name.length() > 25 ? name.substring( 1, 25 ) : name;
+ }
+
+ public String getCreateTemporaryTableString() {
+ return "create global temporary table";
+ }
+
+ public boolean performTemporaryTableDDLInIsolation() {
+ return false;
+ }
+
+ public String getCreateTemporaryTablePostfix() {
+ return "";
+ }
+
+ public boolean dropTemporaryTableAfterUse() {
+ return true;
+ }
+
+ // IDENTITY support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public boolean supportsIdentityColumns() {
+ return true;
+ }
+
+ public Class getNativeIdentifierGeneratorClass() {
+ return IdentityGenerator.class;
+ }
+
+ public boolean hasDataTypeInIdentityColumn() {
+ // Whether this dialect has an Identity clause added to the data type or a completely seperate identity
+ // data type
+ return true;
+ }
+
+ public String getIdentityColumnString() throws MappingException {
+ // The keyword used to specify an identity column, if identity column key generation is supported.
+ return "identity";
+ }
+
+ public String getIdentitySelectString() {
+ return "SELECT LAST_IDENTITY() FROM %TSQL_sys.snf";
+ }
+
+ // SEQUENCE support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public boolean supportsSequences() {
+ return true;
+ }
+
+ public String getSequenceNextValString(String sequenceName) {
+ return "select InterSystems.Sequences_GetNext('" + sequenceName + "') from InterSystems.Sequences where ucase(name)=ucase('" + sequenceName + "'))";
+ }
+
+ public String getSelectSequenceNextValString(String sequenceName) {
+ return "(select InterSystems.Sequences_GetNext('" + sequenceName + "') from InterSystems.Sequences where ucase(name)=ucase('" + sequenceName + "')))";
+ }
+
+ public String getCreateSequenceString(String sequenceName) {
+ return "insert into InterSystems.Sequences(Name) values (ucase('" + sequenceName + "'))";
+ }
+
+ public String getDropSequenceString(String sequenceName) {
+ return "delete from InterSystems.Sequences where ucase(name)=ucase('" + sequenceName + "')";
+ }
+
+ public String getQuerySequencesString() {
+ return "select name from InterSystems.Sequences";
+ }
+
+ // lock acquisition support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public boolean supportsForUpdate() {
+ // Does this dialect support the FOR UPDATE syntax?
+ return false;
+ }
+
+ public boolean supportsForUpdateOf() {
+ // Does this dialect support FOR UPDATE OF, allowing particular rows to be locked?
+ return false;
+ }
+
+ public boolean supportsForUpdateNowait() {
+ // Does this dialect support the Oracle-style FOR UPDATE NOWAIT syntax?
+ return false;
+ }
+
+ public boolean supportsOuterJoinForUpdate() {
+ return false;
+ }
+
+ public LockingStrategy getLockingStrategy(Lockable lockable, LockMode lockMode) {
+ // InterSystems Cache' does not current support "SELECT ... FOR UPDATE" syntax...
+ // Set your transaction mode to READ_COMMITTED before using
+ if ( lockMode.greaterThan( LockMode.READ ) ) {
+ return new UpdateLockingStrategy( lockable, lockMode );
+ }
+ else {
+ return new SelectLockingStrategy( lockable, lockMode );
+ }
+ }
+
+ // LIMIT support (ala TOP) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public boolean supportsLimit() {
+ return true;
+ }
+
+ public boolean supportsLimitOffset() {
+ return false;
+ }
+
+ public boolean supportsVariableLimit() {
+ return true;
+ }
+
+ public boolean bindLimitParametersFirst() {
+ // Does the LIMIT clause come at the start of the SELECT statement, rather than at the end?
+ return true;
+ }
+
+ public boolean useMaxForLimit() {
+ // Does the LIMIT clause take a "maximum" row number instead of a total number of returned rows?
+ return true;
+ }
+
+ public String getLimitString(String sql, boolean hasOffset) {
+ if ( hasOffset ) {
+ throw new UnsupportedOperationException( "An offset may not be specified to <TOP n> in Cache SQL" );
+ }
+
+ // This does not support the Cache SQL 'DISTINCT BY (comma-list)' extensions,
+ // but this extension is not supported through Hibernate anyway.
+ int insertionPoint = sql.startsWith( "select distinct" ) ? 15 : 6;
+
+ return new StringBuffer( sql.length() + 8 )
+ .append( sql )
+ .insert( insertionPoint, " TOP ? " )
+ .toString();
+ }
+
+ // callable statement support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public int registerResultSetOutParameter(CallableStatement statement, int col) throws SQLException {
+ return col;
+ }
+
+ public ResultSet getResultSet(CallableStatement ps) throws SQLException {
+ ps.execute();
+ return ( ResultSet ) ps.getObject( 1 );
+ }
+
+ // miscellaneous support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public String getLowercaseFunction() {
+ // The name of the SQL function that transforms a string to lowercase
+ return "lower";
+ }
+
+ public String getNullColumnString() {
+ // The keyword used to specify a nullable column.
+ return " null";
+ }
+
+ public JoinFragment createOuterJoinFragment() {
+ // Create an OuterJoinGenerator for this dialect.
+ return new CacheJoinFragment();
+ }
+
+ public String getNoColumnsInsertString() {
+ // The keyword used to insert a row without specifying
+ // any column values
+ return " default values";
+ }
+
+ public SQLExceptionConverter buildSQLExceptionConverter() {
+ return new CacheSQLStateConverter( EXTRACTER );
+ }
+
+ public static final ViolatedConstraintNameExtracter EXTRACTER = new TemplatedViolatedConstraintNameExtracter() {
+ /**
+ * Extract the name of the violated constraint from the given SQLException.
+ *
+ * @param sqle The exception that was the result of the constraint violation.
+ * @return The extracted constraint name.
+ */
+ public String extractConstraintName(SQLException sqle) {
+ return extractUsingTemplate( "constraint (", ") violated", sqle.getMessage() );
+ }
+ };
+}
Modified: branches/Branch_3_2/Hibernate3/src/org/hibernate/dialect/Dialect.java
===================================================================
--- branches/Branch_3_2/Hibernate3/src/org/hibernate/dialect/Dialect.java 2006-11-08 16:54:55 UTC (rev 10774)
+++ branches/Branch_3_2/Hibernate3/src/org/hibernate/dialect/Dialect.java 2006-11-08 16:56:52 UTC (rev 10775)
@@ -57,11 +57,19 @@
private static final Log log = LogFactory.getLog( Dialect.class );
- static final String DEFAULT_BATCH_SIZE = "15";
- static final String NO_BATCH = "0";
+ public static final String DEFAULT_BATCH_SIZE = "15";
+ public static final String NO_BATCH = "0";
+ /**
+ * Characters used for quoting SQL identifiers
+ */
+ public static final String QUOTE = "`\"[";
+ public static final String CLOSED_QUOTE = "`\"]";
+
+
+ // build the map of standard ANSI SQL aggregation functions ~~~~~~~~~~~~~~~
+
private static final Map STANDARD_AGGREGATE_FUNCTIONS = new HashMap();
-
static {
STANDARD_AGGREGATE_FUNCTIONS.put( "count", new StandardSQLFunction("count") {
public Type getReturnType(Type columnType, Mapping mapping) {
@@ -136,7 +144,9 @@
private final Map sqlFunctions = new HashMap();
private final Set sqlKeywords = new HashSet();
-
+
+ // constructors and factory methods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
protected Dialect() {
log.info( "Using dialect: " + this );
sqlFunctions.putAll( STANDARD_AGGREGATE_FUNCTIONS );
@@ -186,27 +196,74 @@
registerHibernateType( Types.DECIMAL, Hibernate.BIG_DECIMAL.getName() );
registerHibernateType( Types.BLOB, Hibernate.BLOB.getName() );
registerHibernateType( Types.CLOB, Hibernate.CLOB.getName() );
-
}
- public String toString() {
- return getClass().getName();
+ /**
+ * Get an instance of the dialect specified by the current <tt>System</tt> properties.
+ *
+ * @return The specified Dialect
+ * @throws HibernateException If no dialect was specified, or if it could not be instantiated.
+ */
+ public static Dialect getDialect() throws HibernateException {
+ String dialectName = Environment.getProperties().getProperty( Environment.DIALECT );
+ return instantiateDialect( dialectName );
}
+
/**
- * Characters used for quoting SQL identifiers
+ * Get an instance of the dialect specified by the given properties or by
+ * the current <tt>System</tt> properties.
+ *
+ * @param props The properties to use for finding the dialect class to use.
+ * @return The specified Dialect
+ * @throws HibernateException If no dialect was specified, or if it could not be instantiated.
*/
- public static final String QUOTE = "`\"[";
- public static final String CLOSED_QUOTE = "`\"]";
+ public static Dialect getDialect(Properties props) throws HibernateException {
+ String dialectName = props.getProperty( Environment.DIALECT );
+ if ( dialectName == null ) {
+ return getDialect();
+ }
+ return instantiateDialect( dialectName );
+ }
+ private static Dialect instantiateDialect(String dialectName) throws HibernateException {
+ if ( dialectName == null ) {
+ throw new HibernateException( "The dialect was not set. Set the property hibernate.dialect." );
+ }
+ try {
+ return ( Dialect ) ReflectHelper.classForName( dialectName ).newInstance();
+ }
+ catch ( ClassNotFoundException cnfe ) {
+ throw new HibernateException( "Dialect class not found: " + dialectName );
+ }
+ catch ( Exception e ) {
+ throw new HibernateException( "Could not instantiate dialect class", e );
+ }
+ }
/**
+ * Retrieve a set of default Hibernate properties for this database.
+ *
+ * @return a set of Hibernate properties
+ */
+ public final Properties getDefaultProperties() {
+ return properties;
+ }
+
+ public String toString() {
+ return getClass().getName();
+ }
+
+
+ // database type mapping support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
* Get the name of the database type associated with the given
- * <tt>java.sql.Types</tt> typecode.
+ * {@link java.sql.Types} typecode.
*
- * @param code <tt>java.sql.Types</tt> typecode
+ * @param code The {@link java.sql.Types} typecode
* @return the database type name
- * @throws HibernateException
+ * @throws HibernateException If no mapping was specified for that type.
*/
public String getTypeName(int code) throws HibernateException {
String result = typeNames.get( code );
@@ -216,355 +273,310 @@
return result;
}
-
- public String getHibernateTypeName(int code) throws HibernateException {
- String result = hibernateTypeNames.get( code );
- if ( result == null ) {
- throw new HibernateException(
- "No Hibernate type mapping for java.sql.Types code: " +
- code);
- }
- return result;
- }
-
- public String getHibernateTypeName(int code, int length, int precision, int scale) throws HibernateException {
- String result = hibernateTypeNames.get( code, length, precision, scale );
- if ( result == null ) {
- throw new HibernateException(
- "No Hibernate type mapping for java.sql.Types code: " +
- code +
- ", length: " +
- length
- );
- }
- return result;
- }
-
/**
* Get the name of the database type associated with the given
- * <tt>java.sql.Types</tt> typecode.
- * @param code <tt>java.sql.Types</tt> typecode
- * @param length the length or precision of the column
- * @param precision the precision of the column
- * @param scale the scale of the column
+ * {@link java.sql.Types} typecode with the given storage specification
+ * parameters.
*
+ * @param code The {@link java.sql.Types} typecode
+ * @param length The datatype length
+ * @param precision The datatype precision
+ * @param scale The datatype scale
* @return the database type name
- * @throws HibernateException
+ * @throws HibernateException If no mapping was specified for that type.
*/
public String getTypeName(int code, int length, int precision, int scale) throws HibernateException {
String result = typeNames.get( code, length, precision, scale );
if ( result == null ) {
- throw new HibernateException(
+ throw new HibernateException(
"No type mapping for java.sql.Types code: " +
code +
", length: " +
- length
- );
+ length
+ );
}
return result;
}
-
+
+ /**
+ * Get the name of the database type appropriate for casting operations
+ * (via the CAST() SQL function) for the given {@link java.sql.Types} typecode.
+ *
+ * @param code The {@link java.sql.Types} typecode
+ * @return The database type name
+ */
public String getCastTypeName(int code) {
- return getTypeName(
- code,
- Column.DEFAULT_LENGTH,
- Column.DEFAULT_PRECISION,
- Column.DEFAULT_SCALE
- );
+ return getTypeName( code, Column.DEFAULT_LENGTH, Column.DEFAULT_PRECISION, Column.DEFAULT_SCALE );
}
- protected void registerFunction(String name, SQLFunction function) {
- sqlFunctions.put( name, function );
- }
-
- protected void registerKeyword(String word) {
- sqlKeywords.add(word);
- }
-
- public Set getKeywords() {
- return sqlKeywords;
- }
-
/**
- * Subclasses register a typename for the given type code and maximum
+ * Subclasses register a type name for the given type code and maximum
* column length. <tt>$l</tt> in the type name with be replaced by the
* column length (if appropriate).
*
- * @param code <tt>java.sql.Types</tt> typecode
- * @param capacity maximum length of database type
- * @param name the database type name
+ * @param code The {@link java.sql.Types} typecode
+ * @param capacity The maximum length of database type
+ * @param name The database type name
*/
protected void registerColumnType(int code, int capacity, String name) {
typeNames.put( code, capacity, name );
}
/**
- * Subclasses register a typename for the given type code. <tt>$l</tt> in
+ * Subclasses register a type name for the given type code. <tt>$l</tt> in
* the type name with be replaced by the column length (if appropriate).
*
- * @param code <tt>java.sql.Types</tt> typecode
- * @param name the database type name
+ * @param code The {@link java.sql.Types} typecode
+ * @param name The database type name
*/
protected void registerColumnType(int code, String name) {
typeNames.put( code, name );
}
-
- protected void registerHibernateType(int sqlcode, String name) {
- hibernateTypeNames.put( sqlcode, name);
- }
- protected void registerHibernateType(int sqlcode, int capacity, String name) {
- hibernateTypeNames.put( sqlcode, capacity, name);
- }
- /**
- * Does this dialect support the <tt>ALTER TABLE</tt> syntax?
- *
- * @return boolean
- */
- public boolean hasAlterTable() {
- return true;
- }
+ // hibernate type mapping support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
- * Do we need to drop constraints before dropping tables in this dialect?
+ * Get the name of the Hibernate {@link org.hibernate.type.Type} associated with th given
+ * {@link java.sql.Types} typecode.
*
- * @return boolean
+ * @param code The {@link java.sql.Types} typecode
+ * @return The Hibernate {@link org.hibernate.type.Type} name.
+ * @throws HibernateException If no mapping was specified for that type.
*/
- public boolean dropConstraints() {
- return true;
+ public String getHibernateTypeName(int code) throws HibernateException {
+ String result = hibernateTypeNames.get( code );
+ if ( result == null ) {
+ throw new HibernateException( "No Hibernate type mapping for java.sql.Types code: " + code );
+ }
+ return result;
}
/**
- * Do we need to qualify index names with the schema name?
+ * Get the name of the Hibernate {@link org.hibernate.type.Type} associated
+ * with the given {@link java.sql.Types} typecode with the given storage
+ * specification parameters.
*
- * @return boolean
+ * @param code The {@link java.sql.Types} typecode
+ * @param length The datatype length
+ * @param precision The datatype precision
+ * @param scale The datatype scale
+ * @return The Hibernate {@link org.hibernate.type.Type} name.
+ * @throws HibernateException If no mapping was specified for that type.
*/
- public boolean qualifyIndexName() {
- return true;
+ public String getHibernateTypeName(int code, int length, int precision, int scale) throws HibernateException {
+ String result = hibernateTypeNames.get( code, length, precision, scale );
+ if ( result == null ) {
+ throw new HibernateException(
+ "No Hibernate type mapping for java.sql.Types code: " +
+ code +
+ ", length: " +
+ length
+ );
+ }
+ return result;
}
/**
- * Does the <tt>FOR UPDATE OF</tt> syntax specify particular
- * columns?
- */
- public boolean forUpdateOfColumns() {
- return false;
- }
-
- /**
- * Retrieves the <tt>FOR UPDATE OF column_list</tt> syntax specific to this
- * dialect, where the given aliases represent the aliases of the columns
- * which are to be write locked.
+ * Registers a Hibernate {@link org.hibernate.type.Type} name for the given
+ * {@link java.sql.Types} type code and maximum column length.
*
- * @return The appropriate <tt>FOR UPDATE OF column_list</tt> clause string.
+ * @param code The {@link java.sql.Types} typecode
+ * @param capacity The maximum length of database type
+ * @param name The Hibernate {@link org.hibernate.type.Type} name
*/
- public String getForUpdateString(String aliases) {
- return getForUpdateString();
+ protected void registerHibernateType(int code, int capacity, String name) {
+ hibernateTypeNames.put( code, capacity, name);
}
/**
- * Retrieves the <tt>FOR UPDATE OF column_list NOWAIT</tt> syntax specific
- * to this dialect, where the given aliases represent the aliases of the
- * columns which are to be write locked.
+ * Registers a Hibernate {@link org.hibernate.type.Type} name for the given
+ * {@link java.sql.Types} type code.
*
- * @return The appropriate <tt>FOR UPDATE colunm_list</tt> NOWAIT clause string.
+ * @param code The {@link java.sql.Types} typecode
+ * @param name The Hibernate {@link org.hibernate.type.Type} name
*/
- public String getForUpdateNowaitString(String aliases) {
- return getForUpdateString( aliases );
+ protected void registerHibernateType(int code, String name) {
+ hibernateTypeNames.put( code, name);
}
- /**
- * Retrieves the <tt>FOR UPDATE</tt> syntax specific to this dialect.
- *
- * @return The appropriate <tt>FOR UPDATE</tt> clause string.
- */
- public String getForUpdateString() {
- return " for update";
- }
- /**
- * Retrieves the <tt>FOR UPDATE NOWAIT</tt> syntax specific to this dialect.
- *
- * @return The appropriate <tt>FOR UPDATE NOWAIT</tt> clause string.
- */
- public String getForUpdateNowaitString() {
- return getForUpdateString();
+ // function support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ protected void registerFunction(String name, SQLFunction function) {
+ sqlFunctions.put( name, function );
}
/**
- * Does this dialect support the <tt>UNIQUE</tt> column syntax?
+ * Retrieves a map of the dialect's registered fucntions
+ * (functionName => {@link org.hibernate.dialect.function.SQLFunction}).
*
- * @return boolean
+ * @return The map of registered functions.
*/
- public boolean supportsUnique() {
- return true;
+ public final Map getFunctions() {
+ return sqlFunctions;
}
-
- /**
- * Does this dialect support adding Unique constraints via create and alter table ?
- * @return boolean
- */
- public boolean supportsUniqueConstraintInCreateAlterTable() {
- return true;
- }
+ // keyword support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- /**
- * The syntax used to add a column to a table (optional).
- */
- public String getAddColumnString() {
- throw new UnsupportedOperationException( "No add column syntax supported by Dialect" );
+ protected void registerKeyword(String word) {
+ sqlKeywords.add(word);
}
-
- public String getDropForeignKeyString() {
- return " drop constraint ";
+
+ public Set getKeywords() {
+ return sqlKeywords;
}
- public String getTableTypeString() {
- return "";
- }
+ // native identifier generatiion ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
/**
- * The syntax used to add a foreign key constraint to a table.
- *
- * @param referencesPrimaryKey if false, constraint should be
- * explicit about which column names the constraint refers to
+ * The class (which implements {@link org.hibernate.id.IdentifierGenerator})
+ * which acts as this dialects native generation strategy.
+ * <p/>
+ * Comes into play whenever the user specifies the native generator.
*
- * @return String
+ * @return The native generator class.
*/
- public String getAddForeignKeyConstraintString(
- String constraintName,
- String[] foreignKey,
- String referencedTable,
- String[] primaryKey,
- boolean referencesPrimaryKey
- ) {
- StringBuffer res = new StringBuffer( 30 );
-
- res.append( " add constraint " )
- .append( constraintName )
- .append( " foreign key (" )
- .append( StringHelper.join( ", ", foreignKey ) )
- .append( ") references " )
- .append( referencedTable );
-
- if(!referencesPrimaryKey) {
- res.append(" (")
- .append( StringHelper.join(", ", primaryKey) )
- .append(')');
+ public Class getNativeIdentifierGeneratorClass() {
+ if ( supportsIdentityColumns() ) {
+ return IdentityGenerator.class;
}
-
- return res.toString();
+ else if ( supportsSequences() ) {
+ return SequenceGenerator.class;
+ }
+ else {
+ return TableHiLoGenerator.class;
+ }
}
- /**
- * The syntax used to add a primary key constraint to a table.
- *
- * @return String
- */
- public String getAddPrimaryKeyConstraintString(String constraintName) {
- return " add constraint " + constraintName + " primary key ";
- }
+ // IDENTITY support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
/**
- * The keyword used to specify a nullable column.
+ * Does this dialect support identity column key generation?
*
- * @return String
+ * @return True if IDENTITY columns are supported; false otherwise.
*/
- public String getNullColumnString() {
- return "";
+ public boolean supportsIdentityColumns() {
+ return false;
}
/**
- * Does this dialect support identity column key generation?
+ * Does the dialect support some form of inserting and selecting
+ * the generated IDENTITY value all in the same statement.
*
- * @return boolean
+ * @return True if the dialect supports selecting the just
+ * generated IDENTITY in the insert statement.
*/
- public boolean supportsIdentityColumns() {
+ public boolean supportsInsertSelectIdentity() {
return false;
}
/**
- * Does this dialect support sequences?
+ * Whether this dialect have an Identity clause added to the data type or a
+ * completely seperate identity data type
*
* @return boolean
*/
- public boolean supportsSequences() {
- return false;
+ public boolean hasDataTypeInIdentityColumn() {
+ return true;
}
- public boolean supportsInsertSelectIdentity() {
- return false;
- }
-
/**
- * Append a clause to retrieve the generated identity value for the
- * given <tt>INSERT</tt> statement.
+ * Provided we {@link #supportsInsertSelectIdentity}, then attch the
+ * "select identity" clause to the insert statement.
+ * <p/>
+ * Note, if {@link #supportsInsertSelectIdentity} == false then
+ * the insert-string should be returned without modification.
+ *
+ * @param insertString The insert command
+ * @return The insert command with any necessary identity select
+ * clause attached.
*/
public String appendIdentitySelectToInsert(String insertString) {
return insertString;
}
- protected String getIdentitySelectString() throws MappingException {
- throw new MappingException( "Dialect does not support identity key generation" );
- }
-
/**
- * The syntax that returns the identity value of the last insert, if
- * identity column key generation is supported.
+ * Get the select command to use to retrieve the last generated IDENTITY
+ * value for a particuar table
*
- * @param type TODO
- * @throws MappingException if no native key generation
+ * @param table The table into which the insert was done
+ * @param column The PK column.
+ * @param type The {@link java.sql.Types} type code.
+ * @return The appropriate select command
+ * @throws MappingException If IDENTITY generation is not supported.
*/
- public String getIdentitySelectString(String table, String column, int type)
- throws MappingException {
+ public String getIdentitySelectString(String table, String column, int type) throws MappingException {
return getIdentitySelectString();
}
- protected String getIdentityColumnString() throws MappingException {
+ /**
+ * Get the select command to use to retrieve the last generated IDENTITY
+ * value.
+ *
+ * @return The appropriate select command
+ * @throws MappingException If IDENTITY generation is not supported.
+ */
+ protected String getIdentitySelectString() throws MappingException {
throw new MappingException( "Dialect does not support identity key generation" );
}
/**
- * The keyword used to specify an identity column, if identity
- * column key generation is supported.
+ * The syntax used during DDL to define a column as being an IDENTITY of
+ * a particular type.
*
- * @param type the SQL column type, as defined by <tt>java.sql.Types</tt>
- * @throws MappingException if no native key generation
+ * @param type The {@link java.sql.Types} type code.
+ * @return The appropriate DDL fragment.
+ * @throws MappingException If IDENTITY generation is not supported.
*/
public String getIdentityColumnString(int type) throws MappingException {
return getIdentityColumnString();
}
/**
+ * The syntax used during DDL to define a column as being an IDENTITY.
+ *
+ * @return The appropriate DDL fragment.
+ * @throws MappingException If IDENTITY generation is not supported.
+ */
+ protected String getIdentityColumnString() throws MappingException {
+ throw new MappingException( "Dialect does not support identity key generation" );
+ }
+
+ /**
* The keyword used to insert a generated value into an identity column (or null).
* Need if the dialect does not support inserts that specify no column values.
*
- * @return String
+ * @return The appropriate keyword.
*/
public String getIdentityInsertString() {
return null;
}
+
+ // SEQUENCE support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
/**
- * The keyword used to insert a row without specifying any column values.
- * This is not possible on some databases.
+ * Does this dialect support sequences?
+ *
+ * @return True if sequences supported; false otherwise.
*/
- public String getNoColumnsInsertString() {
- return "values ( )";
+ public boolean supportsSequences() {
+ return false;
}
/**
* Generate the appropriate select statement to to retreive the next value
- * of a sequence, if sequences are supported.
+ * of a sequence.
* <p/>
* This should be a "stand alone" select statement.
*
* @param sequenceName the name of the sequence
* @return String The "nextval" select string.
- * @throws MappingException if no sequences
+ * @throws MappingException If sequences are not supported.
*/
public String getSequenceNextValString(String sequenceName) throws MappingException {
throw new MappingException( "Dialect does not support sequences" );
@@ -572,322 +584,467 @@
/**
* Generate the select expression fragment that will retreive the next
- * value of a sequence, if sequences are supported.
+ * value of a sequence as part of another (typically DML) statement.
* <p/>
* This differs from {@link #getSequenceNextValString(String)} in that this
- * should return an expression usable within another select statement.
+ * should return an expression usable within another statement.
*
* @param sequenceName the name of the sequence
- * @return String
- * @throws MappingException if no sequences
+ * @return The "nextval" fragment.
+ * @throws MappingException If sequences are not supported.
*/
public String getSelectSequenceNextValString(String sequenceName) throws MappingException {
throw new MappingException( "Dialect does not support sequences" );
}
/**
- * The syntax used to create a sequence, if sequences are supported.
+ * The multiline script used to create a sequence.
*
- * @param sequenceName the name of the sequence
- * @return String
- * @throws MappingException if no sequences
+ * @param sequenceName The name of the sequence
+ * @return The sequence creation commands
+ * @throws MappingException If sequences are not supported.
*/
- protected String getCreateSequenceString(String sequenceName) throws MappingException {
- throw new MappingException( "Dialect does not support sequences" );
+ public String[] getCreateSequenceStrings(String sequenceName) throws MappingException {
+ return new String[] { getCreateSequenceString( sequenceName ) };
}
/**
- * The multiline script used to create a sequence, if sequences are supported.
+ * Typically dialects which support sequences can create a sequence
+ * with a single command. This is convenience form of
+ * {@link #getCreateSequenceStrings} to help facilitate that.
+ * <p/>
+ * Dialects which support sequences and can create a sequence in a
+ * single command need *only* override this method. Dialects
+ * which support sequences but require multiple commands to create
+ * a sequence should instead override {@link #getCreateSequenceStrings}.
*
- * @param sequenceName the name of the sequence
- * @return String[]
- * @throws MappingException if no sequences
+ * @param sequenceName The name of the sequence
+ * @return The sequence creation command
+ * @throws MappingException If sequences are not supported.
*/
- public String[] getCreateSequenceStrings(String sequenceName) throws MappingException {
- return new String[]{getCreateSequenceString( sequenceName )};
+ protected String getCreateSequenceString(String sequenceName) throws MappingException {
+ throw new MappingException( "Dialect does not support sequences" );
}
/**
- * The syntax used to drop a sequence, if sequences are supported.
+ * The multiline script used to drop a sequence.
*
- * @param sequenceName the name of the sequence
- * @return String
- * @throws MappingException if no sequences
+ * @param sequenceName The name of the sequence
+ * @return The sequence drop commands
+ * @throws MappingException If sequences are not supported.
*/
- protected String getDropSequenceString(String sequenceName) throws MappingException {
- throw new MappingException( "Dialect does not support sequences" );
+ public String[] getDropSequenceStrings(String sequenceName) throws MappingException {
+ return new String[]{getDropSequenceString( sequenceName )};
}
/**
- * The multiline script used to drop a sequence, if sequences are supported.
+ * Typically dialects which support sequences can drop a sequence
+ * with a single command. This is convenience form of
+ * {@link #getDropSequenceStrings} to help facilitate that.
+ * <p/>
+ * Dialects which support sequences and can drop a sequence in a
+ * single command need *only* override this method. Dialects
+ * which support sequences but require multiple commands to drop
+ * a sequence should instead override {@link #getDropSequenceStrings}.
*
- * @param sequenceName the name of the sequence
- * @return String[]
- * @throws MappingException if no sequences
+ * @param sequenceName The name of the sequence
+ * @return The sequence drop commands
+ * @throws MappingException If sequences are not supported.
*/
- public String[] getDropSequenceStrings(String sequenceName) throws MappingException {
- return new String[]{getDropSequenceString( sequenceName )};
+ protected String getDropSequenceString(String sequenceName) throws MappingException {
+ throw new MappingException( "Dialect does not support sequences" );
}
/**
- * A query used to find all sequences
+ * Get the select command used retrieve the names of all sequences.
*
+ * @return The select command; or null if sequences are not supported.
* @see org.hibernate.tool.hbm2ddl.SchemaUpdate
*/
public String getQuerySequencesString() {
return null;
}
+
+ // GUID support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
/**
- * Get the <tt>Dialect</tt> specified by the current <tt>System</tt> properties.
+ * Get the command used to select a GUID from the underlying database.
+ * <p/>
+ * Optional operation.
*
- * @return Dialect
- * @throws HibernateException
+ * @return The appropriate command.
*/
- public static Dialect getDialect() throws HibernateException {
- String dialectName = Environment.getProperties().getProperty( Environment.DIALECT );
- if ( dialectName == null ) throw new HibernateException( "The dialect was not set. Set the property hibernate.dialect." );
- try {
- return ( Dialect ) ReflectHelper.classForName( dialectName ).newInstance();
- }
- catch ( ClassNotFoundException cnfe ) {
- throw new HibernateException( "Dialect class not found: " + dialectName );
- }
- catch ( Exception e ) {
- throw new HibernateException( "Could not instantiate dialect class", e );
- }
+ public String getSelectGUIDString() {
+ throw new UnsupportedOperationException( "dialect does not support GUIDs" );
}
+ // limit/offset support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
/**
- * Get the <tt>Dialect</tt> specified by the given properties or system properties.
+ * Does this dialect support some form of limiting query results
+ * via a SQL clause?
*
- * @param props
- * @return Dialect
- * @throws HibernateException
+ * @return True if this dialect supports some form of LIMIT.
*/
- public static Dialect getDialect(Properties props) throws HibernateException {
- String dialectName = props.getProperty( Environment.DIALECT );
- if ( dialectName == null ) return getDialect();
- try {
- return ( Dialect ) ReflectHelper.classForName( dialectName ).newInstance();
- }
- catch ( ClassNotFoundException cnfe ) {
- throw new HibernateException( "Dialect class not found: " + dialectName );
- }
- catch ( Exception e ) {
- throw new HibernateException( "Could not instantiate dialect class", e );
- }
+ public boolean supportsLimit() {
+ return false;
}
/**
- * Retrieve a set of default Hibernate properties for this database.
+ * Does this dialect's LIMIT support (if any) additionally
+ * support specifying an offset?
*
- * @return a set of Hibernate properties
+ * @return True if the dialect supports an offset within the limit support.
*/
- public final Properties getDefaultProperties() {
- return properties;
+ public boolean supportsLimitOffset() {
+ return supportsLimit();
}
/**
- * Completely optional cascading drop clause
+ * Does this dialect support bind variables (i.e., prepared statememnt
+ * parameters) for its limit/offset?
*
- * @return String
+ * @return True if bind variables can be used; false otherwise.
*/
- public String getCascadeConstraintsString() {
- return "";
+ public boolean supportsVariableLimit() {
+ return supportsLimit();
}
/**
- * Create an <tt>OuterJoinGenerator</tt> for this dialect.
+ * ANSI SQL defines the LIMIT clause to be in the form LIMIT offset, limit.
+ * Does this dialect require us to bind the parameters in reverse order?
*
- * @return OuterJoinGenerator
+ * @return true if the correct order is limit, offset
*/
- public JoinFragment createOuterJoinFragment() {
- return new ANSIJoinFragment();
+ public boolean bindLimitParametersInReverseOrder() {
+ return false;
}
/**
- * Create a <tt>CaseFragment</tt> for this dialect.
+ * Does the <tt>LIMIT</tt> clause come at the start of the
+ * <tt>SELECT</tt> statement, rather than at the end?
*
- * @return OuterJoinGenerator
+ * @return true if limit parameters should come before other parameters
*/
- public CaseFragment createCaseFragment() {
- return new ANSICaseFragment();
+ public boolean bindLimitParametersFirst() {
+ return false;
}
/**
- * The name of the SQL function that transforms a string to
- * lowercase
+ * Does the <tt>LIMIT</tt> clause take a "maximum" row number instead
+ * of a total number of returned rows?
+ * <p/>
+ * This is easiest understood via an example. Consider you have a table
+ * with 20 rows, but you only want to retrieve rows number 11 through 20.
+ * Generally, a limit with offset would say that the offset = 11 and the
+ * limit = 10 (we only want 10 rows at a time); this is specifying the
+ * total number of returned rows. Some dialects require that we instead
+ * specify offset = 11 and limit = 20, where 20 is the "last" row we want
+ * relative to offset (i.e. total number of rows = 20 - 11 = 9)
+ * <p/>
+ * So essentially, is limit relative from offset? Or is limit absolute?
*
- * @return String
+ * @return True if limit is relative from offset; false otherwise.
*/
- public String getLowercaseFunction() {
- return "lower";
+ public boolean useMaxForLimit() {
+ return false;
}
/**
- * Does this <tt>Dialect</tt> have some kind of <tt>LIMIT</tt> syntax?
+ * Given a limit and an offset, apply the limit clause to the query.
+ *
+ * @param query The query to which to apply the limit.
+ * @param offset The offset of the limit
+ * @param limit The limit of the limit ;)
+ * @return The modified query statement with the limit applied.
*/
- public boolean supportsLimit() {
- return false;
+ public String getLimitString(String query, int offset, int limit) {
+ return getLimitString( query, offset > 0 );
}
/**
- * Does this dialect support an offset?
+ * Apply s limit clause to the query.
+ * <p/>
+ * Typically dialects utilize {@link #supportsVariableLimit() variable}
+ * limit caluses when they support limits. Thus, when building the
+ * select command we do not actually need to know the limit or the offest
+ * since we will just be using placeholders.
+ * <p/>
+ * Here we do still pass along whether or not an offset was specified
+ * so that dialects not supporting offsets can generate proper exceptions.
+ * In general, dialects will override one or the other of this method and
+ * {@link #getLimitString(String, int, int)}.
+ *
+ * @param query The query to which to apply the limit.
+ * @param hasOffset Is the query requesting an offset?
+ * @return the modified SQL
*/
- public boolean supportsLimitOffset() {
- return supportsLimit();
+ protected String getLimitString(String query, boolean hasOffset) {
+ throw new UnsupportedOperationException( "paged queries not supported" );
}
+
+ // lock acquisition support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
/**
- * Add a <tt>LIMIT</tt> clause to the given SQL <tt>SELECT</tt>
+ * Get a strategy instance which knows how to acquire a database-level lock
+ * of the specified mode for this dialect.
*
- * @return the modified SQL
+ * @param lockable The persister for the entity to be locked.
+ * @param lockMode The type of lock to be acquired.
+ * @return The appropriate locking strategy.
+ * @since 3.2
*/
- public String getLimitString(String querySelect, boolean hasOffset) {
- throw new UnsupportedOperationException( "paged queries not supported" );
+ public LockingStrategy getLockingStrategy(Lockable lockable, LockMode lockMode) {
+ return new SelectLockingStrategy( lockable, lockMode );
}
- public String getLimitString(String querySelect, int offset, int limit) {
- return getLimitString( querySelect, offset>0 );
+ /**
+ * Given a lock mode, determine the appropriate for update fragment to use.
+ *
+ * @param lockMode The lock mode to apply.
+ * @return The appropriate for update fragment.
+ */
+ public String getForUpdateString(LockMode lockMode) {
+ if ( lockMode==LockMode.UPGRADE ) {
+ return getForUpdateString();
+ }
+ else if ( lockMode==LockMode.UPGRADE_NOWAIT ) {
+ return getForUpdateNowaitString();
+ }
+ else if ( lockMode==LockMode.FORCE ) {
+ return getForUpdateNowaitString();
+ }
+ else {
+ return "";
+ }
}
- public boolean supportsVariableLimit() {
- return supportsLimit();
+ /**
+ * Get the string to append to SELECT statements to acquire locks
+ * for this dialect.
+ *
+ * @return The appropriate <tt>FOR UPDATE</tt> clause string.
+ */
+ public String getForUpdateString() {
+ return " for update";
}
/**
- * Does the <tt>LIMIT</tt> clause specify arguments in the "reverse" order
- * limit, offset instead of offset, limit?
+ * Is <tt>FOR UPDATE OF</tt> syntax supported?
*
- * @return true if the correct order is limit, offset
+ * @return True if the database supports <tt>FOR UPDATE OF</tt> syntax;
+ * false otherwise.
*/
- public boolean bindLimitParametersInReverseOrder() {
+ public boolean forUpdateOfColumns() {
+ // by default we report no support
return false;
}
/**
- * Does the <tt>LIMIT</tt> clause come at the start of the
- * <tt>SELECT</tt> statement, rather than at the end?
+ * Does this dialect support <tt>FOR UPDATE</tt> in conjunction with
+ * outer joined rows?
*
- * @return true if limit parameters should come before other parameters
+ * @return True if outer joined rows can be locked via <tt>FOR UPDATE</tt>.
*/
- public boolean bindLimitParametersFirst() {
- return false;
+ public boolean supportsOuterJoinForUpdate() {
+ return true;
}
/**
- * Does the <tt>LIMIT</tt> clause take a "maximum" row number instead
- * of a total number of returned rows?
+ * Get the <tt>FOR UPDATE OF column_list</tt> fragment appropriate for this
+ * dialect given the aliases of the columns to be write locked.
+ *
+ * @param aliases The columns to be write locked.
+ * @return The appropriate <tt>FOR UPDATE OF column_list</tt> clause string.
*/
- public boolean useMaxForLimit() {
- return false;
+ public String getForUpdateString(String aliases) {
+ // by default we simply return the getForUpdateString() result since
+ // the default is to say no support for "FOR UPDATE OF ..."
+ return getForUpdateString();
}
/**
- * The opening quote for a quoted identifier
+ * Retrieves the <tt>FOR UPDATE NOWAIT</tt> syntax specific to this dialect.
+ *
+ * @return The appropriate <tt>FOR UPDATE NOWAIT</tt> clause string.
*/
- public char openQuote() {
- return '"';
+ public String getForUpdateNowaitString() {
+ // by default we report no support for NOWAIT lock semantics
+ return getForUpdateString();
}
/**
- * The closing quote for a quoted identifier
+ * Get the <tt>FOR UPDATE OF column_list NOWAIT</tt> fragment appropriate
+ * for this dialect given the aliases of the columns to be write locked.
+ *
+ * @param aliases The columns to be write locked.
+ * @return The appropriate <tt>FOR UPDATE colunm_list NOWAIT</tt> clause string.
*/
- public char closeQuote() {
- return '"';
+ public String getForUpdateNowaitString(String aliases) {
+ return getForUpdateString( aliases );
}
/**
- * SQL functions as defined in general. The results of this
- * method should be integrated with the specialisation's data.
+ * Some dialects support an alternative means to <tt>SELECT FOR UPDATE</tt>,
+ * whereby a "lock hint" is appends to the table name in the from clause.
+ * <p/>
+ * contributed by <a href="http://sourceforge.net/users/heschulz">Helge Schulz</a>
+ *
+ * @param mode The lock mode to apply
+ * @param tableName The name of the table to which to apply the lock hint.
+ * @return The table with any required lock hints.
*/
- public final Map getFunctions() {
- return sqlFunctions;
+ public String appendLockHint(LockMode mode, String tableName) {
+ return tableName;
}
- public boolean supportsIfExistsBeforeTableName() {
+
+ // temporary table support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Does this dialect support temporary tables?
+ *
+ * @return True if temp tables are supported; false otherwise.
+ */
+ public boolean supportsTemporaryTables() {
return false;
}
- public boolean supportsIfExistsAfterTableName() {
- return false;
+ /**
+ * Generate a temporary table name given the bas table.
+ *
+ * @param baseTableName The table name from which to base the temp table name.
+ * @return The generated temp table name.
+ */
+ public String generateTemporaryTableName(String baseTableName) {
+ return "HT_" + baseTableName;
}
-
+
/**
- * Does this dialect support column-level check constraints?
+ * Command used to create a temporary table.
+ *
+ * @return The command used to create a temporary table.
*/
- public boolean supportsColumnCheck() {
- return true;
+ public String getCreateTemporaryTableString() {
+ return "create table";
}
-
+
/**
- * Does this dialect support table-level check constraints?
+ * Get any fragments needing to be postfixed to the command for
+ * temporary table creation.
+ *
+ * @return Any required postfix.
*/
- public boolean supportsTableCheck() {
- return true;
+ public String getCreateTemporaryTablePostfix() {
+ return "";
}
/**
- * Whether this dialect have an Identity clause added to the data type or a
- * completely seperate identity data type
+ * Does the dialect require that temporary table DDL statements
+ * occur in isolation from other statements?
+ * todo : perhaps have this return java.lang.Boolean instead where null means to use the value returned by the driver.
+ *
+ * @return
+ */
+ public boolean performTemporaryTableDDLInIsolation() {
+ return false;
+ }
+
+ /**
+ * Do we need to drop the temporary table after use?
*
- * @return boolean
+ * @return True if the table should be dropped.
*/
- public boolean hasDataTypeInIdentityColumn() {
+ public boolean dropTemporaryTableAfterUse() {
return true;
}
- public boolean supportsCascadeDelete() {
- return true;
+
+ // callable statement support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Registers an OUT parameter which will be returing a
+ * {@link java.sql.ResultSet}. How this is accomplished varies greatly
+ * from DB to DB, hence its inclusion (along with {@link #getResultSet}) here.
+ *
+ * @param statement The callable statement.
+ * @param position The bind position at which to register the OUT param.
+ * @return The number of (contiguous) bind positions used.
+ * @throws SQLException Indicates problems registering the OUT param.
+ */
+ public int registerResultSetOutParameter(CallableStatement statement, int position) throws SQLException {
+ throw new UnsupportedOperationException(
+ getClass().getName() +
+ " does not support resultsets via stored procedures"
+ );
}
/**
- * Method <code>appendLockHint</code> appends according to the given
- * lock mode a lock hint behind the given table name, if this dialect
- * needs this. MS SQL Server for example doesn't support the
- * standard "<code>select ... for update</code>" syntax and use a
- * special "<code>select ... from TABLE as ALIAS with (updlock, rowlock)
- * where ...</code>" syntax instead.
+ * Given a callable statement previously processed by {@link #registerResultSetOutParameter},
+ * extract the {@link java.sql.ResultSet} from the OUT parameter.
*
- * @param tableName name of table to append lock hint
- * @return String
- * <p/>
- * author <a href="http://sourceforge.net/users/heschulz">Helge Schulz</a>
+ * @param statement The callable statement.
+ * @return The extracted result set.
+ * @throws SQLException Indicates problems extracting the result set.
*/
- public String appendLockHint(LockMode mode, String tableName) {
- return tableName;
+ public ResultSet getResultSet(CallableStatement statement) throws SQLException {
+ throw new UnsupportedOperationException(
+ getClass().getName() +
+ " does not support resultsets via stored procedures"
+ );
}
- public Class getNativeIdentifierGeneratorClass() {
- if ( supportsIdentityColumns() ) {
- return IdentityGenerator.class;
- }
- else if ( supportsSequences() ) {
- return SequenceGenerator.class;
- }
- else {
- return TableHiLoGenerator.class;
- }
+ // current timestamp support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Does this dialect support a way to retrieve the database's current
+ * timestamp value?
+ *
+ * @return True if the current timestamp can be retrieved; false otherwise.
+ */
+ public boolean supportsCurrentTimestampSelection() {
+ return false;
}
- public String getSelectGUIDString() {
- throw new UnsupportedOperationException( "dialect does not support GUIDs" );
+ /**
+ * Should the value returned by {@link #getCurrentTimestampSelectString}
+ * be treated as callable. Typically this indicates that JDBC escape
+ * sytnax is being used...
+ *
+ * @return True if the {@link #getCurrentTimestampSelectString} return
+ * is callable; false otherwise.
+ */
+ public boolean isCurrentTimestampSelectStringCallable() {
+ throw new UnsupportedOperationException( "Database not known to define a current timestamp function" );
}
- public boolean supportsOuterJoinForUpdate() {
- return true;
+ /**
+ * Retrieve the command used to retrieve the current timestammp from the
+ * database.
+ *
+ * @return The command.
+ */
+ public String getCurrentTimestampSelectString() {
+ throw new UnsupportedOperationException( "Database not known to define a current timestamp function" );
}
- public String getSelectClauseNullString(int sqlType) {
- return "null";
+ /**
+ * The name of the database-specific SQL function for retrieving the
+ * current timestamp.
+ *
+ * @return The function name.
+ */
+ public String getCurrentTimestampSQLFunctionName() {
+ // the standard SQL function name is current_timestamp...
+ return "current_timestamp";
}
-
- public boolean supportsNotNullUnique() {
- return true;
- }
+
+ // SQLException support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
/**
* Build an instance of the SQLExceptionConverter preferred by this dialect for
* converting SQLExceptions into Hibernate's JDBCException hierarchy. The default
@@ -917,167 +1074,357 @@
return EXTRACTER;
}
- public final String quote(String column) {
- if ( column.charAt( 0 ) == '`' ) {
- return openQuote() + column.substring( 1, column.length() - 1 ) + closeQuote();
- }
- else {
- return column;
- }
+
+ // union subclass support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Given a {@link java.sql.Types} type code, determine an appropriate
+ * null value to use in a select clause.
+ * <p/>
+ * One thing to consider here is that certain databases might
+ * require proper casting for the nulls here since the select here
+ * will be part of a UNION/UNION ALL.
+ *
+ * @param sqlType The {@link java.sql.Types} type code.
+ * @return The appropriate select clause value fragment.
+ */
+ public String getSelectClauseNullString(int sqlType) {
+ return "null";
}
- public boolean hasSelfReferentialForeignKeyBug() {
+ /**
+ * Does this dialect support UNION ALL, which is generally a faster
+ * variant of UNION?
+ *
+ * @return True if UNION ALL is supported; false otherwise.
+ */
+ public boolean supportsUnionAll() {
return false;
}
-
- public boolean useInputStreamToInsertBlob() {
- return true;
+
+ // miscellaneous support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+ /**
+ * Create a {@link org.hibernate.sql.JoinFragment} strategy responsible
+ * for handling this dialect's variations in how joins are handled.
+ *
+ * @return This dialect's {@link org.hibernate.sql.JoinFragment} strategy.
+ */
+ public JoinFragment createOuterJoinFragment() {
+ return new ANSIJoinFragment();
}
- public int registerResultSetOutParameter(CallableStatement statement, int col) throws SQLException {
- throw new UnsupportedOperationException(
- getClass().getName() +
- " does not support resultsets via stored procedures"
- );
+ /**
+ * Create a {@link org.hibernate.sql.CaseFragment} strategy responsible
+ * for handling this dialect's variations in how CASE statements are
+ * handled.
+ *
+ * @return This dialect's {@link org.hibernate.sql.CaseFragment} strategy.
+ */
+ public CaseFragment createCaseFragment() {
+ return new ANSICaseFragment();
}
- public ResultSet getResultSet(CallableStatement ps) throws SQLException {
- throw new UnsupportedOperationException(
- getClass().getName() +
- " does not support resultsets via stored procedures"
- );
+ /**
+ * The fragment used to insert a row without specifying any column values.
+ * This is not possible on some databases.
+ *
+ * @return The appropriate empty values clause.
+ */
+ public String getNoColumnsInsertString() {
+ return "values ( )";
}
-
- public boolean supportsUnionAll() {
- return false;
+
+ /**
+ * The name of the SQL function that transforms a string to
+ * lowercase
+ *
+ * @return The dialect-specific lowercase function.
+ */
+ public String getLowercaseFunction() {
+ return "lower";
}
-
- public boolean supportsCommentOn() {
- return false;
+
+ /**
+ * Should LOBs (both BLOB and CLOB) be bound using stream operations (i.e.
+ * {@link java.sql.PreparedStatement#setBinaryStream}).
+ *
+ * @return True if BLOBs and CLOBs should be bound using stream operations.
+ */
+ public boolean useInputStreamToInsertBlob() {
+ return true;
}
-
- public String getTableComment(String comment) {
- return "";
- }
- public String getColumnComment(String comment) {
- return "";
- }
-
+ /**
+ * Meant as a means for end users to affect the select strings being sent
+ * to the database and perhaps manipulate them in some fashion.
+ * <p/>
+ * The recommend approach is to instead use
+ * {@link org.hibernate.Interceptor#onPrepareStatement(String)}.
+ *
+ * @param select The select command
+ * @return The mutated select command, or the same as was passed in.
+ */
public String transformSelectString(String select) {
return select;
}
- public boolean supportsTemporaryTables() {
- return false;
+ /**
+ * What is the maximum length Hibernate can use for generated aliases?
+ *
+ * @return The maximum length.
+ */
+ public int getMaxAliasLength() {
+ return 10;
}
- public String generateTemporaryTableName(String baseTableName) {
- return "HT_" + baseTableName;
+ /**
+ * The SQL literal value to which this database maps boolean values.
+ *
+ * @param bool The boolean value
+ * @return The appropriate SQL literal.
+ */
+ public String toBooleanValueString(boolean bool) {
+ return bool ? "1" : "0";
}
- public String getCreateTemporaryTableString() {
- return "create table";
+ /**
+ * Does this dialect support parameters within the select clause of
+ * INSERT ... SELECT ... statements?
+ *
+ * @return True if this is supported; false otherwise.
+ */
+ public boolean supportsParametersInInsertSelect() {
+ return true;
}
- public boolean performTemporaryTableDDLInIsolation() {
+ /**
+ * Is this dialect known to support what ANSI-SQL terms "row value
+ * constructor" syntax; sometimes called tuple syntax.
+ * <p/>
+ * Basically, does it support syntax like
+ * "... where (FIRST_NAME, LAST_NAME) = ('Steve', 'Ebersole') ...".
+ *
+ * @return True if this SQL dialect is known to support "row value
+ * constructor" syntax; false otherwise.
+ * @since 3.2
+ */
+ public boolean supportsRowValueConstructorSyntax() {
+ // return false here, as most databases do not properly support this construct...
return false;
}
- public String getCreateTemporaryTablePostfix() {
- return "";
+
+ // identifier quoting support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * The character specific to this dialect used to begin a quoted identifier.
+ *
+ * @return The dialect's specific open quote character.
+ */
+ public char openQuote() {
+ return '"';
}
- public boolean dropTemporaryTableAfterUse() {
- return true;
+ /**
+ * The character specific to this dialect used to close a quoted identifier.
+ *
+ * @return The dialect's specific close quote character.
+ */
+ public char closeQuote() {
+ return '"';
}
- public String getForUpdateString(LockMode lockMode) {
- if ( lockMode==LockMode.UPGRADE ) {
- return getForUpdateString();
+ /**
+ * Apply dialect-specific quoting.
+ * <p/>
+ * By default, the incoming value is checked to see if its first character
+ * is the back-tick (`). If so, the dialect specific quoting is applied.
+ *
+ * @param column The value to be quoted.
+ * @return The quoted (or unmodified, if not starting with back-tick) value.
+ * @see #openQuote()
+ * @see #closeQuote()
+ */
+ public final String quote(String column) {
+ if ( column.charAt( 0 ) == '`' ) {
+ return openQuote() + column.substring( 1, column.length() - 1 ) + closeQuote();
}
- else if ( lockMode==LockMode.UPGRADE_NOWAIT ) {
- return getForUpdateNowaitString();
- }
- else if ( lockMode==LockMode.FORCE ) {
- return getForUpdateNowaitString();
- }
else {
- return "";
+ return column;
}
}
-
- public int getMaxAliasLength() {
- return 10;
+
+
+ // DDL support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Does this dialect support the <tt>ALTER TABLE</tt> syntax?
+ *
+ * @return True if we support altering of tables; false otherwise.
+ */
+ public boolean hasAlterTable() {
+ return true;
}
- public boolean supportsCurrentTimestampSelection() {
- return false;
+ /**
+ * Do we need to drop constraints before dropping tables in this dialect?
+ *
+ * @return True if constraints must be dropped prior to dropping
+ * the table; false otherwise.
+ */
+ public boolean dropConstraints() {
+ return true;
}
- public String getCurrentTimestampSelectString() {
- throw new UnsupportedOperationException( "Database not known to define a current timestamp function" );
+ /**
+ * Do we need to qualify index names with the schema name?
+ *
+ * @return boolean
+ */
+ public boolean qualifyIndexName() {
+ return true;
}
- public boolean isCurrentTimestampSelectStringCallable() {
- throw new UnsupportedOperationException( "Database not known to define a current timestamp function" );
+ /**
+ * Does this dialect support the <tt>UNIQUE</tt> column syntax?
+ *
+ * @return boolean
+ */
+ public boolean supportsUnique() {
+ return true;
}
+ /**
+ * Does this dialect support adding Unique constraints via create and alter table ?
+ * @return boolean
+ */
+ public boolean supportsUniqueConstraintInCreateAlterTable() {
+ return true;
+ }
+
/**
- * The SQL value that the JDBC driver maps boolean values to
+ * The syntax used to add a column to a table (optional).
*/
- public String toBooleanValueString(boolean bool) {
- return bool ? "1" : "0";
+ public String getAddColumnString() {
+ throw new UnsupportedOperationException( "No add column syntax supported by Dialect" );
}
+ public String getDropForeignKeyString() {
+ return " drop constraint ";
+ }
+
+ public String getTableTypeString() {
+ // grrr... for differentiation of mysql storage engines
+ return "";
+ }
+
/**
- * Does this dialect support parameters within the select clause of
- * INSERT ... SELECT ... statements?
+ * The syntax used to add a foreign key constraint to a table.
+ *
+ * @param referencesPrimaryKey if false, constraint should be
+ * explicit about which column names the constraint refers to
*
- * @return True if this is supported; false otherwise.
+ * @return String
*/
- public boolean supportsParametersInInsertSelect() {
- return true;
+ public String getAddForeignKeyConstraintString(
+ String constraintName,
+ String[] foreignKey,
+ String referencedTable,
+ String[] primaryKey,
+ boolean referencesPrimaryKey
+ ) {
+ StringBuffer res = new StringBuffer( 30 );
+
+ res.append( " add constraint " )
+ .append( constraintName )
+ .append( " foreign key (" )
+ .append( StringHelper.join( ", ", foreignKey ) )
+ .append( ") references " )
+ .append( referencedTable );
+
+ if(!referencesPrimaryKey) {
+ res.append(" (")
+ .append( StringHelper.join(", ", primaryKey) )
+ .append(')');
+ }
+
+ return res.toString();
}
/**
- * The name of the database-specific SQL function for retrieving the
- * current timestamp.
+ * The syntax used to add a primary key constraint to a table.
*
- * @return The function name.
+ * @return String
*/
- public String getCurrentTimestampSQLFunctionName() {
- // the standard SQL function name is current_timestamp...
- return "current_timestamp";
+ public String getAddPrimaryKeyConstraintString(String constraintName) {
+ return " add constraint " + constraintName + " primary key ";
}
+ public boolean hasSelfReferentialForeignKeyBug() {
+ return false;
+ }
+
/**
- * Get a strategy instance which knows how to acquire a database-level lock
- * of the specified mode for this dialect.
+ * The keyword used to specify a nullable column.
*
- * @param lockable The persister for the entity to be locked.
- * @param lockMode The type of lock to be acquired.
- * @return The appropriate locking strategy.
- * @since 3.2
+ * @return String
*/
- public LockingStrategy getLockingStrategy(Lockable lockable, LockMode lockMode) {
- return new SelectLockingStrategy( lockable, lockMode );
+ public String getNullColumnString() {
+ return "";
}
+ public boolean supportsCommentOn() {
+ return false;
+ }
+
+ public String getTableComment(String comment) {
+ return "";
+ }
+
+ public String getColumnComment(String comment) {
+ return "";
+ }
+
+ public boolean supportsIfExistsBeforeTableName() {
+ return false;
+ }
+
+ public boolean supportsIfExistsAfterTableName() {
+ return false;
+ }
+
/**
- * Is this dialect known to support what ANSI-SQL terms "row value
- * constructor" syntax; sometimes called tuple syntax.
- * <p/>
- * Basically, does it support syntax like
- * "... where (FIRST_NAME, LAST_NAME) = ('Steve', 'Ebersole') ...".
+ * Does this dialect support column-level check constraints?
+ */
+ public boolean supportsColumnCheck() {
+ return true;
+ }
+
+ /**
+ * Does this dialect support table-level check constraints?
+ */
+ public boolean supportsTableCheck() {
+ return true;
+ }
+
+ public boolean supportsCascadeDelete() {
+ return true;
+ }
+
+ public boolean supportsNotNullUnique() {
+ return true;
+ }
+
+ /**
+ * Completely optional cascading drop clause
*
- * @return True if this SQL dialect is known to support "row value
- * constructor" syntax; false otherwise.
- * @since 3.2
+ * @return String
*/
- public boolean supportsRowValueConstructorSyntax() {
- // return false here, as most databases do not properly support this construct...
- return false;
+ public String getCascadeConstraintsString() {
+ return "";
}
+
}
Added: branches/Branch_3_2/Hibernate3/src/org/hibernate/dialect/function/ConditionalParenthesisFunction.java
===================================================================
--- branches/Branch_3_2/Hibernate3/src/org/hibernate/dialect/function/ConditionalParenthesisFunction.java 2006-11-08 16:54:55 UTC (rev 10774)
+++ branches/Branch_3_2/Hibernate3/src/org/hibernate/dialect/function/ConditionalParenthesisFunction.java 2006-11-08 16:56:52 UTC (rev 10775)
@@ -0,0 +1,45 @@
+//$Id: ConditionalParenthesisFunction.java,v 1.4 2005/04/26 18:08:01 oneovthafew Exp $
+package org.hibernate.dialect.function;
+
+import java.util.List;
+
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.type.Type;
+
+/**
+ * Essentially the same as {@link org.hibernate.dialect.function.StandardSQLFunction},
+ * except that here the parentheses are not included when no arguments are given.
+ *
+ * @author Jonathan Levinson
+ */
+public class ConditionalParenthesisFunction extends StandardSQLFunction {
+
+ public ConditionalParenthesisFunction(String name) {
+ super( name );
+ }
+
+ public ConditionalParenthesisFunction(String name, Type type) {
+ super( name, type );
+ }
+
+ public boolean hasParenthesesIfNoArguments() {
+ return false;
+ }
+
+ public String render(List args, SessionFactoryImplementor factory) {
+ final boolean hasArgs = !args.isEmpty();
+ StringBuffer buf = new StringBuffer();
+ buf.append( getName() );
+ if ( hasArgs ) {
+ buf.append( "(" );
+ for ( int i = 0; i < args.size(); i++ ) {
+ buf.append( args.get( i ) );
+ if ( i < args.size() - 1 ) {
+ buf.append( ", " );
+ }
+ }
+ buf.append( ")" );
+ }
+ return buf.toString();
+ }
+}
Added: branches/Branch_3_2/Hibernate3/src/org/hibernate/dialect/function/ConvertFunction.java
===================================================================
--- branches/Branch_3_2/Hibernate3/src/org/hibernate/dialect/function/ConvertFunction.java 2006-11-08 16:54:55 UTC (rev 10774)
+++ branches/Branch_3_2/Hibernate3/src/org/hibernate/dialect/function/ConvertFunction.java 2006-11-08 16:56:52 UTC (rev 10775)
@@ -0,0 +1,45 @@
+//$Id: CastFunction.java 7368 2005-07-04 02:54:27Z oneovthafew $
+package org.hibernate.dialect.function;
+
+import java.util.List;
+
+import org.hibernate.QueryException;
+import org.hibernate.Hibernate;
+import org.hibernate.engine.Mapping;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.type.Type;
+
+/**
+ * A Caché defintion of a convert function.
+ *
+ * @author Jonathan Levinson
+ */
+public class ConvertFunction implements SQLFunction {
+
+ public Type getReturnType(Type columnType, Mapping mapping) throws QueryException {
+ return Hibernate.STRING;
+ }
+
+ public boolean hasArguments() {
+ return true;
+ }
+
+ public boolean hasParenthesesIfNoArguments() {
+ return true;
+ }
+
+ public String render(List args, SessionFactoryImplementor factory) throws QueryException {
+ if ( args.size() != 2 && args.size() != 3 ) {
+ throw new QueryException( "convert() requires two or three arguments" );
+ }
+ String type = ( String ) args.get( 1 );
+
+ if ( args.size() == 2 ) {
+ return "{fn convert(" + args.get( 0 ) + " , " + type + ")}";
+ }
+ else {
+ return "convert(" + args.get( 0 ) + " , " + type + "," + args.get( 2 ) + ")";
+ }
+ }
+
+}
Modified: branches/Branch_3_2/Hibernate3/src/org/hibernate/dialect/function/SQLFunction.java
===================================================================
--- branches/Branch_3_2/Hibernate3/src/org/hibernate/dialect/function/SQLFunction.java 2006-11-08 16:54:55 UTC (rev 10774)
+++ branches/Branch_3_2/Hibernate3/src/org/hibernate/dialect/function/SQLFunction.java 2006-11-08 16:56:52 UTC (rev 10775)
@@ -20,20 +20,39 @@
*/
public interface SQLFunction {
/**
- * The function return type
+ * The return type of the function. May be either a concrete type which
+ * is preset, or variable depending upon the type of the first function
+ * argument.
+ *
* @param columnType the type of the first argument
+ * @param mapping The mapping source.
+ * @return The type to be expected as a return.
+ * @throws org.hibernate.QueryException Indicates an issue resolving the return type.
*/
public Type getReturnType(Type columnType, Mapping mapping) throws QueryException;
+
/**
* Does this function have any arguments?
+ *
+ * @return True if the function expects to have parameters; false otherwise.
*/
public boolean hasArguments();
+
/**
* If there are no arguments, are parens required?
+ *
+ * @return True if a no-arg call of this function requires parentheses.
*/
public boolean hasParenthesesIfNoArguments();
+
/**
- * Render the function call as SQL
+ * Render the function call as SQL fragment.
+ *
+ * @param args The function arguments
+ * @param factory The SessionFactory
+ * @return The rendered function call
+ * @throws org.hibernate.QueryException Indicates a problem rendering the
+ * function call.
*/
public String render(List args, SessionFactoryImplementor factory) throws QueryException;
}
Added: branches/Branch_3_2/Hibernate3/src/org/hibernate/dialect/function/StandardJDBCEscapeFunction.java
===================================================================
--- branches/Branch_3_2/Hibernate3/src/org/hibernate/dialect/function/StandardJDBCEscapeFunction.java 2006-11-08 16:54:55 UTC (rev 10774)
+++ branches/Branch_3_2/Hibernate3/src/org/hibernate/dialect/function/StandardJDBCEscapeFunction.java 2006-11-08 16:56:52 UTC (rev 10775)
@@ -0,0 +1,31 @@
+package org.hibernate.dialect.function;
+
+import java.util.List;
+
+import org.hibernate.type.Type;
+import org.hibernate.engine.SessionFactoryImplementor;
+
+/**
+ * Analogous to {@link org.hibernate.dialect.function.StandardSQLFunction}
+ * except that standard JDBC escape sequences (i.e. {fn blah}) are used when
+ * rendering the SQL.
+ *
+ * @author Steve Ebersole
+ */
+public class StandardJDBCEscapeFunction extends StandardSQLFunction {
+ public StandardJDBCEscapeFunction(String name) {
+ super( name );
+ }
+
+ public StandardJDBCEscapeFunction(String name, Type typeValue) {
+ super( name, typeValue );
+ }
+
+ public String render(List args, SessionFactoryImplementor factory) {
+ return "{fn " + super.render( args, factory ) + "}";
+ }
+
+ public String toString() {
+ return "{fn " + getName() + "...}";
+ }
+}
Modified: branches/Branch_3_2/Hibernate3/src/org/hibernate/dialect/function/StandardSQLFunction.java
===================================================================
--- branches/Branch_3_2/Hibernate3/src/org/hibernate/dialect/function/StandardSQLFunction.java 2006-11-08 16:54:55 UTC (rev 10774)
+++ branches/Branch_3_2/Hibernate3/src/org/hibernate/dialect/function/StandardSQLFunction.java 2006-11-08 16:56:52 UTC (rev 10775)
@@ -16,41 +16,91 @@
* @author David Channon
*/
public class StandardSQLFunction implements SQLFunction {
- private Type returnType = null;
- private String name;
-
+ private final String name;
+ private final Type type;
+
+ /**
+ * Construct a standard SQL function definition with a variable return type;
+ * the actual return type will depend on the types to which the function
+ * is applied.
+ * <p/>
+ * Using this form, the return type is considered non-static and assumed
+ * to be the type of the first argument.
+ *
+ * @param name The name of the function.
+ */
public StandardSQLFunction(String name) {
- this.name = name;
+ this( name, null );
}
-
- public StandardSQLFunction(String name, Type typeValue) {
- returnType = typeValue;
+
+ /**
+ * Construct a standard SQL function definition with a static return type.
+ *
+ * @param name The name of the function.
+ * @param type The static return type.
+ */
+ public StandardSQLFunction(String name, Type type) {
this.name = name;
+ this.type = type;
}
-
+
+ /**
+ * Function name accessor
+ *
+ * @return The function name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Function static return type accessor.
+ *
+ * @return The static function return type; or null if return type is
+ * not static.
+ */
+ public Type getType() {
+ return type;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
public Type getReturnType(Type columnType, Mapping mapping) {
- return returnType == null ? columnType : returnType;
+ // return the concrete type, or the underlying type if a concrete type
+ // was not specified
+ return type == null ? columnType : type;
}
-
+
+ /**
+ * {@inheritDoc}
+ */
public boolean hasArguments() {
return true;
}
-
+
+ /**
+ * {@inheritDoc}
+ */
public boolean hasParenthesesIfNoArguments() {
return true;
}
-
+
+ /**
+ * {@inheritDoc}
+ */
public String render(List args, SessionFactoryImplementor factory) {
StringBuffer buf = new StringBuffer();
- buf.append(name)
- .append('(');
- for ( int i=0; i<args.size(); i++ ) {
- buf.append( args.get(i) );
- if ( i<args.size()-1 ) buf.append(", ");
+ buf.append( name ).append( '(' );
+ for ( int i = 0; i < args.size(); i++ ) {
+ buf.append( args.get( i ) );
+ if ( i < args.size() - 1 ) {
+ buf.append( ", " );
+ }
}
- return buf.append(')').toString();
+ return buf.append( ')' ).toString();
}
-
+
public String toString() {
return name;
}
Added: branches/Branch_3_2/Hibernate3/src/org/hibernate/exception/CacheSQLStateConverter.java
===================================================================
--- branches/Branch_3_2/Hibernate3/src/org/hibernate/exception/CacheSQLStateConverter.java 2006-11-08 16:54:55 UTC (rev 10774)
+++ branches/Branch_3_2/Hibernate3/src/org/hibernate/exception/CacheSQLStateConverter.java 2006-11-08 16:56:52 UTC (rev 10775)
@@ -0,0 +1,93 @@
+// $Id: $
+package org.hibernate.exception;
+
+import org.hibernate.JDBCException;
+
+import java.sql.SQLException;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A SQLExceptionConverter implementation specific to Caché SQL,
+ * accounting for its custom integrity constraint violation error codes.
+ *
+ * @author Jonathan Levinson
+ */
+public class CacheSQLStateConverter implements SQLExceptionConverter {
+
+ private ViolatedConstraintNameExtracter extracter;
+
+ private static final Set SQL_GRAMMAR_CATEGORIES = new HashSet();
+ private static final Set DATA_CATEGORIES = new HashSet();
+ private static final Set INTEGRITY_VIOLATION_CATEGORIES = new HashSet();
+ private static final Set CONNECTION_CATEGORIES = new HashSet();
+
+ static {
+ SQL_GRAMMAR_CATEGORIES.add( "07" );
+ SQL_GRAMMAR_CATEGORIES.add( "37" );
+ SQL_GRAMMAR_CATEGORIES.add( "42" );
+ SQL_GRAMMAR_CATEGORIES.add( "65" );
+ SQL_GRAMMAR_CATEGORIES.add( "S0" );
+ SQL_GRAMMAR_CATEGORIES.add( "20" );
+
+ DATA_CATEGORIES.add( "22" );
+ DATA_CATEGORIES.add( "21" );
+ DATA_CATEGORIES.add( "02" );
+
+ INTEGRITY_VIOLATION_CATEGORIES.add( new Integer( 119 ) );
+ INTEGRITY_VIOLATION_CATEGORIES.add( new Integer( 120 ) );
+ INTEGRITY_VIOLATION_CATEGORIES.add( new Integer( 121 ) );
+ INTEGRITY_VIOLATION_CATEGORIES.add( new Integer( 122 ) );
+ INTEGRITY_VIOLATION_CATEGORIES.add( new Integer( 123 ) );
+ INTEGRITY_VIOLATION_CATEGORIES.add( new Integer( 124 ) );
+ INTEGRITY_VIOLATION_CATEGORIES.add( new Integer( 125 ) );
+ INTEGRITY_VIOLATION_CATEGORIES.add( new Integer( 127 ) );
+
+ CONNECTION_CATEGORIES.add( "08" );
+ }
+
+ public CacheSQLStateConverter(ViolatedConstraintNameExtracter extracter) {
+ this.extracter = extracter;
+ }
+
+ /**
+ * Convert the given SQLException into Hibernate's JDBCException hierarchy.
+ *
+ * @param sqlException The SQLException to be converted.
+ * @param message An optional error message.
+ * @param sql Optionally, the sql being performed when the exception occurred.
+ * @return The resulting JDBCException.
+ */
+ public JDBCException convert(SQLException sqlException, String message, String sql) {
+ String sqlStateClassCode = JDBCExceptionHelper.extractSqlStateClassCode( sqlException );
+ Integer errorCode = new Integer( JDBCExceptionHelper.extractErrorCode( sqlException ) );
+ if ( sqlStateClassCode != null ) {
+ if ( SQL_GRAMMAR_CATEGORIES.contains( sqlStateClassCode ) ) {
+ return new SQLGrammarException( message, sqlException, sql );
+ }
+ else if ( INTEGRITY_VIOLATION_CATEGORIES.contains( errorCode ) ) {
+ String constraintName = extracter.extractConstraintName( sqlException );
+ return new ConstraintViolationException( message, sqlException, sql, constraintName );
+ }
+ else if ( CONNECTION_CATEGORIES.contains( sqlStateClassCode ) ) {
+ return new JDBCConnectionException( message, sqlException, sql );
+ }
+ else if ( DATA_CATEGORIES.contains( sqlStateClassCode ) ) {
+ return new DataException( message, sqlException, sql );
+ }
+ }
+ return handledNonSpecificException( sqlException, message, sql );
+ }
+
+ /**
+ * Handle an exception not converted to a specific type based on the SQLState.
+ *
+ * @param sqlException The exception to be handled.
+ * @param message An optional message
+ * @param sql Optionally, the sql being performed when the exception occurred.
+ * @return The converted exception; should <b>never</b> be null.
+ */
+ protected JDBCException handledNonSpecificException(SQLException sqlException, String message, String sql) {
+ return new GenericJDBCException( message, sqlException, sql );
+ }
+}
Added: branches/Branch_3_2/Hibernate3/src/org/hibernate/sql/CacheJoinFragment.java
===================================================================
--- branches/Branch_3_2/Hibernate3/src/org/hibernate/sql/CacheJoinFragment.java 2006-11-08 16:54:55 UTC (rev 10774)
+++ branches/Branch_3_2/Hibernate3/src/org/hibernate/sql/CacheJoinFragment.java 2006-11-08 16:56:52 UTC (rev 10775)
@@ -0,0 +1,22 @@
+//$Id: $
+package org.hibernate.sql;
+
+import org.hibernate.AssertionFailure;
+
+/**
+ * A Caché dialect join. Differs from ANSI only in that full outer join
+ * is not supported.
+ *
+ * @author Jeff Miller
+ * @author Jonathan Levinson
+ */
+public class CacheJoinFragment extends ANSIJoinFragment {
+
+ public void addJoin(String tableName, String alias, String[] fkColumns, String[] pkColumns, int joinType, String on) {
+ if ( joinType == FULL_JOIN ) {
+ throw new AssertionFailure( "Cache does not support full outer joins" );
+ }
+ super.addJoin( tableName, alias, fkColumns, pkColumns, joinType, on );
+ }
+
+}
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-08 16:54:55 UTC (rev 10774)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/TestCase.java 2006-11-08 16:56:52 UTC (rev 10775)
@@ -29,6 +29,7 @@
import org.hibernate.dialect.SybaseDialect;
import org.hibernate.dialect.TimesTenDialect;
import org.hibernate.dialect.DerbyDialect;
+import org.hibernate.dialect.Cache71Dialect;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.PersistentClass;
@@ -386,11 +387,14 @@
/**
* Intended to indicate that this test class as a whole is intended for
- * a dialect or series of dialects. Skips here (appliesTo = false), therefore
+ * a dialect or series of dialects. Skips here (appliesTo = false) therefore
* simply indicate that the given tests target a particular feature of the
- * current database...
+ * checked database and none of the tests on this class should be run for the
+ * checked dialect.
*
- * @param dialect
+ * @param dialect The dialect to be checked.
+ * @return True if all the tests on this class apply to the given dialect (and
+ * therefore should be run); false otherwise.
*/
public boolean appliesTo(Dialect dialect) {
return true;
@@ -490,7 +494,8 @@
SQLServerDialect.class,
SybaseDialect.class,
PostgreSQLDialect.class,
- TimesTenDialect.class
+ TimesTenDialect.class,
+ Cache71Dialect.class
}
);
@@ -508,7 +513,7 @@
protected boolean dialectIsCaseSensitive(String testDescription) {
// MySQL and SQLServer is case insensitive on strings (at least in default installation)
boolean canDoIt = dialectIsNot(
- new Class[] { MySQLDialect.class, SQLServerDialect.class }
+ new Class[] { MySQLDialect.class, SQLServerDialect.class, Cache71Dialect.class }
);
if ( !canDoIt ) {
@@ -518,12 +523,13 @@
}
protected boolean supportsRowValueConstructorSyntaxInInList() {
- boolean supported = ! (
- getDialect() instanceof HSQLDialect ||
+ // this is a bit more lenient than Dialect.supportsRowValueConstructorSyntax() check
+ boolean supported = ! (getDialect() instanceof HSQLDialect ||
getDialect() instanceof PostgreSQLDialect ||
getDialect() instanceof MySQLDialect ||
getDialect() instanceof DB2Dialect ||
- getDialect() instanceof SybaseDialect
+ getDialect() instanceof SybaseDialect ||
+ getDialect() instanceof Cache71Dialect
);
if ( !supported ) {
@@ -533,7 +539,7 @@
}
protected boolean supportsResultSetPositionQueryMethodsOnForwardOnlyCursor() {
- if ( getDialect() instanceof SQLServerDialect ) {
+ if ( getDialect() instanceof SQLServerDialect || getDialect() instanceof Cache71Dialect ) {
reportSkip( "Driver does not support 'position query' methods on forward-only cursors", "query support" );
return false;
}
Modified: branches/Branch_3_2/Hibernate3/test/org/hibernate/test/hql/FooBarCopy.hbm.xml
===================================================================
--- branches/Branch_3_2/Hibernate3/test/org/hibernate/test/hql/FooBarCopy.hbm.xml 2006-11-08 16:54:55 UTC (rev 10774)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/hql/FooBarCopy.hbm.xml 2006-11-08 16:56:52 UTC (rev 10775)
@@ -23,7 +23,7 @@
</generator>
</id>
- <discriminator column="`^foo_subclass$1234`" type="character" force="true"/>
+ <discriminator column="`foo_subclass_1234`" type="character" force="true"/>
<version name="version"/>
<many-to-one name="foo" class="Foo">
@@ -34,7 +34,7 @@
<column name="long_" index="fbmtoidx" unique-key="abc" not-null="true"/>
</property>
<property name="integer">
- <column name="`@@##integer_*`" unique-key="abc" not-null="true"/>
+ <column name="`integer__`" unique-key="abc" not-null="true"/>
</property>
<property name="float">
<column name="float_" unique-key="abc" not-null="true" check="float_ > 0.0"/>
@@ -47,7 +47,7 @@
<index column="i"/>
<element column="byte_" type="byte"/>
</primitive-array>
-
+
<property name="date" type="date" column="date_"/>
<property name="timestamp" type="timestamp" column="timestamp_"/>
<property name="boolean" column="boolean_"/>
@@ -65,7 +65,7 @@
<property name="blob" type="org.hibernate.test.legacy.Foo$Struct" column="blobb_"/>
<property name="nullBlob" type="serializable"/>
<property name="binary" column="bin_"/>
- <property name="theLocale" access="field" column="`localeayzabc123!@#$`"/>
+ <property name="theLocale" access="field" column="`localeayzabc123`"/>
<property name="formula" formula="int_/2"/>
Modified: branches/Branch_3_2/Hibernate3/test/org/hibernate/test/legacy/Baz.hbm.xml
===================================================================
--- branches/Branch_3_2/Hibernate3/test/org/hibernate/test/legacy/Baz.hbm.xml 2006-11-08 16:54:55 UTC (rev 10774)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/legacy/Baz.hbm.xml 2006-11-08 16:56:52 UTC (rev 10775)
@@ -60,11 +60,11 @@
<index column="j"/>
<element column="the_time" type="time"/>
</array>
- <bag name="bag" order-by="`name!`" table="`^%$^bxaxg`">
+ <bag name="bag" order-by="`name_`" table="`bxaxg`">
<key>
- <column name="`baz_id$`" length="16"/>
+ <column name="`baz_id_`" length="16"/>
</key>
- <element column="`name!`" type="string"/>
+ <element column="`name_`" type="string"/>
</bag>
<map name="fooToGlarch" lazy="false">
<key>
Modified: branches/Branch_3_2/Hibernate3/test/org/hibernate/test/legacy/FooBar.hbm.xml
===================================================================
--- branches/Branch_3_2/Hibernate3/test/org/hibernate/test/legacy/FooBar.hbm.xml 2006-11-08 16:54:55 UTC (rev 10774)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/legacy/FooBar.hbm.xml 2006-11-08 16:56:52 UTC (rev 10775)
@@ -25,7 +25,7 @@
<param name="seperator">:</param>
</generator>
</id>
- <discriminator column="`^foo_subclass$1234`" type="character" force="true"/>
+ <discriminator column="`foo_subclass_1234`" type="character" force="true"/>
<version name="version"/>
<!--<version name="versionCalendar" type="calendar"/>-->
<!--<timestamp name="versionTimestamp"/>-->
@@ -36,7 +36,7 @@
<column name="long_" index="fbmtoidx" unique-key="abc" not-null="true"/>
</property>
<property name="integer">
- <column name="`@@##integer_*`" unique-key="abc" not-null="true"/>
+ <column name="`integer__`" unique-key="abc" not-null="true"/>
</property>
<property name="float">
<column name="float_" unique-key="abc" not-null="true" check="float_ > 0.0"/>
@@ -49,7 +49,7 @@
<index column="i"/>
<element column="byte_" type="byte"/>
</primitive-array>
-
+
<property name="date" type="date" column="date_"/>
<property name="timestamp" type="timestamp" column="timestamp_"/>
<property name="boolean" column="boolean_"/>
@@ -67,7 +67,7 @@
<property name="blob" type="org.hibernate.test.legacy.Foo$Struct" column="blobb_"/>
<property name="nullBlob" type="serializable"/>
<property name="binary" column="bin_"/>
- <property name="theLocale" access="field" column="`localeayzabc123!@#$`"/>
+ <property name="theLocale" access="field" column="`localeayzabc123`"/>
<property name="formula" formula="int_/2"/>
Modified: branches/Branch_3_2/Hibernate3/test/org/hibernate/test/legacy/Glarch.hbm.xml
===================================================================
--- branches/Branch_3_2/Hibernate3/test/org/hibernate/test/legacy/Glarch.hbm.xml 2006-11-08 16:54:55 UTC (rev 10774)
+++ branches/Branch_3_2/Hibernate3/test/org/hibernate/test/legacy/Glarch.hbm.xml 2006-11-08 16:56:52 UTC (rev 10775)
@@ -25,7 +25,7 @@
<property name="x"/>
<list name="strings">
<key column="glarch_key"/>
- <index column="`!@#i`"/>
+ <index column="`indx_`"/>
<element type="string" column="`tha_stryng`"/>
</list>
<list name="fooComponents" lazy="true" cascade="all">
18 years, 4 months
Hibernate SVN: r10774 - in trunk/Hibernate3: src/org/hibernate/dialect src/org/hibernate/dialect/function src/org/hibernate/exception src/org/hibernate/sql test/org/hibernate/test test/org/hibernate/test/hql test/org/hibernate/test/legacy
by hibernate-commits@lists.jboss.org
Author: steve.ebersole(a)jboss.com
Date: 2006-11-08 11:54:55 -0500 (Wed, 08 Nov 2006)
New Revision: 10774
Added:
trunk/Hibernate3/src/org/hibernate/dialect/Cache71Dialect.java
trunk/Hibernate3/src/org/hibernate/dialect/function/ConditionalParenthesisFunction.java
trunk/Hibernate3/src/org/hibernate/dialect/function/ConvertFunction.java
trunk/Hibernate3/src/org/hibernate/dialect/function/StandardJDBCEscapeFunction.java
trunk/Hibernate3/src/org/hibernate/exception/CacheSQLStateConverter.java
trunk/Hibernate3/src/org/hibernate/sql/CacheJoinFragment.java
Modified:
trunk/Hibernate3/src/org/hibernate/dialect/Dialect.java
trunk/Hibernate3/src/org/hibernate/dialect/function/SQLFunction.java
trunk/Hibernate3/src/org/hibernate/dialect/function/StandardSQLFunction.java
trunk/Hibernate3/test/org/hibernate/test/TestCase.java
trunk/Hibernate3/test/org/hibernate/test/hql/FooBarCopy.hbm.xml
trunk/Hibernate3/test/org/hibernate/test/legacy/Baz.hbm.xml
trunk/Hibernate3/test/org/hibernate/test/legacy/FooBar.hbm.xml
trunk/Hibernate3/test/org/hibernate/test/legacy/Glarch.hbm.xml
Log:
HHH-2205 : dialect for InterSystems' CacheSQL
Added: trunk/Hibernate3/src/org/hibernate/dialect/Cache71Dialect.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/dialect/Cache71Dialect.java 2006-11-08 12:10:08 UTC (rev 10773)
+++ trunk/Hibernate3/src/org/hibernate/dialect/Cache71Dialect.java 2006-11-08 16:54:55 UTC (rev 10774)
@@ -0,0 +1,844 @@
+//$Id: $
+package org.hibernate.dialect;
+
+import java.sql.CallableStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+
+import org.hibernate.Hibernate;
+import org.hibernate.LockMode;
+import org.hibernate.MappingException;
+import org.hibernate.cfg.Environment;
+import org.hibernate.dialect.function.NoArgSQLFunction;
+import org.hibernate.dialect.function.NvlFunction;
+import org.hibernate.dialect.function.SQLFunctionTemplate;
+import org.hibernate.dialect.function.StandardSQLFunction;
+import org.hibernate.dialect.function.VarArgsSQLFunction;
+import org.hibernate.dialect.function.StandardJDBCEscapeFunction;
+import org.hibernate.dialect.function.ConvertFunction;
+import org.hibernate.dialect.function.ConditionalParenthesisFunction;
+import org.hibernate.dialect.lock.LockingStrategy;
+import org.hibernate.dialect.lock.SelectLockingStrategy;
+import org.hibernate.dialect.lock.UpdateLockingStrategy;
+import org.hibernate.exception.CacheSQLStateConverter;
+import org.hibernate.exception.SQLExceptionConverter;
+import org.hibernate.exception.TemplatedViolatedConstraintNameExtracter;
+import org.hibernate.exception.ViolatedConstraintNameExtracter;
+import org.hibernate.id.IdentityGenerator;
+import org.hibernate.persister.entity.Lockable;
+import org.hibernate.sql.CacheJoinFragment;
+import org.hibernate.sql.JoinFragment;
+import org.hibernate.util.StringHelper;
+
+/**
+ * Caché 2007.1 dialect. This class is required in order to use Hibernate with Intersystems Cach� SQL.<br>
+ * <br>
+ * Compatible with Cach� 2007.1.
+ * Cache JDBC driver version ?.?.
+ * <br>
+ * <head>
+ * <title>Caché and Hibernate</title>
+ * </head>
+ * <body>
+ * <h1>Caché and Hibernate</h1>
+ * <h2>PREREQUISITES</h2>
+ * These setup instructions assume that both Caché and Hibernate are installed and operational.
+ * <br>
+ * <h2>HIBERNATE DIRECTORIES AND FILES</h2>
+ * InterSystems support for Hibernate 2.1.8 and Hibernate 3.0.5
+ * requires different dialect files from those distributed with Hibernate 3.2.
+ * Also Hibernate 2.1.8 has a different directory and Java package structure which is reflected in the InterSystems sources.
+ * JBoss distributes the InterSystems Cache' dialect for Hibernate 3.2.
+ * For earlier versions of Hibernate please contact
+ * <a href="http://www.intersystems.com/support/cache-support.html">InterSystems Worldwide Response Center</A> (WRC)
+ * for the appropriate source files.
+ * <br>
+ * <h2>CACHÉ DOCUMENTATION</h2>
+ * Documentation for Caché is available online when Caché is running.
+ * It can also be obtained from the
+ * <a href="http://www.intersystems.com/cache/downloads/documentation.html">InterSystems</A> website.
+ * The book, "Object-oriented Application Development Using the Caché Post-relational Database:
+ * is also available from Springer-Verlag.
+ * <br>
+ * <h2>HIBERNATE DOCUMENTATION</h2>
+ * Hibernate comes with extensive electronic documentation.
+ * In addition, several books on Hibernate are available from
+ * <a href="http://www.manning.com">Manning Publications Co</a>.
+ * Three available titles are "Hibernate Quickly", "Hibernate in Action", and "Java Persistence with Hibernate".
+ * <br>
+ * <h2>TO SET UP HIBERNATE FOR USE WITH CACHÉ</h2>
+ * The following steps assume that the directory where Caché was installed is C:\CacheSys.
+ * This is the default installation directory for Caché.
+ * The default installation directory for Hibernate is assumed to be C:\Hibernate.
+ * <p/>
+ * If either product is installed in a different location, the pathnames that follow should be modified appropriately.
+ * <p/>
+ * Caché version 2007.1 and above is recommended for use with
+ * Hibernate. The next step depends on the location of your
+ * CacheDB.jar depending on your version of Caché.
+ * <ol>
+ * <li>Copy C:\CacheSys\dev\java\lib\CacheDB.jar to C:\Hibernate\lib\CacheDB.jar.</li>
+ * <li>Copy C:\CacheSys\dev\java\lib\JDK15\CacheDB.jar to C:\Hibernate\lib\CacheDB.jar.</li>
+ * <p/>
+ * <li>Insert the following files into your Java classpath:
+ * <p/>
+ * <ul>
+ * <li>All jar files in the directory C:\Hibernate\lib</li>
+ * <li>The directory (or directories) where hibernate.properties and/or hibernate.cfg.xml are kept.</li>
+ * </ul>
+ * </li>
+ * <p/>
+ * <li>In the file, hibernate.properties (or hibernate.cfg.xml),
+ * specify the Caché dialect and the Caché version URL settings.</li>
+ * </ol>
+ * <p/>
+ * For example, in Hibernate 3.2, typical entries in hibernate.properties would have the following
+ * "name=value" pairs:
+ * <p/>
+ * <table cols=3 border cellpadding=5 cellspacing=0>
+ * <tr>
+ * <th>Property Name</th>
+ * <th>Property Value 3.2</th>
+ * <th>Property Value 2.1.8</th>
+ * </tr>
+ * <tr>
+ * <td>hibernate.dialect</td>
+ * <td>org.hibernate.dialect.Cache71Dialect</td>
+ * <td>net.sf.hibernate.dialect.Cache50Dialect</td>
+ * </tr>
+ * <tr>
+ * <td>hibernate.connection.driver_class</td>
+ * <td>com.intersys.jdbc.CacheDriver</td>
+ * <td>com.intersys.jdbc.CacheDriver</td>
+ * </tr>
+ * <tr>
+ * <td>hibernate.connection.username</td>
+ * <td>(see note 1)</td>
+ * <td>(see note 1)</td>
+ * </tr>
+ * <tr>
+ * <td>hibernate.connection.password</td>
+ * <td>(see note 1)</td>
+ * <td>(see note 1)</td>
+ * </tr>
+ * <tr>
+ * <td>hibernate.connection.url</td>
+ * <td>jdbc:Cache://127.0.0.1:1972/USER</td>
+ * <td>jdbc:Cache://127.0.0.1:1972/USER</td>
+ * </tr>
+ * </table>
+ * <p/>
+ * <dl>
+ * <dt><b>Note 1</b></dt>
+ * <dd>Please contact your administrator for the userid and password you should use when attempting access via JDBC.
+ * By default, these are chosen to be "_SYSTEM" and "SYS" respectively as noted in the SQL standard.</dd>
+ * </dl>
+ * <br>
+ * <h2>CACHÉ VERSION URL</h2>
+ * This is the standard URL for the JDBC driver.
+ * For a JDBC driver on the machine hosting Caché, use the IP "loopback" address, 127.0.0.1.
+ * For 1972, the default port, specify the super server port of your Caché instance.
+ * For USER, substitute the NAMESPACE which contains your Caché database data.
+ * <br>
+ * <h2>CACHÉ DIALECTS</h2>
+ * Choices for Dialect are:
+ * <br>
+ * <p/>
+ * <ol>
+ * <li>
+ * org.hibernate.dialect.Cache50Dialect (requires Caché
+ * 5.0 or above)
+ * </li>
+ * <p/>
+ * <li>org.hibernate.dialect.Cache51Dialect (requires Caché 5.1 or
+ * above)</li>
+ * <p/>
+ * <li>org.hibernate.dialect.Cache51SequenceDialect (requires Caché 5.1 or
+ * above)</li>
+ * <p/>
+ * <li>org.hibernate.dialect.Cache71Dialect (requires Caché
+ * 2007.1 or above)</li>
+ * <p/>
+ * <li>org.hibernate.dialect.Cache71SequenceDialect (requires Caché
+ * 2007.1 or above)</li>
+ * </ol>
+ * <p/>
+ * Setting up for Caché 5.0 is similar except that the hibernate
+ * dialect package is "org.hibernate.dialect.Cache50Dialect"
+ * <br>
+ * <h2>SETTING UP CACHÉ DIALECT FROM INTERSYSTEMS SOURCES</h2>
+ * <p/>
+ * InterSystems provides source code for the Hibernate Dialect classes.
+ * Therefore, you must first place the source code in the proper locations
+ * and compile it into Java class files.
+ * Doing this will eliminate possible version mismatches in the compiled Java code.
+ * <p/>
+ * To begin, unzip the InterSystems source into the PARENT directory of the location where you installed Hibernate.
+ * The InterSystems zip file that contains InterSystems Hibernate support for
+ * Hibernate 2.1.8 and Hibernate 3.0.5 already contains hibernate-2.1
+ * and hibernate-3.0 in its directory structure. This is why you must
+ * unzip to the directory CONTAINING your Hibernate install directory.
+ * <p/>
+ * If you are using Hibernate 3.2, JBoss has already built the right JAR file for you.
+ * If you are NOT using Hibernate 3.2, then you must do the following:
+ * <p/>
+ * <ol>
+ * <li>In your Hibernate install directory, after the unzip we described above,
+ * type the following:
+ * <br>
+ * <pre>
+ * build
+ * </pre></li>
+ * <li>Copy the JAR file to the proper place.
+ * <p/>
+ * For Hibernate 3.0.5, copy
+ * <pre>
+ * ..\hibernate\hibernate3.jar
+ * </pre>
+ * to the Hibernate install directory.
+ * <p/>
+ * For Hibernate 2.1.8, copy
+ * <pre>
+ * ..\hibernate\hibernate.2jar
+ * </pre>
+ * to the Hibernate install directory.</li>
+ * <p/>
+ * <li>Test your installation by configuring etc\hibernate.properties for your Caché database,
+ * and then running the following:
+ * <pre>
+ * build eg
+ * </pre>
+ * The build process reports its success or failure. If you see,
+ * <pre>
+ * BUILD FAILED
+ * </pre>
+ * please contact the
+ * <a href="http://www.intersystems.com/support/cache-support.html">InterSystems Worldwide Response Center</A>
+ * for assistance.
+ * </li>
+ * </ol>
+ * <br>
+ * <h2>SUPPORT FOR IDENTITY COLUMNS</h2>
+ * Caché 5.1 or later supports identity columns. This includes Cache' 2007.1. To cause
+ * Hibernate to use identity columns, specify "native" as the
+ * generator. Also make sure that
+ * hibernate.jdbc.use_get_generated_keys is set to true, which is the
+ * default. In the Cache' 2007.1 release, with the
+ * Cache71Dialect, you can set hibernate.jdbc.use_get_generated_keys to
+ * false, and still use identity columns.
+ * <br>
+ * <h2>SEQUENCE DIALECTS SUPPORT SEQUENCES</h2>
+ * You do not have to use the sequence dialects with Caché.
+ * These are Cache50SequenceDialect, Cache51SequenceDialect and Cache71SequenceDialect.
+ * But if you choose to use them, set them up as follows:
+ * <p/>
+ * To use Hibernate sequence support with Caché in a namespace, you must FIRST load the following file into that namespace:
+ * <pre>
+ * src\org\hibernate\dialect\CacheSequences.xml
+ * </pre>
+ * In your Hibernate mapping you can specify sequence use.
+ * When you are using a Cache' sequence dialect, the type "native" maps to sequence.
+ * <p/>
+ * For example, the following shows the use of a sequence generator in a Hibernate mapping:
+ * <pre>
+ * <id name="id" column="uid" type="long" unsaved-value="null">
+ * <generator class="seqhilo">
+ * <param name="sequence">EVENTS_SEQ</param>
+ * <param name="max_lo">0</param>
+ * </generator>
+ * </id>
+ * </pre>
+ * <br>
+ * <p/>
+ * Some versions of Hibernate under some circumstances call
+ * getSelectSequenceNextValString() in the dialect. If this happens
+ * you will receive the error message: new MappingException( "Dialect
+ * does not support sequences" ).
+ * <h2>BIGINT SUPPORT</h2>
+ * Caché 5.1 and above supports BIGINT. 2007.1 supports BIGINT.
+ * <p/>
+ * Caché 5.0.x does not have direct BIGINT support.
+ * To imitate BIGINT support in Cache 5.0.x, in the SQL configuration,
+ * remap %INTEGER as follows, to be used by Caché 5.0.x dialects:
+ * <p/>
+ * <pre>
+ * %Library.Integer(MAXVAL=99999999999999999999,MINVAL=-999999999999999999)
+ * </pre>
+ * <p/>
+ * To change SQL settings:
+ * <p/>
+ * <ol>
+ * <li>In Caché 2007.1, use the System Management Portal.</li>
+ * <p/>
+ * <li>In Caché 5.0, use the Configuration Manager.</li>
+ * </ol>
+ * <p/>
+ * Set Caché SQL to allow:
+ * <p/>
+ * <ol>
+ * <li>delimited identifiers</li>
+ * <li>drop of non-existent tables</li>
+ * <li>drop of non-existent constraints</li>
+ * </ol>
+ * <p/>
+ * <h2>HIBERNATE 2.1.8</h2>
+ * Hibernate 2.1.8 requires different source files from InterSystems reflecting
+ * the different directory and Java package structure of Hibernate 2.1.8.
+ * Please contact
+ * <a href="http://www.intersystems.com/support/cache-support.html">InterSystems Worldwide Response Center</A> (WRC)
+ * for these source files if you need to use Hibernate 2.1.8.
+ * <p/>
+ * To run a Cach� application with Hibernate 2.1.8, set the following flag when starting your JVM:
+ * <pre>
+ * -Dhibernate.jdbc.use_streams_for_binary=false
+ * </pre>
+ * <p/>
+ * In Hibernate 3.0.5, this flag is not necessary;
+ * it is taken care of by the Caché dialect itself.
+ * <br>
+ * <h2>HIBERNATE FILES ASSOCIATED WITH CACHÉ DIALECT</h2>
+ * The following files are associated with Caché dialect:
+ * <p/>
+ * <ol>
+ * <li>src\org\hibernate\dialect\Cache71Dialect.java</li>
+ * <li>src\org\hibernate\dialect\function\ConditionalParenthesisFunction.java</li>
+ * <li>src\org\hibernate\dialect\function\ConvertFunction.java</li>
+ * <li>src\org\hibernate\exception\CacheSQLStateConverter.java</li>
+ * <li>src\org\hibernate\sql\CacheJoinFragment.java</li>
+ * </ol>
+ * Cache71Dialect ships with Hibernate 3.2. All other dialects are distributed by InterSystems and subclass Cache71Dialect.
+ * <h2>Limitations</h2>
+ * The following is a list of the currently known limitations of using Cache' with Hibernate. Please check with InterSystems on the latest status as these are server side issues.
+ * <p/>
+ * The following also lists known issues with running the full Hibernate regression tests against InterSystems Cache'.
+ * <p/>
+ * - Delete of self-referential foreign keys.
+ * <p/>
+ * We do not current support deletion of a table row when the table has a foreign key that references itself and the foreign key in the table row references that table row. (self-referential foreign keys)
+ * <p/>
+ * - Support for "SELECT FOR UPDATE."
+ * <p/>
+ * The default locking mode strategy for Cache' is Hibernate's UpdateLockingStrategy, which you use with Hibernate's versioning capability.
+ * <p/>
+ * To use Concurrency modes that requires other locking strategies you can subclass your Cache' dialect and define a locking strategy.
+ * <p/>
+ * Cache' does not currently support "SELECT FOR UPDATE." While grammatically correct, it does no locking.
+ * <p/>
+ * In versions prior to 2007.1, there are limitations in support for outer joins (see Cache' documentation).
+ * <p/>
+ * For example, Cache' prior to 2007.1 does support "NOT NULL" clauses in the ON conditions.
+ * <p/>
+ * Cache' does not support using longvarbinary or longvarchar in a where clause.
+ * <p/>
+ * In Hibernate regression tests, Baz.hbm.xml, FooBar.hbm.xml, Glarch.hbm.xml and XY.hbm.xml have to be edited to replace "!" by BANG and "^" by CARET. Our own identifier translation is not enough since Hibernate uses names containing these in its own mapping files and Hibernate (on the client-side) does not know about our server-side mapping.
+ * <p/>
+ * There are some tests that involve batch operations where the test
+ * will succeed if you increase the size of the lock table from
+ * 786432 (the default) to 1786432. You can use the System Management
+ * Portal in 2007.1 or the configuration manager in 5.0.X to increase the
+ * size of the lock table. This requirement of increasing the lock
+ * table size is specific to running the Hibernate regression tests.
+ * <p/>
+ * Cache' fails tests that attempt to COUNT stream fields
+ * <p/>
+ * Here is an example of such an error message, SQL ERROR -37: Aggregate function COUNT not supported for Stream fields
+ * <p/>
+ * We fail on tests that use mappings like the following
+ * <p/>
+ * <pre>
+ * <property name="yob" formula="year(dob)"/>
+ * </pre>
+ * and the error is:
+ * <p/>
+ * ERROR #359: User defined SQL Function 'SQLUSER.YEAR' does not exist
+ * <p/>
+ * If the mapping is changed to
+ * <p/>
+ * <pre>
+ * <property name="yob" formula="{fn year(dob)}"/>
+ * </pre>
+ * then such tests succeed.
+ * <p/>
+ * Cache' 2007.1 supports temp tables. Prior versions do not.
+ * <p/>
+ * We do not support row valued expression. So WHERE clauses like "WHERE (a,b,c) = (d,e,f)" give syntax errors. The Hibernate regression tests use row valued expressions and we fail on these.
+ * <p/>
+ * For example:
+ * <p/>
+ * <pre>
+ * result = s.createQuery("from Transaction txn where txn.value = (1.5, 'AUD')").list();
+ * </pre>
+ * will not generate valid Cache' SQL because of the parenthesis around (1.5, 'AUD')
+ * <p/>
+ * Queries like the following:
+ * <p/>
+ * <pre>
+ * session.createQuery("select extract(second from current_timestamp()), extract(minute from current_timestamp()), extract(hour from current_timestamp()) from Mammal m").list();
+ * </pre>
+ * <p/>
+ * will not work, because we do support "extract" function.
+ * <p/>
+ * We do not support in () (empty parenthesis)
+ * <p/>
+ * We do not guarantee the case of a column returned using a group by clause.
+ * <p/>
+ * For instance in the regression test,
+ * <p/>
+ * <pre>
+ * list = s.find("select new Result( baz.name, max(foo.long), count(foo) ) from Baz baz join baz.fooArray foo group by baz.name");
+ * </pre>
+ * we return baz.name in lower case, and the test that generates this SQL fails.
+ *
+ * @author Jonathan Levinson
+ */
+
+public class Cache71Dialect extends Dialect {
+
+ /**
+ * Creates new <code>Cach�71Dialect</code> instance. Sets up the JDBC /
+ * Cach� type mappings.
+ */
+ public Cache71Dialect() {
+ super();
+ commonRegistration();
+ register71Functions();
+ }
+
+ protected final void commonRegistration() {
+ // Note: For object <-> SQL datatype mappings see:
+ // Configuration Manager | Advanced | SQL | System DDL Datatype Mappings
+ //
+ // TBD registerColumnType(Types.BINARY, "binary($1)");
+ // changed 08-11-2005, jsl
+ registerColumnType( Types.BINARY, "varbinary($1)" );
+ registerColumnType( Types.BIGINT, "BigInt" );
+ registerColumnType( Types.BIT, "bit" );
+ registerColumnType( Types.CHAR, "char(1)" );
+ registerColumnType( Types.DATE, "date" );
+ registerColumnType( Types.DECIMAL, "decimal" );
+ registerColumnType( Types.DOUBLE, "double" );
+ registerColumnType( Types.FLOAT, "float" );
+ registerColumnType( Types.INTEGER, "integer" );
+ registerColumnType( Types.LONGVARBINARY, "longvarbinary" ); // binary %Stream
+ registerColumnType( Types.LONGVARCHAR, "longvarchar" ); // character %Stream
+ registerColumnType( Types.NUMERIC, "numeric($p,$s)" );
+ registerColumnType( Types.REAL, "real" );
+ registerColumnType( Types.SMALLINT, "smallint" );
+ registerColumnType( Types.TIMESTAMP, "timestamp" );
+ registerColumnType( Types.TIME, "time" );
+ registerColumnType( Types.TINYINT, "tinyint" );
+ // TBD should this be varbinary($1)?
+ // registerColumnType(Types.VARBINARY, "binary($1)");
+ registerColumnType( Types.VARBINARY, "longvarbinary" );
+ registerColumnType( Types.VARCHAR, "varchar($l)" );
+ registerColumnType( Types.BLOB, "longvarbinary" );
+ registerColumnType( Types.CLOB, "longvarchar" );
+
+ getDefaultProperties().setProperty( Environment.USE_STREAMS_FOR_BINARY, "false" );
+ getDefaultProperties().setProperty( Environment.STATEMENT_BATCH_SIZE, DEFAULT_BATCH_SIZE );
+ //getDefaultProperties().setProperty(Environment.STATEMENT_BATCH_SIZE, NO_BATCH);
+
+ getDefaultProperties().setProperty( Environment.USE_SQL_COMMENTS, "false" );
+
+ registerFunction( "abs", new StandardSQLFunction( "abs" ) );
+ registerFunction( "acos", new StandardJDBCEscapeFunction( "acos", Hibernate.DOUBLE ) );
+ registerFunction( "%alphaup", new StandardSQLFunction( "%alphaup", Hibernate.STRING ) );
+ registerFunction( "ascii", new StandardSQLFunction( "ascii", Hibernate.STRING ) );
+ registerFunction( "asin", new StandardJDBCEscapeFunction( "asin", Hibernate.DOUBLE ) );
+ registerFunction( "atan", new StandardJDBCEscapeFunction( "atan", Hibernate.DOUBLE ) );
+ registerFunction( "bit_length", new SQLFunctionTemplate( Hibernate.INTEGER, "($length(?1)*8)" ) );
+ // hibernate impelemnts cast in Dialect.java
+ registerFunction( "ceiling", new StandardSQLFunction( "ceiling", Hibernate.INTEGER ) );
+ registerFunction( "char", new StandardJDBCEscapeFunction( "char", Hibernate.CHARACTER ) );
+ registerFunction( "character_length", new StandardSQLFunction( "character_length", Hibernate.INTEGER ) );
+ registerFunction( "char_length", new StandardSQLFunction( "char_length", Hibernate.INTEGER ) );
+ registerFunction( "cos", new StandardJDBCEscapeFunction( "cos", Hibernate.DOUBLE ) );
+ registerFunction( "cot", new StandardJDBCEscapeFunction( "cot", Hibernate.DOUBLE ) );
+ registerFunction( "coalesce", new VarArgsSQLFunction( "coalesce(", ",", ")" ) );
+ registerFunction( "concat", new VarArgsSQLFunction( Hibernate.STRING, "", "||", "" ) );
+ registerFunction( "convert", new ConvertFunction() );
+ registerFunction( "curdate", new StandardJDBCEscapeFunction( "curdate", Hibernate.DATE ) );
+ registerFunction( "current_date", new NoArgSQLFunction( "current_date", Hibernate.DATE, false ) );
+ registerFunction( "current_time", new NoArgSQLFunction( "current_time", Hibernate.TIME, false ) );
+ registerFunction(
+ "current_timestamp", new ConditionalParenthesisFunction( "current_timestamp", Hibernate.TIMESTAMP )
+ );
+ registerFunction( "curtime", new StandardJDBCEscapeFunction( "curtime", Hibernate.TIME ) );
+ registerFunction( "database", new StandardJDBCEscapeFunction( "database", Hibernate.STRING ) );
+ registerFunction( "dateadd", new VarArgsSQLFunction( Hibernate.TIMESTAMP, "dateadd(", ",", ")" ) );
+ registerFunction( "datediff", new VarArgsSQLFunction( Hibernate.INTEGER, "datediff(", ",", ")" ) );
+ registerFunction( "datename", new VarArgsSQLFunction( Hibernate.STRING, "datename(", ",", ")" ) );
+ registerFunction( "datepart", new VarArgsSQLFunction( Hibernate.INTEGER, "datepart(", ",", ")" ) );
+ registerFunction( "day", new StandardSQLFunction( "day", Hibernate.INTEGER ) );
+ registerFunction( "dayname", new StandardJDBCEscapeFunction( "dayname", Hibernate.STRING ) );
+ registerFunction( "dayofmonth", new StandardJDBCEscapeFunction( "dayofmonth", Hibernate.INTEGER ) );
+ registerFunction( "dayofweek", new StandardJDBCEscapeFunction( "dayofweek", Hibernate.INTEGER ) );
+ registerFunction( "dayofyear", new StandardJDBCEscapeFunction( "dayofyear", Hibernate.INTEGER ) );
+ // is it necessary to register %exact since it can only appear in a where clause?
+ registerFunction( "%exact", new StandardSQLFunction( "%exact", Hibernate.STRING ) );
+ registerFunction( "exp", new StandardJDBCEscapeFunction( "exp", Hibernate.DOUBLE ) );
+ registerFunction( "%external", new StandardSQLFunction( "%external", Hibernate.STRING ) );
+ registerFunction( "$extract", new VarArgsSQLFunction( Hibernate.INTEGER, "$extract(", ",", ")" ) );
+ registerFunction( "$find", new VarArgsSQLFunction( Hibernate.INTEGER, "$find(", ",", ")" ) );
+ registerFunction( "floor", new StandardSQLFunction( "floor", Hibernate.INTEGER ) );
+ registerFunction( "getdate", new StandardSQLFunction( "getdate", Hibernate.TIMESTAMP ) );
+ registerFunction( "hour", new StandardJDBCEscapeFunction( "hour", Hibernate.INTEGER ) );
+ registerFunction( "ifnull", new VarArgsSQLFunction( "ifnull(", ",", ")" ) );
+ registerFunction( "%internal", new StandardSQLFunction( "%internal" ) );
+ registerFunction( "isnull", new VarArgsSQLFunction( "isnull(", ",", ")" ) );
+ registerFunction( "isnumeric", new StandardSQLFunction( "isnumeric", Hibernate.INTEGER ) );
+ registerFunction( "lcase", new StandardJDBCEscapeFunction( "lcase", Hibernate.STRING ) );
+ registerFunction( "left", new StandardJDBCEscapeFunction( "left", Hibernate.STRING ) );
+ registerFunction( "len", new StandardSQLFunction( "len", Hibernate.INTEGER ) );
+ registerFunction( "length", new StandardSQLFunction( "length", Hibernate.INTEGER ) );
+ registerFunction( "$length", new VarArgsSQLFunction( "$length(", ",", ")" ) );
+ // aggregate functions shouldn't be registered, right?
+ //registerFunction( "list", new StandardSQLFunction("list",Hibernate.STRING) );
+ // stopped on $list
+ registerFunction( "$list", new VarArgsSQLFunction( "$list(", ",", ")" ) );
+ registerFunction( "$listdata", new VarArgsSQLFunction( "$listdata(", ",", ")" ) );
+ registerFunction( "$listfind", new VarArgsSQLFunction( "$listfind(", ",", ")" ) );
+ registerFunction( "$listget", new VarArgsSQLFunction( "$listget(", ",", ")" ) );
+ registerFunction( "$listlength", new StandardSQLFunction( "$listlength", Hibernate.INTEGER ) );
+ registerFunction( "locate", new StandardSQLFunction( "$FIND", Hibernate.INTEGER ) );
+ registerFunction( "log", new StandardJDBCEscapeFunction( "log", Hibernate.DOUBLE ) );
+ registerFunction( "log10", new StandardJDBCEscapeFunction( "log", Hibernate.DOUBLE ) );
+ registerFunction( "lower", new StandardSQLFunction( "lower" ) );
+ registerFunction( "ltrim", new StandardSQLFunction( "ltrim" ) );
+ registerFunction( "minute", new StandardJDBCEscapeFunction( "minute", Hibernate.INTEGER ) );
+ registerFunction( "mod", new StandardJDBCEscapeFunction( "mod", Hibernate.DOUBLE ) );
+ registerFunction( "month", new StandardJDBCEscapeFunction( "month", Hibernate.INTEGER ) );
+ registerFunction( "monthname", new StandardJDBCEscapeFunction( "monthname", Hibernate.STRING ) );
+ registerFunction( "now", new StandardJDBCEscapeFunction( "monthname", Hibernate.TIMESTAMP ) );
+ registerFunction( "nullif", new VarArgsSQLFunction( "nullif(", ",", ")" ) );
+ registerFunction( "nvl", new NvlFunction() );
+ registerFunction( "%odbcin", new StandardSQLFunction( "%odbcin" ) );
+ registerFunction( "%odbcout", new StandardSQLFunction( "%odbcin" ) );
+ registerFunction( "%pattern", new VarArgsSQLFunction( Hibernate.STRING, "", "%pattern", "" ) );
+ registerFunction( "pi", new StandardJDBCEscapeFunction( "pi", Hibernate.DOUBLE ) );
+ registerFunction( "$piece", new VarArgsSQLFunction( Hibernate.STRING, "$piece(", ",", ")" ) );
+ registerFunction( "position", new VarArgsSQLFunction( Hibernate.INTEGER, "position(", " in ", ")" ) );
+ registerFunction( "power", new VarArgsSQLFunction( Hibernate.STRING, "power(", ",", ")" ) );
+ registerFunction( "quarter", new StandardJDBCEscapeFunction( "quarter", Hibernate.INTEGER ) );
+ registerFunction( "repeat", new VarArgsSQLFunction( Hibernate.STRING, "repeat(", ",", ")" ) );
+ registerFunction( "replicate", new VarArgsSQLFunction( Hibernate.STRING, "replicate(", ",", ")" ) );
+ registerFunction( "right", new StandardJDBCEscapeFunction( "right", Hibernate.STRING ) );
+ registerFunction( "round", new VarArgsSQLFunction( Hibernate.FLOAT, "round(", ",", ")" ) );
+ registerFunction( "rtrim", new StandardSQLFunction( "rtrim", Hibernate.STRING ) );
+ registerFunction( "second", new StandardJDBCEscapeFunction( "second", Hibernate.INTEGER ) );
+ registerFunction( "sign", new StandardSQLFunction( "sign", Hibernate.INTEGER ) );
+ registerFunction( "sin", new StandardJDBCEscapeFunction( "sin", Hibernate.DOUBLE ) );
+ registerFunction( "space", new StandardSQLFunction( "space", Hibernate.STRING ) );
+ registerFunction( "%sqlstring", new VarArgsSQLFunction( Hibernate.STRING, "%sqlstring(", ",", ")" ) );
+ registerFunction( "%sqlupper", new VarArgsSQLFunction( Hibernate.STRING, "%sqlupper(", ",", ")" ) );
+ registerFunction( "sqrt", new StandardJDBCEscapeFunction( "SQRT", Hibernate.DOUBLE ) );
+ registerFunction( "%startswith", new VarArgsSQLFunction( Hibernate.STRING, "", "%startswith", "" ) );
+ // below is for Cache' that don't have str in 2007.1 there is str and we register str directly
+ registerFunction( "str", new SQLFunctionTemplate( Hibernate.STRING, "cast(?1 as char varying)" ) );
+ registerFunction( "string", new VarArgsSQLFunction( Hibernate.STRING, "string(", ",", ")" ) );
+ // note that %string is deprecated
+ registerFunction( "%string", new VarArgsSQLFunction( Hibernate.STRING, "%string(", ",", ")" ) );
+ registerFunction( "substr", new VarArgsSQLFunction( Hibernate.STRING, "substr(", ",", ")" ) );
+ registerFunction( "substring", new VarArgsSQLFunction( Hibernate.STRING, "substring(", ",", ")" ) );
+ registerFunction( "sysdate", new NoArgSQLFunction( "sysdate", Hibernate.TIMESTAMP, false ) );
+ registerFunction( "tan", new StandardJDBCEscapeFunction( "tan", Hibernate.DOUBLE ) );
+ registerFunction( "timestampadd", new StandardJDBCEscapeFunction( "timestampadd", Hibernate.DOUBLE ) );
+ registerFunction( "timestampdiff", new StandardJDBCEscapeFunction( "timestampdiff", Hibernate.DOUBLE ) );
+ registerFunction( "tochar", new VarArgsSQLFunction( Hibernate.STRING, "tochar(", ",", ")" ) );
+ registerFunction( "to_char", new VarArgsSQLFunction( Hibernate.STRING, "to_char(", ",", ")" ) );
+ registerFunction( "todate", new VarArgsSQLFunction( Hibernate.STRING, "todate(", ",", ")" ) );
+ registerFunction( "to_date", new VarArgsSQLFunction( Hibernate.STRING, "todate(", ",", ")" ) );
+ registerFunction( "tonumber", new StandardSQLFunction( "tonumber" ) );
+ registerFunction( "to_number", new StandardSQLFunction( "tonumber" ) );
+ // TRIM(end_keyword string-expression-1 FROM string-expression-2)
+ // use Hibernate implementation "From" is one of the parameters they pass in position ?3
+ //registerFunction( "trim", new SQLFunctionTemplate(Hibernate.STRING, "trim(?1 ?2 from ?3)") );
+ registerFunction( "truncate", new StandardJDBCEscapeFunction( "truncate", Hibernate.STRING ) );
+ registerFunction( "ucase", new StandardJDBCEscapeFunction( "ucase", Hibernate.STRING ) );
+ registerFunction( "upper", new StandardSQLFunction( "upper" ) );
+ // %upper is deprecated
+ registerFunction( "%upper", new StandardSQLFunction( "%upper" ) );
+ registerFunction( "user", new StandardJDBCEscapeFunction( "user", Hibernate.STRING ) );
+ registerFunction( "week", new StandardJDBCEscapeFunction( "user", Hibernate.INTEGER ) );
+ registerFunction( "xmlconcat", new VarArgsSQLFunction( Hibernate.STRING, "xmlconcat(", ",", ")" ) );
+ registerFunction( "xmlelement", new VarArgsSQLFunction( Hibernate.STRING, "xmlelement(", ",", ")" ) );
+ // xmlforest requires a new kind of function constructor
+ registerFunction( "year", new StandardJDBCEscapeFunction( "year", Hibernate.INTEGER ) );
+ }
+
+ protected final void register71Functions() {
+ this.registerFunction( "str", new VarArgsSQLFunction( Hibernate.STRING, "str(", ",", ")" ) );
+ }
+
+ // DDL support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public boolean hasAlterTable() {
+ // Does this dialect support the ALTER TABLE syntax?
+ return true;
+ }
+
+ public boolean qualifyIndexName() {
+ // Do we need to qualify index names with the schema name?
+ return false;
+ }
+
+ public boolean supportsUnique() {
+ // Does this dialect support the UNIQUE column syntax?
+ return true;
+ }
+
+ /**
+ * The syntax used to add a foreign key constraint to a table.
+ *
+ * @return String
+ */
+ public String getAddForeignKeyConstraintString(
+ String constraintName,
+ String[] foreignKey,
+ String referencedTable,
+ String[] primaryKey,
+ boolean referencesPrimaryKey) {
+ // The syntax used to add a foreign key constraint to a table.
+ return new StringBuffer( 300 )
+ .append( " ADD CONSTRAINT " )
+ .append( constraintName )
+ .append( " FOREIGN KEY " )
+ .append( constraintName )
+ .append( " (" )
+ .append( StringHelper.join( ", ", foreignKey ) ) // identifier-commalist
+ .append( ") REFERENCES " )
+ .append( referencedTable )
+ .append( " (" )
+ .append( StringHelper.join( ", ", primaryKey ) ) // identifier-commalist
+ .append( ") " )
+ .toString();
+ }
+
+ public boolean supportsCheck() {
+ // Does this dialect support check constraints?
+ return false;
+ }
+
+ public String getAddColumnString() {
+ // The syntax used to add a column to a table
+ return " add column";
+ }
+
+ public String getCascadeConstraintsString() {
+ // Completely optional cascading drop clause.
+ return "";
+ }
+
+ public boolean dropConstraints() {
+ // Do we need to drop constraints before dropping tables in this dialect?
+ return true;
+ }
+
+ public boolean supportsCascadeDelete() {
+ return true;
+ }
+
+ public boolean hasSelfReferentialForeignKeyBug() {
+ return true;
+ }
+
+ // temporary table support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public boolean supportsTemporaryTables() {
+ return true;
+ }
+
+ public String generateTemporaryTableName(String baseTableName) {
+ String name = super.generateTemporaryTableName( baseTableName );
+ return name.length() > 25 ? name.substring( 1, 25 ) : name;
+ }
+
+ public String getCreateTemporaryTableString() {
+ return "create global temporary table";
+ }
+
+ public boolean performTemporaryTableDDLInIsolation() {
+ return false;
+ }
+
+ public String getCreateTemporaryTablePostfix() {
+ return "";
+ }
+
+ public boolean dropTemporaryTableAfterUse() {
+ return true;
+ }
+
+ // IDENTITY support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public boolean supportsIdentityColumns() {
+ return true;
+ }
+
+ public Class getNativeIdentifierGeneratorClass() {
+ return IdentityGenerator.class;
+ }
+
+ public boolean hasDataTypeInIdentityColumn() {
+ // Whether this dialect has an Identity clause added to the data type or a completely seperate identity
+ // data type
+ return true;
+ }
+
+ public String getIdentityColumnString() throws MappingException {
+ // The keyword used to specify an identity column, if identity column key generation is supported.
+ return "identity";
+ }
+
+ public String getIdentitySelectString() {
+ return "SELECT LAST_IDENTITY() FROM %TSQL_sys.snf";
+ }
+
+ // SEQUENCE support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public boolean supportsSequences() {
+ return true;
+ }
+
+ public String getSequenceNextValString(String sequenceName) {
+ return "select InterSystems.Sequences_GetNext('" + sequenceName + "') from InterSystems.Sequences where ucase(name)=ucase('" + sequenceName + "'))";
+ }
+
+ public String getSelectSequenceNextValString(String sequenceName) {
+ return "(select InterSystems.Sequences_GetNext('" + sequenceName + "') from InterSystems.Sequences where ucase(name)=ucase('" + sequenceName + "')))";
+ }
+
+ public String getCreateSequenceString(String sequenceName) {
+ return "insert into InterSystems.Sequences(Name) values (ucase('" + sequenceName + "'))";
+ }
+
+ public String getDropSequenceString(String sequenceName) {
+ return "delete from InterSystems.Sequences where ucase(name)=ucase('" + sequenceName + "')";
+ }
+
+ public String getQuerySequencesString() {
+ return "select name from InterSystems.Sequences";
+ }
+
+ // lock acquisition support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public boolean supportsForUpdate() {
+ // Does this dialect support the FOR UPDATE syntax?
+ return false;
+ }
+
+ public boolean supportsForUpdateOf() {
+ // Does this dialect support FOR UPDATE OF, allowing particular rows to be locked?
+ return false;
+ }
+
+ public boolean supportsForUpdateNowait() {
+ // Does this dialect support the Oracle-style FOR UPDATE NOWAIT syntax?
+ return false;
+ }
+
+ public boolean supportsOuterJoinForUpdate() {
+ return false;
+ }
+
+ public LockingStrategy getLockingStrategy(Lockable lockable, LockMode lockMode) {
+ // InterSystems Cache' does not current support "SELECT ... FOR UPDATE" syntax...
+ // Set your transaction mode to READ_COMMITTED before using
+ if ( lockMode.greaterThan( LockMode.READ ) ) {
+ return new UpdateLockingStrategy( lockable, lockMode );
+ }
+ else {
+ return new SelectLockingStrategy( lockable, lockMode );
+ }
+ }
+
+ // LIMIT support (ala TOP) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public boolean supportsLimit() {
+ return true;
+ }
+
+ public boolean supportsLimitOffset() {
+ return false;
+ }
+
+ public boolean supportsVariableLimit() {
+ return true;
+ }
+
+ public boolean bindLimitParametersFirst() {
+ // Does the LIMIT clause come at the start of the SELECT statement, rather than at the end?
+ return true;
+ }
+
+ public boolean useMaxForLimit() {
+ // Does the LIMIT clause take a "maximum" row number instead of a total number of returned rows?
+ return true;
+ }
+
+ public String getLimitString(String sql, boolean hasOffset) {
+ if ( hasOffset ) {
+ throw new UnsupportedOperationException( "An offset may not be specified to <TOP n> in Cache SQL" );
+ }
+
+ // This does not support the Cache SQL 'DISTINCT BY (comma-list)' extensions,
+ // but this extension is not supported through Hibernate anyway.
+ int insertionPoint = sql.startsWith( "select distinct" ) ? 15 : 6;
+
+ return new StringBuffer( sql.length() + 8 )
+ .append( sql )
+ .insert( insertionPoint, " TOP ? " )
+ .toString();
+ }
+
+ // callable statement support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public int registerResultSetOutParameter(CallableStatement statement, int col) throws SQLException {
+ return col;
+ }
+
+ public ResultSet getResultSet(CallableStatement ps) throws SQLException {
+ ps.execute();
+ return ( ResultSet ) ps.getObject( 1 );
+ }
+
+ // miscellaneous support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ public String getLowercaseFunction() {
+ // The name of the SQL function that transforms a string to lowercase
+ return "lower";
+ }
+
+ public String getNullColumnString() {
+ // The keyword used to specify a nullable column.
+ return " null";
+ }
+
+ public JoinFragment createOuterJoinFragment() {
+ // Create an OuterJoinGenerator for this dialect.
+ return new CacheJoinFragment();
+ }
+
+ public String getNoColumnsInsertString() {
+ // The keyword used to insert a row without specifying
+ // any column values
+ return " default values";
+ }
+
+ public SQLExceptionConverter buildSQLExceptionConverter() {
+ return new CacheSQLStateConverter( EXTRACTER );
+ }
+
+ public static final ViolatedConstraintNameExtracter EXTRACTER = new TemplatedViolatedConstraintNameExtracter() {
+ /**
+ * Extract the name of the violated constraint from the given SQLException.
+ *
+ * @param sqle The exception that was the result of the constraint violation.
+ * @return The extracted constraint name.
+ */
+ public String extractConstraintName(SQLException sqle) {
+ return extractUsingTemplate( "constraint (", ") violated", sqle.getMessage() );
+ }
+ };
+}
Modified: trunk/Hibernate3/src/org/hibernate/dialect/Dialect.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/dialect/Dialect.java 2006-11-08 12:10:08 UTC (rev 10773)
+++ trunk/Hibernate3/src/org/hibernate/dialect/Dialect.java 2006-11-08 16:54:55 UTC (rev 10774)
@@ -57,11 +57,19 @@
private static final Log log = LogFactory.getLog( Dialect.class );
- static final String DEFAULT_BATCH_SIZE = "15";
- static final String NO_BATCH = "0";
+ public static final String DEFAULT_BATCH_SIZE = "15";
+ public static final String NO_BATCH = "0";
+ /**
+ * Characters used for quoting SQL identifiers
+ */
+ public static final String QUOTE = "`\"[";
+ public static final String CLOSED_QUOTE = "`\"]";
+
+
+ // build the map of standard ANSI SQL aggregation functions ~~~~~~~~~~~~~~~
+
private static final Map STANDARD_AGGREGATE_FUNCTIONS = new HashMap();
-
static {
STANDARD_AGGREGATE_FUNCTIONS.put( "count", new StandardSQLFunction("count") {
public Type getReturnType(Type columnType, Mapping mapping) {
@@ -97,7 +105,7 @@
}
if ( sqlTypes.length != 1 ) throw new QueryException( "multi-column type in sum()" );
int sqlType = sqlTypes[0];
-
+
// First allow the actual type to control the return value. (the actual underlying sqltype could actually be different)
if ( columnType == Hibernate.BIG_INTEGER ) {
return Hibernate.BIG_INTEGER;
@@ -110,21 +118,21 @@
}
else if ( columnType == Hibernate.FLOAT || columnType == Hibernate.DOUBLE) {
return Hibernate.DOUBLE;
- }
-
+ }
+
// finally use the sqltype if == on Hibernate types did not find a match.
if ( sqlType == Types.NUMERIC ) {
- return columnType; //because numeric can be anything
- }
+ return columnType; //because numeric can be anything
+ }
else if ( sqlType == Types.FLOAT || sqlType == Types.DOUBLE || sqlType == Types.DECIMAL || sqlType == Types.REAL) {
return Hibernate.DOUBLE;
- }
+ }
else if ( sqlType == Types.BIGINT || sqlType == Types.INTEGER || sqlType == Types.SMALLINT || sqlType == Types.TINYINT ) {
return Hibernate.LONG;
}
else {
return columnType;
- }
+ }
}
});
}
@@ -136,7 +144,9 @@
private final Map sqlFunctions = new HashMap();
private final Set sqlKeywords = new HashSet();
-
+
+ // constructors and factory methods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
protected Dialect() {
log.info( "Using dialect: " + this );
sqlFunctions.putAll( STANDARD_AGGREGATE_FUNCTIONS );
@@ -156,7 +166,7 @@
registerFunction( "lower", new StandardSQLFunction("lower") );
registerFunction( "cast", new CastFunction() );
registerFunction( "extract", new SQLFunctionTemplate(Hibernate.INTEGER, "extract(?1 ?2 ?3)") );
-
+
//map second/minute/hour/day/month/year to ANSI extract(), override on subclasses
registerFunction( "second", new SQLFunctionTemplate(Hibernate.INTEGER, "extract(second from ?1)") );
registerFunction( "minute", new SQLFunctionTemplate(Hibernate.INTEGER, "extract(minute from ?1)") );
@@ -164,7 +174,7 @@
registerFunction( "day", new SQLFunctionTemplate(Hibernate.INTEGER, "extract(day from ?1)") );
registerFunction( "month", new SQLFunctionTemplate(Hibernate.INTEGER, "extract(month from ?1)") );
registerFunction( "year", new SQLFunctionTemplate(Hibernate.INTEGER, "extract(year from ?1)") );
-
+
registerFunction( "str", new SQLFunctionTemplate(Hibernate.STRING, "cast(?1 as char)") );
// register hibernate types for default use in scalar sqlquery type auto detection
@@ -186,27 +196,74 @@
registerHibernateType( Types.DECIMAL, Hibernate.BIG_DECIMAL.getName() );
registerHibernateType( Types.BLOB, Hibernate.BLOB.getName() );
registerHibernateType( Types.CLOB, Hibernate.CLOB.getName() );
-
}
- public String toString() {
- return getClass().getName();
+ /**
+ * Get an instance of the dialect specified by the current <tt>System</tt> properties.
+ *
+ * @return The specified Dialect
+ * @throws HibernateException If no dialect was specified, or if it could not be instantiated.
+ */
+ public static Dialect getDialect() throws HibernateException {
+ String dialectName = Environment.getProperties().getProperty( Environment.DIALECT );
+ return instantiateDialect( dialectName );
}
+
/**
- * Characters used for quoting SQL identifiers
+ * Get an instance of the dialect specified by the given properties or by
+ * the current <tt>System</tt> properties.
+ *
+ * @param props The properties to use for finding the dialect class to use.
+ * @return The specified Dialect
+ * @throws HibernateException If no dialect was specified, or if it could not be instantiated.
*/
- public static final String QUOTE = "`\"[";
- public static final String CLOSED_QUOTE = "`\"]";
+ public static Dialect getDialect(Properties props) throws HibernateException {
+ String dialectName = props.getProperty( Environment.DIALECT );
+ if ( dialectName == null ) {
+ return getDialect();
+ }
+ return instantiateDialect( dialectName );
+ }
+ private static Dialect instantiateDialect(String dialectName) throws HibernateException {
+ if ( dialectName == null ) {
+ throw new HibernateException( "The dialect was not set. Set the property hibernate.dialect." );
+ }
+ try {
+ return ( Dialect ) ReflectHelper.classForName( dialectName ).newInstance();
+ }
+ catch ( ClassNotFoundException cnfe ) {
+ throw new HibernateException( "Dialect class not found: " + dialectName );
+ }
+ catch ( Exception e ) {
+ throw new HibernateException( "Could not instantiate dialect class", e );
+ }
+ }
/**
+ * Retrieve a set of default Hibernate properties for this database.
+ *
+ * @return a set of Hibernate properties
+ */
+ public final Properties getDefaultProperties() {
+ return properties;
+ }
+
+ public String toString() {
+ return getClass().getName();
+ }
+
+
+ // database type mapping support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
* Get the name of the database type associated with the given
- * <tt>java.sql.Types</tt> typecode.
+ * {@link java.sql.Types} typecode.
*
- * @param code <tt>java.sql.Types</tt> typecode
+ * @param code The {@link java.sql.Types} typecode
* @return the database type name
- * @throws HibernateException
+ * @throws HibernateException If no mapping was specified for that type.
*/
public String getTypeName(int code) throws HibernateException {
String result = typeNames.get( code );
@@ -216,355 +273,310 @@
return result;
}
-
- public String getHibernateTypeName(int code) throws HibernateException {
- String result = hibernateTypeNames.get( code );
- if ( result == null ) {
- throw new HibernateException(
- "No Hibernate type mapping for java.sql.Types code: " +
- code);
- }
- return result;
- }
-
- public String getHibernateTypeName(int code, int length, int precision, int scale) throws HibernateException {
- String result = hibernateTypeNames.get( code, length, precision, scale );
- if ( result == null ) {
- throw new HibernateException(
- "No Hibernate type mapping for java.sql.Types code: " +
- code +
- ", length: " +
- length
- );
- }
- return result;
- }
-
/**
* Get the name of the database type associated with the given
- * <tt>java.sql.Types</tt> typecode.
- * @param code <tt>java.sql.Types</tt> typecode
- * @param length the length or precision of the column
- * @param precision the precision of the column
- * @param scale the scale of the column
+ * {@link java.sql.Types} typecode with the given storage specification
+ * parameters.
*
+ * @param code The {@link java.sql.Types} typecode
+ * @param length The datatype length
+ * @param precision The datatype precision
+ * @param scale The datatype scale
* @return the database type name
- * @throws HibernateException
+ * @throws HibernateException If no mapping was specified for that type.
*/
public String getTypeName(int code, int length, int precision, int scale) throws HibernateException {
String result = typeNames.get( code, length, precision, scale );
if ( result == null ) {
- throw new HibernateException(
+ throw new HibernateException(
"No type mapping for java.sql.Types code: " +
code +
", length: " +
- length
- );
+ length
+ );
}
return result;
}
-
+
+ /**
+ * Get the name of the database type appropriate for casting operations
+ * (via the CAST() SQL function) for the given {@link java.sql.Types} typecode.
+ *
+ * @param code The {@link java.sql.Types} typecode
+ * @return The database type name
+ */
public String getCastTypeName(int code) {
- return getTypeName(
- code,
- Column.DEFAULT_LENGTH,
- Column.DEFAULT_PRECISION,
- Column.DEFAULT_SCALE
- );
+ return getTypeName( code, Column.DEFAULT_LENGTH, Column.DEFAULT_PRECISION, Column.DEFAULT_SCALE );
}
- protected void registerFunction(String name, SQLFunction function) {
- sqlFunctions.put( name, function );
- }
-
- protected void registerKeyword(String word) {
- sqlKeywords.add(word);
- }
-
- public Set getKeywords() {
- return sqlKeywords;
- }
-
/**
- * Subclasses register a typename for the given type code and maximum
+ * Subclasses register a type name for the given type code and maximum
* column length. <tt>$l</tt> in the type name with be replaced by the
* column length (if appropriate).
*
- * @param code <tt>java.sql.Types</tt> typecode
- * @param capacity maximum length of database type
- * @param name the database type name
+ * @param code The {@link java.sql.Types} typecode
+ * @param capacity The maximum length of database type
+ * @param name The database type name
*/
protected void registerColumnType(int code, int capacity, String name) {
typeNames.put( code, capacity, name );
}
/**
- * Subclasses register a typename for the given type code. <tt>$l</tt> in
+ * Subclasses register a type name for the given type code. <tt>$l</tt> in
* the type name with be replaced by the column length (if appropriate).
*
- * @param code <tt>java.sql.Types</tt> typecode
- * @param name the database type name
+ * @param code The {@link java.sql.Types} typecode
+ * @param name The database type name
*/
protected void registerColumnType(int code, String name) {
typeNames.put( code, name );
}
-
- protected void registerHibernateType(int sqlcode, String name) {
- hibernateTypeNames.put( sqlcode, name);
- }
- protected void registerHibernateType(int sqlcode, int capacity, String name) {
- hibernateTypeNames.put( sqlcode, capacity, name);
- }
- /**
- * Does this dialect support the <tt>ALTER TABLE</tt> syntax?
- *
- * @return boolean
- */
- public boolean hasAlterTable() {
- return true;
- }
+ // hibernate type mapping support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
- * Do we need to drop constraints before dropping tables in this dialect?
+ * Get the name of the Hibernate {@link org.hibernate.type.Type} associated with th given
+ * {@link java.sql.Types} typecode.
*
- * @return boolean
+ * @param code The {@link java.sql.Types} typecode
+ * @return The Hibernate {@link org.hibernate.type.Type} name.
+ * @throws HibernateException If no mapping was specified for that type.
*/
- public boolean dropConstraints() {
- return true;
+ public String getHibernateTypeName(int code) throws HibernateException {
+ String result = hibernateTypeNames.get( code );
+ if ( result == null ) {
+ throw new HibernateException( "No Hibernate type mapping for java.sql.Types code: " + code );
+ }
+ return result;
}
/**
- * Do we need to qualify index names with the schema name?
+ * Get the name of the Hibernate {@link org.hibernate.type.Type} associated
+ * with the given {@link java.sql.Types} typecode with the given storage
+ * specification parameters.
*
- * @return boolean
+ * @param code The {@link java.sql.Types} typecode
+ * @param length The datatype length
+ * @param precision The datatype precision
+ * @param scale The datatype scale
+ * @return The Hibernate {@link org.hibernate.type.Type} name.
+ * @throws HibernateException If no mapping was specified for that type.
*/
- public boolean qualifyIndexName() {
- return true;
+ public String getHibernateTypeName(int code, int length, int precision, int scale) throws HibernateException {
+ String result = hibernateTypeNames.get( code, length, precision, scale );
+ if ( result == null ) {
+ throw new HibernateException(
+ "No Hibernate type mapping for java.sql.Types code: " +
+ code +
+ ", length: " +
+ length
+ );
+ }
+ return result;
}
/**
- * Does the <tt>FOR UPDATE OF</tt> syntax specify particular
- * columns?
- */
- public boolean forUpdateOfColumns() {
- return false;
- }
-
- /**
- * Retrieves the <tt>FOR UPDATE OF column_list</tt> syntax specific to this
- * dialect, where the given aliases represent the aliases of the columns
- * which are to be write locked.
+ * Registers a Hibernate {@link org.hibernate.type.Type} name for the given
+ * {@link java.sql.Types} type code and maximum column length.
*
- * @return The appropriate <tt>FOR UPDATE OF column_list</tt> clause string.
+ * @param code The {@link java.sql.Types} typecode
+ * @param capacity The maximum length of database type
+ * @param name The Hibernate {@link org.hibernate.type.Type} name
*/
- public String getForUpdateString(String aliases) {
- return getForUpdateString();
+ protected void registerHibernateType(int code, int capacity, String name) {
+ hibernateTypeNames.put( code, capacity, name);
}
/**
- * Retrieves the <tt>FOR UPDATE OF column_list NOWAIT</tt> syntax specific
- * to this dialect, where the given aliases represent the aliases of the
- * columns which are to be write locked.
+ * Registers a Hibernate {@link org.hibernate.type.Type} name for the given
+ * {@link java.sql.Types} type code.
*
- * @return The appropriate <tt>FOR UPDATE colunm_list</tt> NOWAIT clause string.
+ * @param code The {@link java.sql.Types} typecode
+ * @param name The Hibernate {@link org.hibernate.type.Type} name
*/
- public String getForUpdateNowaitString(String aliases) {
- return getForUpdateString( aliases );
+ protected void registerHibernateType(int code, String name) {
+ hibernateTypeNames.put( code, name);
}
- /**
- * Retrieves the <tt>FOR UPDATE</tt> syntax specific to this dialect.
- *
- * @return The appropriate <tt>FOR UPDATE</tt> clause string.
- */
- public String getForUpdateString() {
- return " for update";
- }
- /**
- * Retrieves the <tt>FOR UPDATE NOWAIT</tt> syntax specific to this dialect.
- *
- * @return The appropriate <tt>FOR UPDATE NOWAIT</tt> clause string.
- */
- public String getForUpdateNowaitString() {
- return getForUpdateString();
+ // function support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ protected void registerFunction(String name, SQLFunction function) {
+ sqlFunctions.put( name, function );
}
/**
- * Does this dialect support the <tt>UNIQUE</tt> column syntax?
+ * Retrieves a map of the dialect's registered fucntions
+ * (functionName => {@link org.hibernate.dialect.function.SQLFunction}).
*
- * @return boolean
+ * @return The map of registered functions.
*/
- public boolean supportsUnique() {
- return true;
+ public final Map getFunctions() {
+ return sqlFunctions;
}
-
- /**
- * Does this dialect support adding Unique constraints via create and alter table ?
- * @return boolean
- */
- public boolean supportsUniqueConstraintInCreateAlterTable() {
- return true;
- }
+ // keyword support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- /**
- * The syntax used to add a column to a table (optional).
- */
- public String getAddColumnString() {
- throw new UnsupportedOperationException( "No add column syntax supported by Dialect" );
+ protected void registerKeyword(String word) {
+ sqlKeywords.add(word);
}
- public String getDropForeignKeyString() {
- return " drop constraint ";
+ public Set getKeywords() {
+ return sqlKeywords;
}
- public String getTableTypeString() {
- return "";
- }
+ // native identifier generatiion ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
/**
- * The syntax used to add a foreign key constraint to a table.
- *
- * @param referencesPrimaryKey if false, constraint should be
- * explicit about which column names the constraint refers to
+ * The class (which implements {@link org.hibernate.id.IdentifierGenerator})
+ * which acts as this dialects native generation strategy.
+ * <p/>
+ * Comes into play whenever the user specifies the native generator.
*
- * @return String
+ * @return The native generator class.
*/
- public String getAddForeignKeyConstraintString(
- String constraintName,
- String[] foreignKey,
- String referencedTable,
- String[] primaryKey,
- boolean referencesPrimaryKey
- ) {
- StringBuffer res = new StringBuffer( 30 );
-
- res.append( " add constraint " )
- .append( constraintName )
- .append( " foreign key (" )
- .append( StringHelper.join( ", ", foreignKey ) )
- .append( ") references " )
- .append( referencedTable );
-
- if(!referencesPrimaryKey) {
- res.append(" (")
- .append( StringHelper.join(", ", primaryKey) )
- .append(')');
+ public Class getNativeIdentifierGeneratorClass() {
+ if ( supportsIdentityColumns() ) {
+ return IdentityGenerator.class;
}
-
- return res.toString();
+ else if ( supportsSequences() ) {
+ return SequenceGenerator.class;
+ }
+ else {
+ return TableHiLoGenerator.class;
+ }
}
- /**
- * The syntax used to add a primary key constraint to a table.
- *
- * @return String
- */
- public String getAddPrimaryKeyConstraintString(String constraintName) {
- return " add constraint " + constraintName + " primary key ";
- }
+ // IDENTITY support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
/**
- * The keyword used to specify a nullable column.
+ * Does this dialect support identity column key generation?
*
- * @return String
+ * @return True if IDENTITY columns are supported; false otherwise.
*/
- public String getNullColumnString() {
- return "";
+ public boolean supportsIdentityColumns() {
+ return false;
}
/**
- * Does this dialect support identity column key generation?
+ * Does the dialect support some form of inserting and selecting
+ * the generated IDENTITY value all in the same statement.
*
- * @return boolean
+ * @return True if the dialect supports selecting the just
+ * generated IDENTITY in the insert statement.
*/
- public boolean supportsIdentityColumns() {
+ public boolean supportsInsertSelectIdentity() {
return false;
}
/**
- * Does this dialect support sequences?
+ * Whether this dialect have an Identity clause added to the data type or a
+ * completely seperate identity data type
*
* @return boolean
*/
- public boolean supportsSequences() {
- return false;
+ public boolean hasDataTypeInIdentityColumn() {
+ return true;
}
- public boolean supportsInsertSelectIdentity() {
- return false;
- }
-
/**
- * Append a clause to retrieve the generated identity value for the
- * given <tt>INSERT</tt> statement.
+ * Provided we {@link #supportsInsertSelectIdentity}, then attch the
+ * "select identity" clause to the insert statement.
+ * <p/>
+ * Note, if {@link #supportsInsertSelectIdentity} == false then
+ * the insert-string should be returned without modification.
+ *
+ * @param insertString The insert command
+ * @return The insert command with any necessary identity select
+ * clause attached.
*/
public String appendIdentitySelectToInsert(String insertString) {
return insertString;
}
- protected String getIdentitySelectString() throws MappingException {
- throw new MappingException( "Dialect does not support identity key generation" );
- }
-
/**
- * The syntax that returns the identity value of the last insert, if
- * identity column key generation is supported.
+ * Get the select command to use to retrieve the last generated IDENTITY
+ * value for a particuar table
*
- * @param type TODO
- * @throws MappingException if no native key generation
+ * @param table The table into which the insert was done
+ * @param column The PK column.
+ * @param type The {@link java.sql.Types} type code.
+ * @return The appropriate select command
+ * @throws MappingException If IDENTITY generation is not supported.
*/
- public String getIdentitySelectString(String table, String column, int type)
- throws MappingException {
+ public String getIdentitySelectString(String table, String column, int type) throws MappingException {
return getIdentitySelectString();
}
- protected String getIdentityColumnString() throws MappingException {
+ /**
+ * Get the select command to use to retrieve the last generated IDENTITY
+ * value.
+ *
+ * @return The appropriate select command
+ * @throws MappingException If IDENTITY generation is not supported.
+ */
+ protected String getIdentitySelectString() throws MappingException {
throw new MappingException( "Dialect does not support identity key generation" );
}
/**
- * The keyword used to specify an identity column, if identity
- * column key generation is supported.
+ * The syntax used during DDL to define a column as being an IDENTITY of
+ * a particular type.
*
- * @param type the SQL column type, as defined by <tt>java.sql.Types</tt>
- * @throws MappingException if no native key generation
+ * @param type The {@link java.sql.Types} type code.
+ * @return The appropriate DDL fragment.
+ * @throws MappingException If IDENTITY generation is not supported.
*/
public String getIdentityColumnString(int type) throws MappingException {
return getIdentityColumnString();
}
/**
+ * The syntax used during DDL to define a column as being an IDENTITY.
+ *
+ * @return The appropriate DDL fragment.
+ * @throws MappingException If IDENTITY generation is not supported.
+ */
+ protected String getIdentityColumnString() throws MappingException {
+ throw new MappingException( "Dialect does not support identity key generation" );
+ }
+
+ /**
* The keyword used to insert a generated value into an identity column (or null).
* Need if the dialect does not support inserts that specify no column values.
*
- * @return String
+ * @return The appropriate keyword.
*/
public String getIdentityInsertString() {
return null;
}
+
+ // SEQUENCE support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
/**
- * The keyword used to insert a row without specifying any column values.
- * This is not possible on some databases.
+ * Does this dialect support sequences?
+ *
+ * @return True if sequences supported; false otherwise.
*/
- public String getNoColumnsInsertString() {
- return "values ( )";
+ public boolean supportsSequences() {
+ return false;
}
/**
* Generate the appropriate select statement to to retreive the next value
- * of a sequence, if sequences are supported.
+ * of a sequence.
* <p/>
* This should be a "stand alone" select statement.
*
* @param sequenceName the name of the sequence
* @return String The "nextval" select string.
- * @throws MappingException if no sequences
+ * @throws MappingException If sequences are not supported.
*/
public String getSequenceNextValString(String sequenceName) throws MappingException {
throw new MappingException( "Dialect does not support sequences" );
@@ -572,322 +584,467 @@
/**
* Generate the select expression fragment that will retreive the next
- * value of a sequence, if sequences are supported.
+ * value of a sequence as part of another (typically DML) statement.
* <p/>
* This differs from {@link #getSequenceNextValString(String)} in that this
- * should return an expression usable within another select statement.
+ * should return an expression usable within another statement.
*
* @param sequenceName the name of the sequence
- * @return String
- * @throws MappingException if no sequences
+ * @return The "nextval" fragment.
+ * @throws MappingException If sequences are not supported.
*/
public String getSelectSequenceNextValString(String sequenceName) throws MappingException {
throw new MappingException( "Dialect does not support sequences" );
}
/**
- * The syntax used to create a sequence, if sequences are supported.
+ * The multiline script used to create a sequence.
*
- * @param sequenceName the name of the sequence
- * @return String
- * @throws MappingException if no sequences
+ * @param sequenceName The name of the sequence
+ * @return The sequence creation commands
+ * @throws MappingException If sequences are not supported.
*/
- protected String getCreateSequenceString(String sequenceName) throws MappingException {
- throw new MappingException( "Dialect does not support sequences" );
+ public String[] getCreateSequenceStrings(String sequenceName) throws MappingException {
+ return new String[] { getCreateSequenceString( sequenceName ) };
}
/**
- * The multiline script used to create a sequence, if sequences are supported.
+ * Typically dialects which support sequences can create a sequence
+ * with a single command. This is convenience form of
+ * {@link #getCreateSequenceStrings} to help facilitate that.
+ * <p/>
+ * Dialects which support sequences and can create a sequence in a
+ * single command need *only* override this method. Dialects
+ * which support sequences but require multiple commands to create
+ * a sequence should instead override {@link #getCreateSequenceStrings}.
*
- * @param sequenceName the name of the sequence
- * @return String[]
- * @throws MappingException if no sequences
+ * @param sequenceName The name of the sequence
+ * @return The sequence creation command
+ * @throws MappingException If sequences are not supported.
*/
- public String[] getCreateSequenceStrings(String sequenceName) throws MappingException {
- return new String[]{getCreateSequenceString( sequenceName )};
+ protected String getCreateSequenceString(String sequenceName) throws MappingException {
+ throw new MappingException( "Dialect does not support sequences" );
}
/**
- * The syntax used to drop a sequence, if sequences are supported.
+ * The multiline script used to drop a sequence.
*
- * @param sequenceName the name of the sequence
- * @return String
- * @throws MappingException if no sequences
+ * @param sequenceName The name of the sequence
+ * @return The sequence drop commands
+ * @throws MappingException If sequences are not supported.
*/
- protected String getDropSequenceString(String sequenceName) throws MappingException {
- throw new MappingException( "Dialect does not support sequences" );
+ public String[] getDropSequenceStrings(String sequenceName) throws MappingException {
+ return new String[]{getDropSequenceString( sequenceName )};
}
/**
- * The multiline script used to drop a sequence, if sequences are supported.
+ * Typically dialects which support sequences can drop a sequence
+ * with a single command. This is convenience form of
+ * {@link #getDropSequenceStrings} to help facilitate that.
+ * <p/>
+ * Dialects which support sequences and can drop a sequence in a
+ * single command need *only* override this method. Dialects
+ * which support sequences but require multiple commands to drop
+ * a sequence should instead override {@link #getDropSequenceStrings}.
*
- * @param sequenceName the name of the sequence
- * @return String[]
- * @throws MappingException if no sequences
+ * @param sequenceName The name of the sequence
+ * @return The sequence drop commands
+ * @throws MappingException If sequences are not supported.
*/
- public String[] getDropSequenceStrings(String sequenceName) throws MappingException {
- return new String[]{getDropSequenceString( sequenceName )};
+ protected String getDropSequenceString(String sequenceName) throws MappingException {
+ throw new MappingException( "Dialect does not support sequences" );
}
/**
- * A query used to find all sequences
+ * Get the select command used retrieve the names of all sequences.
*
+ * @return The select command; or null if sequences are not supported.
* @see org.hibernate.tool.hbm2ddl.SchemaUpdate
*/
public String getQuerySequencesString() {
return null;
}
+
+ // GUID support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
/**
- * Get the <tt>Dialect</tt> specified by the current <tt>System</tt> properties.
+ * Get the command used to select a GUID from the underlying database.
+ * <p/>
+ * Optional operation.
*
- * @return Dialect
- * @throws HibernateException
+ * @return The appropriate command.
*/
- public static Dialect getDialect() throws HibernateException {
- String dialectName = Environment.getProperties().getProperty( Environment.DIALECT );
- if ( dialectName == null ) throw new HibernateException( "The dialect was not set. Set the property hibernate.dialect." );
- try {
- return ( Dialect ) ReflectHelper.classForName( dialectName ).newInstance();
- }
- catch ( ClassNotFoundException cnfe ) {
- throw new HibernateException( "Dialect class not found: " + dialectName );
- }
- catch ( Exception e ) {
- throw new HibernateException( "Could not instantiate dialect class", e );
- }
+ public String getSelectGUIDString() {
+ throw new UnsupportedOperationException( "dialect does not support GUIDs" );
}
+ // limit/offset support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
/**
- * Get the <tt>Dialect</tt> specified by the given properties or system properties.
+ * Does this dialect support some form of limiting query results
+ * via a SQL clause?
*
- * @param props
- * @return Dialect
- * @throws HibernateException
+ * @return True if this dialect supports some form of LIMIT.
*/
- public static Dialect getDialect(Properties props) throws HibernateException {
- String dialectName = props.getProperty( Environment.DIALECT );
- if ( dialectName == null ) return getDialect();
- try {
- return ( Dialect ) ReflectHelper.classForName( dialectName ).newInstance();
- }
- catch ( ClassNotFoundException cnfe ) {
- throw new HibernateException( "Dialect class not found: " + dialectName );
- }
- catch ( Exception e ) {
- throw new HibernateException( "Could not instantiate dialect class", e );
- }
+ public boolean supportsLimit() {
+ return false;
}
/**
- * Retrieve a set of default Hibernate properties for this database.
+ * Does this dialect's LIMIT support (if any) additionally
+ * support specifying an offset?
*
- * @return a set of Hibernate properties
+ * @return True if the dialect supports an offset within the limit support.
*/
- public final Properties getDefaultProperties() {
- return properties;
+ public boolean supportsLimitOffset() {
+ return supportsLimit();
}
/**
- * Completely optional cascading drop clause
+ * Does this dialect support bind variables (i.e., prepared statememnt
+ * parameters) for its limit/offset?
*
- * @return String
+ * @return True if bind variables can be used; false otherwise.
*/
- public String getCascadeConstraintsString() {
- return "";
+ public boolean supportsVariableLimit() {
+ return supportsLimit();
}
/**
- * Create an <tt>OuterJoinGenerator</tt> for this dialect.
+ * ANSI SQL defines the LIMIT clause to be in the form LIMIT offset, limit.
+ * Does this dialect require us to bind the parameters in reverse order?
*
- * @return OuterJoinGenerator
+ * @return true if the correct order is limit, offset
*/
- public JoinFragment createOuterJoinFragment() {
- return new ANSIJoinFragment();
+ public boolean bindLimitParametersInReverseOrder() {
+ return false;
}
/**
- * Create a <tt>CaseFragment</tt> for this dialect.
+ * Does the <tt>LIMIT</tt> clause come at the start of the
+ * <tt>SELECT</tt> statement, rather than at the end?
*
- * @return OuterJoinGenerator
+ * @return true if limit parameters should come before other parameters
*/
- public CaseFragment createCaseFragment() {
- return new ANSICaseFragment();
+ public boolean bindLimitParametersFirst() {
+ return false;
}
/**
- * The name of the SQL function that transforms a string to
- * lowercase
+ * Does the <tt>LIMIT</tt> clause take a "maximum" row number instead
+ * of a total number of returned rows?
+ * <p/>
+ * This is easiest understood via an example. Consider you have a table
+ * with 20 rows, but you only want to retrieve rows number 11 through 20.
+ * Generally, a limit with offset would say that the offset = 11 and the
+ * limit = 10 (we only want 10 rows at a time); this is specifying the
+ * total number of returned rows. Some dialects require that we instead
+ * specify offset = 11 and limit = 20, where 20 is the "last" row we want
+ * relative to offset (i.e. total number of rows = 20 - 11 = 9)
+ * <p/>
+ * So essentially, is limit relative from offset? Or is limit absolute?
*
- * @return String
+ * @return True if limit is relative from offset; false otherwise.
*/
- public String getLowercaseFunction() {
- return "lower";
+ public boolean useMaxForLimit() {
+ return false;
}
/**
- * Does this <tt>Dialect</tt> have some kind of <tt>LIMIT</tt> syntax?
+ * Given a limit and an offset, apply the limit clause to the query.
+ *
+ * @param query The query to which to apply the limit.
+ * @param offset The offset of the limit
+ * @param limit The limit of the limit ;)
+ * @return The modified query statement with the limit applied.
*/
- public boolean supportsLimit() {
- return false;
+ public String getLimitString(String query, int offset, int limit) {
+ return getLimitString( query, offset > 0 );
}
/**
- * Does this dialect support an offset?
+ * Apply s limit clause to the query.
+ * <p/>
+ * Typically dialects utilize {@link #supportsVariableLimit() variable}
+ * limit caluses when they support limits. Thus, when building the
+ * select command we do not actually need to know the limit or the offest
+ * since we will just be using placeholders.
+ * <p/>
+ * Here we do still pass along whether or not an offset was specified
+ * so that dialects not supporting offsets can generate proper exceptions.
+ * In general, dialects will override one or the other of this method and
+ * {@link #getLimitString(String, int, int)}.
+ *
+ * @param query The query to which to apply the limit.
+ * @param hasOffset Is the query requesting an offset?
+ * @return the modified SQL
*/
- public boolean supportsLimitOffset() {
- return supportsLimit();
+ protected String getLimitString(String query, boolean hasOffset) {
+ throw new UnsupportedOperationException( "paged queries not supported" );
}
+
+ // lock acquisition support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
/**
- * Add a <tt>LIMIT</tt> clause to the given SQL <tt>SELECT</tt>
+ * Get a strategy instance which knows how to acquire a database-level lock
+ * of the specified mode for this dialect.
*
- * @return the modified SQL
+ * @param lockable The persister for the entity to be locked.
+ * @param lockMode The type of lock to be acquired.
+ * @return The appropriate locking strategy.
+ * @since 3.2
*/
- public String getLimitString(String querySelect, boolean hasOffset) {
- throw new UnsupportedOperationException( "paged queries not supported" );
+ public LockingStrategy getLockingStrategy(Lockable lockable, LockMode lockMode) {
+ return new SelectLockingStrategy( lockable, lockMode );
}
- public String getLimitString(String querySelect, int offset, int limit) {
- return getLimitString( querySelect, offset>0 );
+ /**
+ * Given a lock mode, determine the appropriate for update fragment to use.
+ *
+ * @param lockMode The lock mode to apply.
+ * @return The appropriate for update fragment.
+ */
+ public String getForUpdateString(LockMode lockMode) {
+ if ( lockMode==LockMode.UPGRADE ) {
+ return getForUpdateString();
+ }
+ else if ( lockMode==LockMode.UPGRADE_NOWAIT ) {
+ return getForUpdateNowaitString();
+ }
+ else if ( lockMode==LockMode.FORCE ) {
+ return getForUpdateNowaitString();
+ }
+ else {
+ return "";
+ }
}
- public boolean supportsVariableLimit() {
- return supportsLimit();
+ /**
+ * Get the string to append to SELECT statements to acquire locks
+ * for this dialect.
+ *
+ * @return The appropriate <tt>FOR UPDATE</tt> clause string.
+ */
+ public String getForUpdateString() {
+ return " for update";
}
/**
- * Does the <tt>LIMIT</tt> clause specify arguments in the "reverse" order
- * limit, offset instead of offset, limit?
+ * Is <tt>FOR UPDATE OF</tt> syntax supported?
*
- * @return true if the correct order is limit, offset
+ * @return True if the database supports <tt>FOR UPDATE OF</tt> syntax;
+ * false otherwise.
*/
- public boolean bindLimitParametersInReverseOrder() {
+ public boolean forUpdateOfColumns() {
+ // by default we report no support
return false;
}
/**
- * Does the <tt>LIMIT</tt> clause come at the start of the
- * <tt>SELECT</tt> statement, rather than at the end?
+ * Does this dialect support <tt>FOR UPDATE</tt> in conjunction with
+ * outer joined rows?
*
- * @return true if limit parameters should come before other parameters
+ * @return True if outer joined rows can be locked via <tt>FOR UPDATE</tt>.
*/
- public boolean bindLimitParametersFirst() {
- return false;
+ public boolean supportsOuterJoinForUpdate() {
+ return true;
}
/**
- * Does the <tt>LIMIT</tt> clause take a "maximum" row number instead
- * of a total number of returned rows?
+ * Get the <tt>FOR UPDATE OF column_list</tt> fragment appropriate for this
+ * dialect given the aliases of the columns to be write locked.
+ *
+ * @param aliases The columns to be write locked.
+ * @return The appropriate <tt>FOR UPDATE OF column_list</tt> clause string.
*/
- public boolean useMaxForLimit() {
- return false;
+ public String getForUpdateString(String aliases) {
+ // by default we simply return the getForUpdateString() result since
+ // the default is to say no support for "FOR UPDATE OF ..."
+ return getForUpdateString();
}
/**
- * The opening quote for a quoted identifier
+ * Retrieves the <tt>FOR UPDATE NOWAIT</tt> syntax specific to this dialect.
+ *
+ * @return The appropriate <tt>FOR UPDATE NOWAIT</tt> clause string.
*/
- public char openQuote() {
- return '"';
+ public String getForUpdateNowaitString() {
+ // by default we report no support for NOWAIT lock semantics
+ return getForUpdateString();
}
/**
- * The closing quote for a quoted identifier
+ * Get the <tt>FOR UPDATE OF column_list NOWAIT</tt> fragment appropriate
+ * for this dialect given the aliases of the columns to be write locked.
+ *
+ * @param aliases The columns to be write locked.
+ * @return The appropriate <tt>FOR UPDATE colunm_list NOWAIT</tt> clause string.
*/
- public char closeQuote() {
- return '"';
+ public String getForUpdateNowaitString(String aliases) {
+ return getForUpdateString( aliases );
}
/**
- * SQL functions as defined in general. The results of this
- * method should be integrated with the specialisation's data.
+ * Some dialects support an alternative means to <tt>SELECT FOR UPDATE</tt>,
+ * whereby a "lock hint" is appends to the table name in the from clause.
+ * <p/>
+ * contributed by <a href="http://sourceforge.net/users/heschulz">Helge Schulz</a>
+ *
+ * @param mode The lock mode to apply
+ * @param tableName The name of the table to which to apply the lock hint.
+ * @return The table with any required lock hints.
*/
- public final Map getFunctions() {
- return sqlFunctions;
+ public String appendLockHint(LockMode mode, String tableName) {
+ return tableName;
}
- public boolean supportsIfExistsBeforeTableName() {
+
+ // temporary table support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Does this dialect support temporary tables?
+ *
+ * @return True if temp tables are supported; false otherwise.
+ */
+ public boolean supportsTemporaryTables() {
return false;
}
- public boolean supportsIfExistsAfterTableName() {
- return false;
+ /**
+ * Generate a temporary table name given the bas table.
+ *
+ * @param baseTableName The table name from which to base the temp table name.
+ * @return The generated temp table name.
+ */
+ public String generateTemporaryTableName(String baseTableName) {
+ return "HT_" + baseTableName;
}
-
+
/**
- * Does this dialect support column-level check constraints?
+ * Command used to create a temporary table.
+ *
+ * @return The command used to create a temporary table.
*/
- public boolean supportsColumnCheck() {
- return true;
+ public String getCreateTemporaryTableString() {
+ return "create table";
}
-
+
/**
- * Does this dialect support table-level check constraints?
+ * Get any fragments needing to be postfixed to the command for
+ * temporary table creation.
+ *
+ * @return Any required postfix.
*/
- public boolean supportsTableCheck() {
- return true;
+ public String getCreateTemporaryTablePostfix() {
+ return "";
}
/**
- * Whether this dialect have an Identity clause added to the data type or a
- * completely seperate identity data type
+ * Does the dialect require that temporary table DDL statements
+ * occur in isolation from other statements?
+ * todo : perhaps have this return java.lang.Boolean instead where null means to use the value returned by the driver.
*
- * @return boolean
+ * @return
*/
- public boolean hasDataTypeInIdentityColumn() {
- return true;
+ public boolean performTemporaryTableDDLInIsolation() {
+ return false;
}
- public boolean supportsCascadeDelete() {
+ /**
+ * Do we need to drop the temporary table after use?
+ *
+ * @return True if the table should be dropped.
+ */
+ public boolean dropTemporaryTableAfterUse() {
return true;
}
+
+ // callable statement support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
/**
- * Method <code>appendLockHint</code> appends according to the given
- * lock mode a lock hint behind the given table name, if this dialect
- * needs this. MS SQL Server for example doesn't support the
- * standard "<code>select ... for update</code>" syntax and use a
- * special "<code>select ... from TABLE as ALIAS with (updlock, rowlock)
- * where ...</code>" syntax instead.
+ * Registers an OUT parameter which will be returing a
+ * {@link java.sql.ResultSet}. How this is accomplished varies greatly
+ * from DB to DB, hence its inclusion (along with {@link #getResultSet}) here.
*
- * @param tableName name of table to append lock hint
- * @return String
- * <p/>
- * author <a href="http://sourceforge.net/users/heschulz">Helge Schulz</a>
+ * @param statement The callable statement.
+ * @param position The bind position at which to register the OUT param.
+ * @return The number of (contiguous) bind positions used.
+ * @throws SQLException Indicates problems registering the OUT param.
*/
- public String appendLockHint(LockMode mode, String tableName) {
- return tableName;
+ public int registerResultSetOutParameter(CallableStatement statement, int position) throws SQLException {
+ throw new UnsupportedOperationException(
+ getClass().getName() +
+ " does not support resultsets via stored procedures"
+ );
}
- public Class getNativeIdentifierGeneratorClass() {
- if ( supportsIdentityColumns() ) {
- return IdentityGenerator.class;
- }
- else if ( supportsSequences() ) {
- return SequenceGenerator.class;
- }
- else {
- return TableHiLoGenerator.class;
- }
+ /**
+ * Given a callable statement previously processed by {@link #registerResultSetOutParameter},
+ * extract the {@link java.sql.ResultSet} from the OUT parameter.
+ *
+ * @param statement The callable statement.
+ * @return The extracted result set.
+ * @throws SQLException Indicates problems extracting the result set.
+ */
+ public ResultSet getResultSet(CallableStatement statement) throws SQLException {
+ throw new UnsupportedOperationException(
+ getClass().getName() +
+ " does not support resultsets via stored procedures"
+ );
}
- public String getSelectGUIDString() {
- throw new UnsupportedOperationException( "dialect does not support GUIDs" );
+ // current timestamp support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Does this dialect support a way to retrieve the database's current
+ * timestamp value?
+ *
+ * @return True if the current timestamp can be retrieved; false otherwise.
+ */
+ public boolean supportsCurrentTimestampSelection() {
+ return false;
}
- public boolean supportsOuterJoinForUpdate() {
- return true;
+ /**
+ * Should the value returned by {@link #getCurrentTimestampSelectString}
+ * be treated as callable. Typically this indicates that JDBC escape
+ * sytnax is being used...
+ *
+ * @return True if the {@link #getCurrentTimestampSelectString} return
+ * is callable; false otherwise.
+ */
+ public boolean isCurrentTimestampSelectStringCallable() {
+ throw new UnsupportedOperationException( "Database not known to define a current timestamp function" );
}
- public String getSelectClauseNullString(int sqlType) {
- return "null";
+ /**
+ * Retrieve the command used to retrieve the current timestammp from the
+ * database.
+ *
+ * @return The command.
+ */
+ public String getCurrentTimestampSelectString() {
+ throw new UnsupportedOperationException( "Database not known to define a current timestamp function" );
}
-
- public boolean supportsNotNullUnique() {
- return true;
+
+ /**
+ * The name of the database-specific SQL function for retrieving the
+ * current timestamp.
+ *
+ * @return The function name.
+ */
+ public String getCurrentTimestampSQLFunctionName() {
+ // the standard SQL function name is current_timestamp...
+ return "current_timestamp";
}
+
+ // SQLException support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
/**
* Build an instance of the SQLExceptionConverter preferred by this dialect for
* converting SQLExceptions into Hibernate's JDBCException hierarchy. The default
@@ -917,212 +1074,357 @@
return EXTRACTER;
}
- public final String quote(String column) {
- if ( column.charAt( 0 ) == '`' ) {
- return openQuote() + column.substring( 1, column.length() - 1 ) + closeQuote();
- }
- else {
- return column;
- }
+
+ // union subclass support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Given a {@link java.sql.Types} type code, determine an appropriate
+ * null value to use in a select clause.
+ * <p/>
+ * One thing to consider here is that certain databases might
+ * require proper casting for the nulls here since the select here
+ * will be part of a UNION/UNION ALL.
+ *
+ * @param sqlType The {@link java.sql.Types} type code.
+ * @return The appropriate select clause value fragment.
+ */
+ public String getSelectClauseNullString(int sqlType) {
+ return "null";
}
- public boolean hasSelfReferentialForeignKeyBug() {
+ /**
+ * Does this dialect support UNION ALL, which is generally a faster
+ * variant of UNION?
+ *
+ * @return True if UNION ALL is supported; false otherwise.
+ */
+ public boolean supportsUnionAll() {
return false;
}
-
- public boolean useInputStreamToInsertBlob() {
- return true;
+
+ // miscellaneous support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+ /**
+ * Create a {@link org.hibernate.sql.JoinFragment} strategy responsible
+ * for handling this dialect's variations in how joins are handled.
+ *
+ * @return This dialect's {@link org.hibernate.sql.JoinFragment} strategy.
+ */
+ public JoinFragment createOuterJoinFragment() {
+ return new ANSIJoinFragment();
}
- public int registerResultSetOutParameter(CallableStatement statement, int col) throws SQLException {
- throw new UnsupportedOperationException(
- getClass().getName() +
- " does not support resultsets via stored procedures"
- );
+ /**
+ * Create a {@link org.hibernate.sql.CaseFragment} strategy responsible
+ * for handling this dialect's variations in how CASE statements are
+ * handled.
+ *
+ * @return This dialect's {@link org.hibernate.sql.CaseFragment} strategy.
+ */
+ public CaseFragment createCaseFragment() {
+ return new ANSICaseFragment();
}
- public ResultSet getResultSet(CallableStatement ps) throws SQLException {
- throw new UnsupportedOperationException(
- getClass().getName() +
- " does not support resultsets via stored procedures"
- );
+ /**
+ * The fragment used to insert a row without specifying any column values.
+ * This is not possible on some databases.
+ *
+ * @return The appropriate empty values clause.
+ */
+ public String getNoColumnsInsertString() {
+ return "values ( )";
}
-
- public boolean supportsUnionAll() {
- return false;
+
+ /**
+ * The name of the SQL function that transforms a string to
+ * lowercase
+ *
+ * @return The dialect-specific lowercase function.
+ */
+ public String getLowercaseFunction() {
+ return "lower";
}
-
- public boolean supportsCommentOn() {
- return false;
+
+ /**
+ * Should LOBs (both BLOB and CLOB) be bound using stream operations (i.e.
+ * {@link java.sql.PreparedStatement#setBinaryStream}).
+ *
+ * @return True if BLOBs and CLOBs should be bound using stream operations.
+ */
+ public boolean useInputStreamToInsertBlob() {
+ return true;
}
-
- public String getTableComment(String comment) {
- return "";
- }
- public String getColumnComment(String comment) {
- return "";
- }
-
+ /**
+ * Meant as a means for end users to affect the select strings being sent
+ * to the database and perhaps manipulate them in some fashion.
+ * <p/>
+ * The recommend approach is to instead use
+ * {@link org.hibernate.Interceptor#onPrepareStatement(String)}.
+ *
+ * @param select The select command
+ * @return The mutated select command, or the same as was passed in.
+ */
public String transformSelectString(String select) {
return select;
}
- public boolean supportsTemporaryTables() {
- return false;
+ /**
+ * What is the maximum length Hibernate can use for generated aliases?
+ *
+ * @return The maximum length.
+ */
+ public int getMaxAliasLength() {
+ return 10;
}
- public String generateTemporaryTableName(String baseTableName) {
- return "HT_" + baseTableName;
+ /**
+ * The SQL literal value to which this database maps boolean values.
+ *
+ * @param bool The boolean value
+ * @return The appropriate SQL literal.
+ */
+ public String toBooleanValueString(boolean bool) {
+ return bool ? "1" : "0";
}
- public String getCreateTemporaryTableString() {
- return "create table";
+ /**
+ * Does this dialect support parameters within the select clause of
+ * INSERT ... SELECT ... statements?
+ *
+ * @return True if this is supported; false otherwise.
+ */
+ public boolean supportsParametersInInsertSelect() {
+ return true;
}
- public boolean performTemporaryTableDDLInIsolation() {
+ /**
+ * Is this dialect known to support what ANSI-SQL terms "row value
+ * constructor" syntax; sometimes called tuple syntax.
+ * <p/>
+ * Basically, does it support syntax like
+ * "... where (FIRST_NAME, LAST_NAME) = ('Steve', 'Ebersole') ...".
+ *
+ * @return True if this SQL dialect is known to support "row value
+ * constructor" syntax; false otherwise.
+ * @since 3.2
+ */
+ public boolean supportsRowValueConstructorSyntax() {
+ // return false here, as most databases do not properly support this construct...
return false;
}
- public String getCreateTemporaryTablePostfix() {
- return "";
+
+ // identifier quoting support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * The character specific to this dialect used to begin a quoted identifier.
+ *
+ * @return The dialect's specific open quote character.
+ */
+ public char openQuote() {
+ return '"';
}
- public boolean dropTemporaryTableAfterUse() {
- return true;
+ /**
+ * The character specific to this dialect used to close a quoted identifier.
+ *
+ * @return The dialect's specific close quote character.
+ */
+ public char closeQuote() {
+ return '"';
}
- public String getForUpdateString(LockMode lockMode) {
- if ( lockMode==LockMode.UPGRADE ) {
- return getForUpdateString();
+ /**
+ * Apply dialect-specific quoting.
+ * <p/>
+ * By default, the incoming value is checked to see if its first character
+ * is the back-tick (`). If so, the dialect specific quoting is applied.
+ *
+ * @param column The value to be quoted.
+ * @return The quoted (or unmodified, if not starting with back-tick) value.
+ * @see #openQuote()
+ * @see #closeQuote()
+ */
+ public final String quote(String column) {
+ if ( column.charAt( 0 ) == '`' ) {
+ return openQuote() + column.substring( 1, column.length() - 1 ) + closeQuote();
}
- else if ( lockMode==LockMode.UPGRADE_NOWAIT ) {
- return getForUpdateNowaitString();
- }
- else if ( lockMode==LockMode.FORCE ) {
- return getForUpdateString();
- }
else {
- return "";
+ return column;
}
}
-
- public int getMaxAliasLength() {
- return 10;
- }
- public boolean supportsCurrentTimestampSelection() {
- return false;
- }
- public String getCurrentTimestampSelectString() {
- throw new UnsupportedOperationException( "Database not known to define a current timestamp function" );
+ // DDL support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ /**
+ * Does this dialect support the <tt>ALTER TABLE</tt> syntax?
+ *
+ * @return True if we support altering of tables; false otherwise.
+ */
+ public boolean hasAlterTable() {
+ return true;
}
- public boolean isCurrentTimestampSelectStringCallable() {
- throw new UnsupportedOperationException( "Database not known to define a current timestamp function" );
- }
-
/**
- * The SQL value that the JDBC driver maps boolean values to
+ * Do we need to drop constraints before dropping tables in this dialect?
+ *
+ * @return True if constraints must be dropped prior to dropping
+ * the table; false otherwise.
*/
- public String toBooleanValueString(boolean bool) {
- return bool ? "1" : "0";
+ public boolean dropConstraints() {
+ return true;
}
/**
- * Does this dialect support parameters within the select clause of
- * INSERT ... SELECT ... statements?
+ * Do we need to qualify index names with the schema name?
*
- * @return True if this is supported; false otherwise.
+ * @return boolean
*/
- public boolean supportsParametersInInsertSelect() {
+ public boolean qualifyIndexName() {
return true;
}
/**
- * The name of the database-specific SQL function for retrieving the
- * current timestamp.
+ * Does this dialect support the <tt>UNIQUE</tt> column syntax?
*
- * @return The function name.
+ * @return boolean
*/
- public String getCurrentTimestampSQLFunctionName() {
- // the standard SQL function name is current_timestamp...
- return "current_timestamp";
+ public boolean supportsUnique() {
+ return true;
}
+ /**
+ * Does this dialect support adding Unique constraints via create and alter table ?
+ * @return boolean
+ */
+ public boolean supportsUniqueConstraintInCreateAlterTable() {
+ return true;
+ }
+
/**
- * Get a strategy instance which knows how to acquire a database-level lock
- * of the specified mode for this dialect.
+ * The syntax used to add a column to a table (optional).
+ */
+ public String getAddColumnString() {
+ throw new UnsupportedOperationException( "No add column syntax supported by Dialect" );
+ }
+
+ public String getDropForeignKeyString() {
+ return " drop constraint ";
+ }
+
+ public String getTableTypeString() {
+ // grrr... for differentiation of mysql storage engines
+ return "";
+ }
+
+ /**
+ * The syntax used to add a foreign key constraint to a table.
*
- * @param lockable The persister for the entity to be locked.
- * @param lockMode The type of lock to be acquired.
- * @return The appropriate locking strategy.
- * @since 3.2
+ * @param referencesPrimaryKey if false, constraint should be
+ * explicit about which column names the constraint refers to
+ *
+ * @return String
*/
- public LockingStrategy getLockingStrategy(Lockable lockable, LockMode lockMode) {
- return new SelectLockingStrategy( lockable, lockMode );
+ public String getAddForeignKeyConstraintString(
+ String constraintName,
+ String[] foreignKey,
+ String referencedTable,
+ String[] primaryKey,
+ boolean referencesPrimaryKey
+ ) {
+ StringBuffer res = new StringBuffer( 30 );
+
+ res.append( " add constraint " )
+ .append( constraintName )
+ .append( " foreign key (" )
+ .append( StringHelper.join( ", ", foreignKey ) )
+ .append( ") references " )
+ .append( referencedTable );
+
+ if(!referencesPrimaryKey) {
+ res.append(" (")
+ .append( StringHelper.join(", ", primaryKey) )
+ .append(')');
+ }
+
+ return res.toString();
}
/**
- * Is this dialect known to support what ANSI-SQL terms "row value
- * constructor" syntax; sometimes called tuple syntax.
- * <p/>
- * Basically, does it support syntax like
- * "... where (FIRST_NAME, LAST_NAME) = ('Steve', 'Ebersole') ...".
+ * The syntax used to add a primary key constraint to a table.
*
- * @return True if this SQL dialect is known to support "row value
- * constructor" syntax; false otherwise.
- * @since 3.2
+ * @return String
*/
- public boolean supportsRowValueConstructorSyntax() {
- // return false here, as most databases do not properly support this construct...
+ public String getAddPrimaryKeyConstraintString(String constraintName) {
+ return " add constraint " + constraintName + " primary key ";
+ }
+
+ public boolean hasSelfReferentialForeignKeyBug() {
return false;
}
/**
- * Which strategy should be used to reference columns in the "result" when
- * performing sorting (i.e. when processing an order by). This is
- * defined by ANSI SQL as the manner in which columns can be referred to in the
- * group-by, having, and order-by clauses.
- * <p/>
- * The default is to use {@link ResultColumnReferenceStrategy#SOURCE} since that
- * is the most performant.
+ * The keyword used to specify a nullable column.
*
- * @return The strategy to use.
+ * @return String
*/
- public ResultColumnReferenceStrategy getOrderByColumnReferenceStrategy() {
- return ResultColumnReferenceStrategy.SOURCE;
+ public String getNullColumnString() {
+ return "";
}
+ public boolean supportsCommentOn() {
+ return false;
+ }
+
+ public String getTableComment(String comment) {
+ return "";
+ }
+
+ public String getColumnComment(String comment) {
+ return "";
+ }
+
+ public boolean supportsIfExistsBeforeTableName() {
+ return false;
+ }
+
+ public boolean supportsIfExistsAfterTableName() {
+ return false;
+ }
+
/**
- * Which strategy should be used to reference columns in the "result" when
- * performing aggregation (i.e. when processing a group by). This is
- * defined by ANSI SQL as the manner in which columns can be referred to in the
- * group-by, having, and order-by clauses.
- * <p/>
- * The default is to use {@link ResultColumnReferenceStrategy#SOURCE} since that
- * is the most performant.
- *
- * @return The strategy to use.
+ * Does this dialect support column-level check constraints?
*/
- public ResultColumnReferenceStrategy getGroupByColumnReferenceStrategy() {
- return ResultColumnReferenceStrategy.SOURCE;
+ public boolean supportsColumnCheck() {
+ return true;
}
/**
- * Which strategy should be used to reference columns in the "result" when
- * performing aggregated restirctiuon(i.e. when processing a having). This is
- * defined by ANSI SQL as the manner in which columns can be referred to in the
- * group-by, having, and order-by clauses.
- * <p/>
- * The default is to use {@link ResultColumnReferenceStrategy#SOURCE} since that
- * is the most performant.
+ * Does this dialect support table-level check constraints?
+ */
+ public boolean supportsTableCheck() {
+ return true;
+ }
+
+ public boolean supportsCascadeDelete() {
+ return true;
+ }
+
+ public boolean supportsNotNullUnique() {
+ return true;
+ }
+
+ /**
+ * Completely optional cascading drop clause
*
- * @return The strategy to use.
+ * @return String
*/
- public ResultColumnReferenceStrategy getHavingColumnReferenceStrategy() {
- return ResultColumnReferenceStrategy.SOURCE;
+ public String getCascadeConstraintsString() {
+ return "";
}
+
}
Added: trunk/Hibernate3/src/org/hibernate/dialect/function/ConditionalParenthesisFunction.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/dialect/function/ConditionalParenthesisFunction.java 2006-11-08 12:10:08 UTC (rev 10773)
+++ trunk/Hibernate3/src/org/hibernate/dialect/function/ConditionalParenthesisFunction.java 2006-11-08 16:54:55 UTC (rev 10774)
@@ -0,0 +1,45 @@
+//$Id: ConditionalParenthesisFunction.java,v 1.4 2005/04/26 18:08:01 oneovthafew Exp $
+package org.hibernate.dialect.function;
+
+import java.util.List;
+
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.type.Type;
+
+/**
+ * Essentially the same as {@link org.hibernate.dialect.function.StandardSQLFunction},
+ * except that here the parentheses are not included when no arguments are given.
+ *
+ * @author Jonathan Levinson
+ */
+public class ConditionalParenthesisFunction extends StandardSQLFunction {
+
+ public ConditionalParenthesisFunction(String name) {
+ super( name );
+ }
+
+ public ConditionalParenthesisFunction(String name, Type type) {
+ super( name, type );
+ }
+
+ public boolean hasParenthesesIfNoArguments() {
+ return false;
+ }
+
+ public String render(List args, SessionFactoryImplementor factory) {
+ final boolean hasArgs = !args.isEmpty();
+ StringBuffer buf = new StringBuffer();
+ buf.append( getName() );
+ if ( hasArgs ) {
+ buf.append( "(" );
+ for ( int i = 0; i < args.size(); i++ ) {
+ buf.append( args.get( i ) );
+ if ( i < args.size() - 1 ) {
+ buf.append( ", " );
+ }
+ }
+ buf.append( ")" );
+ }
+ return buf.toString();
+ }
+}
Added: trunk/Hibernate3/src/org/hibernate/dialect/function/ConvertFunction.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/dialect/function/ConvertFunction.java 2006-11-08 12:10:08 UTC (rev 10773)
+++ trunk/Hibernate3/src/org/hibernate/dialect/function/ConvertFunction.java 2006-11-08 16:54:55 UTC (rev 10774)
@@ -0,0 +1,45 @@
+//$Id: CastFunction.java 7368 2005-07-04 02:54:27Z oneovthafew $
+package org.hibernate.dialect.function;
+
+import java.util.List;
+
+import org.hibernate.QueryException;
+import org.hibernate.Hibernate;
+import org.hibernate.engine.Mapping;
+import org.hibernate.engine.SessionFactoryImplementor;
+import org.hibernate.type.Type;
+
+/**
+ * A Caché defintion of a convert function.
+ *
+ * @author Jonathan Levinson
+ */
+public class ConvertFunction implements SQLFunction {
+
+ public Type getReturnType(Type columnType, Mapping mapping) throws QueryException {
+ return Hibernate.STRING;
+ }
+
+ public boolean hasArguments() {
+ return true;
+ }
+
+ public boolean hasParenthesesIfNoArguments() {
+ return true;
+ }
+
+ public String render(List args, SessionFactoryImplementor factory) throws QueryException {
+ if ( args.size() != 2 && args.size() != 3 ) {
+ throw new QueryException( "convert() requires two or three arguments" );
+ }
+ String type = ( String ) args.get( 1 );
+
+ if ( args.size() == 2 ) {
+ return "{fn convert(" + args.get( 0 ) + " , " + type + ")}";
+ }
+ else {
+ return "convert(" + args.get( 0 ) + " , " + type + "," + args.get( 2 ) + ")";
+ }
+ }
+
+}
Modified: trunk/Hibernate3/src/org/hibernate/dialect/function/SQLFunction.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/dialect/function/SQLFunction.java 2006-11-08 12:10:08 UTC (rev 10773)
+++ trunk/Hibernate3/src/org/hibernate/dialect/function/SQLFunction.java 2006-11-08 16:54:55 UTC (rev 10774)
@@ -20,20 +20,39 @@
*/
public interface SQLFunction {
/**
- * The function return type
+ * The return type of the function. May be either a concrete type which
+ * is preset, or variable depending upon the type of the first function
+ * argument.
+ *
* @param columnType the type of the first argument
+ * @param mapping The mapping source.
+ * @return The type to be expected as a return.
+ * @throws org.hibernate.QueryException Indicates an issue resolving the return type.
*/
public Type getReturnType(Type columnType, Mapping mapping) throws QueryException;
+
/**
* Does this function have any arguments?
+ *
+ * @return True if the function expects to have parameters; false otherwise.
*/
public boolean hasArguments();
+
/**
* If there are no arguments, are parens required?
+ *
+ * @return True if a no-arg call of this function requires parentheses.
*/
public boolean hasParenthesesIfNoArguments();
+
/**
- * Render the function call as SQL
+ * Render the function call as SQL fragment.
+ *
+ * @param args The function arguments
+ * @param factory The SessionFactory
+ * @return The rendered function call
+ * @throws org.hibernate.QueryException Indicates a problem rendering the
+ * function call.
*/
public String render(List args, SessionFactoryImplementor factory) throws QueryException;
}
Added: trunk/Hibernate3/src/org/hibernate/dialect/function/StandardJDBCEscapeFunction.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/dialect/function/StandardJDBCEscapeFunction.java 2006-11-08 12:10:08 UTC (rev 10773)
+++ trunk/Hibernate3/src/org/hibernate/dialect/function/StandardJDBCEscapeFunction.java 2006-11-08 16:54:55 UTC (rev 10774)
@@ -0,0 +1,31 @@
+package org.hibernate.dialect.function;
+
+import java.util.List;
+
+import org.hibernate.type.Type;
+import org.hibernate.engine.SessionFactoryImplementor;
+
+/**
+ * Analogous to {@link org.hibernate.dialect.function.StandardSQLFunction}
+ * except that standard JDBC escape sequences (i.e. {fn blah}) are used when
+ * rendering the SQL.
+ *
+ * @author Steve Ebersole
+ */
+public class StandardJDBCEscapeFunction extends StandardSQLFunction {
+ public StandardJDBCEscapeFunction(String name) {
+ super( name );
+ }
+
+ public StandardJDBCEscapeFunction(String name, Type typeValue) {
+ super( name, typeValue );
+ }
+
+ public String render(List args, SessionFactoryImplementor factory) {
+ return "{fn " + super.render( args, factory ) + "}";
+ }
+
+ public String toString() {
+ return "{fn " + getName() + "...}";
+ }
+}
Modified: trunk/Hibernate3/src/org/hibernate/dialect/function/StandardSQLFunction.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/dialect/function/StandardSQLFunction.java 2006-11-08 12:10:08 UTC (rev 10773)
+++ trunk/Hibernate3/src/org/hibernate/dialect/function/StandardSQLFunction.java 2006-11-08 16:54:55 UTC (rev 10774)
@@ -16,41 +16,91 @@
* @author David Channon
*/
public class StandardSQLFunction implements SQLFunction {
- private Type returnType = null;
- private String name;
-
+ private final String name;
+ private final Type type;
+
+ /**
+ * Construct a standard SQL function definition with a variable return type;
+ * the actual return type will depend on the types to which the function
+ * is applied.
+ * <p/>
+ * Using this form, the return type is considered non-static and assumed
+ * to be the type of the first argument.
+ *
+ * @param name The name of the function.
+ */
public StandardSQLFunction(String name) {
- this.name = name;
+ this( name, null );
}
-
- public StandardSQLFunction(String name, Type typeValue) {
- returnType = typeValue;
+
+ /**
+ * Construct a standard SQL function definition with a static return type.
+ *
+ * @param name The name of the function.
+ * @param type The static return type.
+ */
+ public StandardSQLFunction(String name, Type type) {
this.name = name;
+ this.type = type;
}
-
+
+ /**
+ * Function name accessor
+ *
+ * @return The function name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Function static return type accessor.
+ *
+ * @return The static function return type; or null if return type is
+ * not static.
+ */
+ public Type getType() {
+ return type;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
public Type getReturnType(Type columnType, Mapping mapping) {
- return returnType == null ? columnType : returnType;
+ // return the concrete type, or the underlying type if a concrete type
+ // was not specified
+ return type == null ? columnType : type;
}
-
+
+ /**
+ * {@inheritDoc}
+ */
public boolean hasArguments() {
return true;
}
-
+
+ /**
+ * {@inheritDoc}
+ */
public boolean hasParenthesesIfNoArguments() {
return true;
}
-
+
+ /**
+ * {@inheritDoc}
+ */
public String render(List args, SessionFactoryImplementor factory) {
StringBuffer buf = new StringBuffer();
- buf.append(name)
- .append('(');
- for ( int i=0; i<args.size(); i++ ) {
- buf.append( args.get(i) );
- if ( i<args.size()-1 ) buf.append(", ");
+ buf.append( name ).append( '(' );
+ for ( int i = 0; i < args.size(); i++ ) {
+ buf.append( args.get( i ) );
+ if ( i < args.size() - 1 ) {
+ buf.append( ", " );
+ }
}
- return buf.append(')').toString();
+ return buf.append( ')' ).toString();
}
-
+
public String toString() {
return name;
}
Added: trunk/Hibernate3/src/org/hibernate/exception/CacheSQLStateConverter.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/exception/CacheSQLStateConverter.java 2006-11-08 12:10:08 UTC (rev 10773)
+++ trunk/Hibernate3/src/org/hibernate/exception/CacheSQLStateConverter.java 2006-11-08 16:54:55 UTC (rev 10774)
@@ -0,0 +1,93 @@
+// $Id: $
+package org.hibernate.exception;
+
+import org.hibernate.JDBCException;
+
+import java.sql.SQLException;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A SQLExceptionConverter implementation specific to Caché SQL,
+ * accounting for its custom integrity constraint violation error codes.
+ *
+ * @author Jonathan Levinson
+ */
+public class CacheSQLStateConverter implements SQLExceptionConverter {
+
+ private ViolatedConstraintNameExtracter extracter;
+
+ private static final Set SQL_GRAMMAR_CATEGORIES = new HashSet();
+ private static final Set DATA_CATEGORIES = new HashSet();
+ private static final Set INTEGRITY_VIOLATION_CATEGORIES = new HashSet();
+ private static final Set CONNECTION_CATEGORIES = new HashSet();
+
+ static {
+ SQL_GRAMMAR_CATEGORIES.add( "07" );
+ SQL_GRAMMAR_CATEGORIES.add( "37" );
+ SQL_GRAMMAR_CATEGORIES.add( "42" );
+ SQL_GRAMMAR_CATEGORIES.add( "65" );
+ SQL_GRAMMAR_CATEGORIES.add( "S0" );
+ SQL_GRAMMAR_CATEGORIES.add( "20" );
+
+ DATA_CATEGORIES.add( "22" );
+ DATA_CATEGORIES.add( "21" );
+ DATA_CATEGORIES.add( "02" );
+
+ INTEGRITY_VIOLATION_CATEGORIES.add( new Integer( 119 ) );
+ INTEGRITY_VIOLATION_CATEGORIES.add( new Integer( 120 ) );
+ INTEGRITY_VIOLATION_CATEGORIES.add( new Integer( 121 ) );
+ INTEGRITY_VIOLATION_CATEGORIES.add( new Integer( 122 ) );
+ INTEGRITY_VIOLATION_CATEGORIES.add( new Integer( 123 ) );
+ INTEGRITY_VIOLATION_CATEGORIES.add( new Integer( 124 ) );
+ INTEGRITY_VIOLATION_CATEGORIES.add( new Integer( 125 ) );
+ INTEGRITY_VIOLATION_CATEGORIES.add( new Integer( 127 ) );
+
+ CONNECTION_CATEGORIES.add( "08" );
+ }
+
+ public CacheSQLStateConverter(ViolatedConstraintNameExtracter extracter) {
+ this.extracter = extracter;
+ }
+
+ /**
+ * Convert the given SQLException into Hibernate's JDBCException hierarchy.
+ *
+ * @param sqlException The SQLException to be converted.
+ * @param message An optional error message.
+ * @param sql Optionally, the sql being performed when the exception occurred.
+ * @return The resulting JDBCException.
+ */
+ public JDBCException convert(SQLException sqlException, String message, String sql) {
+ String sqlStateClassCode = JDBCExceptionHelper.extractSqlStateClassCode( sqlException );
+ Integer errorCode = new Integer( JDBCExceptionHelper.extractErrorCode( sqlException ) );
+ if ( sqlStateClassCode != null ) {
+ if ( SQL_GRAMMAR_CATEGORIES.contains( sqlStateClassCode ) ) {
+ return new SQLGrammarException( message, sqlException, sql );
+ }
+ else if ( INTEGRITY_VIOLATION_CATEGORIES.contains( errorCode ) ) {
+ String constraintName = extracter.extractConstraintName( sqlException );
+ return new ConstraintViolationException( message, sqlException, sql, constraintName );
+ }
+ else if ( CONNECTION_CATEGORIES.contains( sqlStateClassCode ) ) {
+ return new JDBCConnectionException( message, sqlException, sql );
+ }
+ else if ( DATA_CATEGORIES.contains( sqlStateClassCode ) ) {
+ return new DataException( message, sqlException, sql );
+ }
+ }
+ return handledNonSpecificException( sqlException, message, sql );
+ }
+
+ /**
+ * Handle an exception not converted to a specific type based on the SQLState.
+ *
+ * @param sqlException The exception to be handled.
+ * @param message An optional message
+ * @param sql Optionally, the sql being performed when the exception occurred.
+ * @return The converted exception; should <b>never</b> be null.
+ */
+ protected JDBCException handledNonSpecificException(SQLException sqlException, String message, String sql) {
+ return new GenericJDBCException( message, sqlException, sql );
+ }
+}
Added: trunk/Hibernate3/src/org/hibernate/sql/CacheJoinFragment.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/sql/CacheJoinFragment.java 2006-11-08 12:10:08 UTC (rev 10773)
+++ trunk/Hibernate3/src/org/hibernate/sql/CacheJoinFragment.java 2006-11-08 16:54:55 UTC (rev 10774)
@@ -0,0 +1,22 @@
+//$Id: $
+package org.hibernate.sql;
+
+import org.hibernate.AssertionFailure;
+
+/**
+ * A Caché dialect join. Differs from ANSI only in that full outer join
+ * is not supported.
+ *
+ * @author Jeff Miller
+ * @author Jonathan Levinson
+ */
+public class CacheJoinFragment extends ANSIJoinFragment {
+
+ public void addJoin(String tableName, String alias, String[] fkColumns, String[] pkColumns, int joinType, String on) {
+ if ( joinType == FULL_JOIN ) {
+ throw new AssertionFailure( "Cache does not support full outer joins" );
+ }
+ super.addJoin( tableName, alias, fkColumns, pkColumns, joinType, on );
+ }
+
+}
Modified: trunk/Hibernate3/test/org/hibernate/test/TestCase.java
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/TestCase.java 2006-11-08 12:10:08 UTC (rev 10773)
+++ trunk/Hibernate3/test/org/hibernate/test/TestCase.java 2006-11-08 16:54:55 UTC (rev 10774)
@@ -29,6 +29,7 @@
import org.hibernate.dialect.SybaseDialect;
import org.hibernate.dialect.TimesTenDialect;
import org.hibernate.dialect.DerbyDialect;
+import org.hibernate.dialect.Cache71Dialect;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.PersistentClass;
@@ -382,11 +383,14 @@
/**
* Intended to indicate that this test class as a whole is intended for
- * a dialect or series of dialects. Skips here (appliesTo = false), therefore
+ * a dialect or series of dialects. Skips here (appliesTo = false) therefore
* simply indicate that the given tests target a particular feature of the
- * current database...
+ * checked database and none of the tests on this class should be run for the
+ * checked dialect.
*
- * @param dialect
+ * @param dialect The dialect to be checked.
+ * @return True if all the tests on this class apply to the given dialect (and
+ * therefore should be run); false otherwise.
*/
public boolean appliesTo(Dialect dialect) {
return true;
@@ -486,7 +490,8 @@
SQLServerDialect.class,
SybaseDialect.class,
PostgreSQLDialect.class,
- TimesTenDialect.class
+ TimesTenDialect.class,
+ Cache71Dialect.class
}
);
@@ -504,7 +509,7 @@
protected boolean dialectIsCaseSensitive(String testDescription) {
// MySQL and SQLServer is case insensitive on strings (at least in default installation)
boolean canDoIt = dialectIsNot(
- new Class[] { MySQLDialect.class, SQLServerDialect.class }
+ new Class[] { MySQLDialect.class, SQLServerDialect.class, Cache71Dialect.class }
);
if ( !canDoIt ) {
@@ -514,12 +519,12 @@
}
protected boolean supportsRowValueConstructorSyntaxInInList() {
- boolean supported = ! (
- getDialect() instanceof HSQLDialect ||
+ boolean supported = ! (getDialect() instanceof HSQLDialect ||
getDialect() instanceof PostgreSQLDialect ||
getDialect() instanceof MySQLDialect ||
getDialect() instanceof DB2Dialect ||
- getDialect() instanceof SybaseDialect
+ getDialect() instanceof SybaseDialect ||
+ getDialect() instanceof Cache71Dialect
);
if ( !supported ) {
@@ -529,7 +534,7 @@
}
protected boolean supportsResultSetPositionQueryMethodsOnForwardOnlyCursor() {
- if ( getDialect() instanceof SQLServerDialect ) {
+ if ( getDialect() instanceof SQLServerDialect || getDialect() instanceof Cache71Dialect ) {
reportSkip( "Driver does not support 'position query' methods on forward-only cursors", "query support" );
return false;
}
Modified: trunk/Hibernate3/test/org/hibernate/test/hql/FooBarCopy.hbm.xml
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/hql/FooBarCopy.hbm.xml 2006-11-08 12:10:08 UTC (rev 10773)
+++ trunk/Hibernate3/test/org/hibernate/test/hql/FooBarCopy.hbm.xml 2006-11-08 16:54:55 UTC (rev 10774)
@@ -1,5 +1,5 @@
<?xml version="1.0"?>
-<!DOCTYPE hibernate-mapping PUBLIC
+<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping default-lazy="false" package="org.hibernate.test.legacy">
@@ -23,7 +23,7 @@
</generator>
</id>
- <discriminator column="`^foo_subclass$1234`" type="character" force="true"/>
+ <discriminator column="`foo_subclass_1234`" type="character" force="true"/>
<version name="version"/>
<many-to-one name="foo" class="Foo">
@@ -34,7 +34,7 @@
<column name="long_" index="fbmtoidx" unique-key="abc" not-null="true"/>
</property>
<property name="integer">
- <column name="`@@##integer_*`" unique-key="abc" not-null="true"/>
+ <column name="`integer__`" unique-key="abc" not-null="true"/>
</property>
<property name="float">
<column name="float_" unique-key="abc" not-null="true" check="float_ > 0.0"/>
@@ -47,7 +47,7 @@
<index column="i"/>
<element column="byte_" type="byte"/>
</primitive-array>
-
+
<property name="date" type="date" column="date_"/>
<property name="timestamp" type="timestamp" column="timestamp_"/>
<property name="boolean" column="boolean_"/>
@@ -65,10 +65,10 @@
<property name="blob" type="org.hibernate.test.legacy.Foo$Struct" column="blobb_"/>
<property name="nullBlob" type="serializable"/>
<property name="binary" column="bin_"/>
- <property name="theLocale" access="field" column="`localeayzabc123!@#$`"/>
-
+ <property name="theLocale" access="field" column="`localeayzabc123`"/>
+
<property name="formula" formula="int_/2"/>
-
+
<property name="custom" type="org.hibernate.test.legacy.DoubleStringType" access="field">
<column name="first_name" length="66"/>
<column name="surname" length="66"/>
@@ -76,29 +76,29 @@
<component name="nullComponent">
<property name="name" column="null_cmpnt_"/>
</component>
-
+
<join table="jointable">
<key column="fooid" on-delete="cascade"/>
<property name="joinedProp"/>
</join>
-
+
<subclass
name="Trivial"
- proxy="FooProxy"
+ proxy="FooProxy"
discriminator-value="T"/>
-
- <subclass
- name="Abstract"
- proxy="AbstractProxy"
+
+ <subclass
+ name="Abstract"
+ proxy="AbstractProxy"
discriminator-value="null">
<set name="abstracts" batch-size="2">
<key column="abstract_id"/>
<one-to-many class="Abstract"/>
</set>
<property name="time" column="the_time"/>
-
- <subclass
- name="Bar"
+
+ <subclass
+ name="Bar"
proxy="BarProxy"
discriminator-value="B">
<property name="barString">
Modified: trunk/Hibernate3/test/org/hibernate/test/legacy/Baz.hbm.xml
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/legacy/Baz.hbm.xml 2006-11-08 12:10:08 UTC (rev 10773)
+++ trunk/Hibernate3/test/org/hibernate/test/legacy/Baz.hbm.xml 2006-11-08 16:54:55 UTC (rev 10774)
@@ -60,11 +60,11 @@
<index column="j"/>
<element column="the_time" type="time"/>
</array>
- <bag name="bag" order-by="`name!`" table="`^%$^bxaxg`">
+ <bag name="bag" order-by="`name_`" table="`bxaxg`">
<key>
- <column name="`baz_id$`" length="16"/>
+ <column name="`baz_id_`" length="16"/>
</key>
- <element column="`name!`" type="string"/>
+ <element column="`name_`" type="string"/>
</bag>
<map name="fooToGlarch" lazy="false">
<key>
Modified: trunk/Hibernate3/test/org/hibernate/test/legacy/FooBar.hbm.xml
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/legacy/FooBar.hbm.xml 2006-11-08 12:10:08 UTC (rev 10773)
+++ trunk/Hibernate3/test/org/hibernate/test/legacy/FooBar.hbm.xml 2006-11-08 16:54:55 UTC (rev 10774)
@@ -1,13 +1,13 @@
<?xml version="1.0"?>
-<!DOCTYPE hibernate-mapping PUBLIC
+<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping default-lazy="false" package="org.hibernate.test.legacy">
<import class="Result"/>
<import class="Named"/>
-
- <class
+
+ <class
name="Foo"
table="`foos`"
proxy="FooProxy"
@@ -16,16 +16,16 @@
dynamic-insert="true"
dynamic-update="true"
select-before-update="true">
-
+
<!--cache-->
-
+
<id name="key" type="string">
<column name="`foo_idcolumnname123`" length="36"/>
<generator class="uuid.hex">
<param name="seperator">:</param>
</generator>
</id>
- <discriminator column="`^foo_subclass$1234`" type="character" force="true"/>
+ <discriminator column="`foo_subclass_1234`" type="character" force="true"/>
<version name="version"/>
<!--<version name="versionCalendar" type="calendar"/>-->
<!--<timestamp name="versionTimestamp"/>-->
@@ -36,7 +36,7 @@
<column name="long_" index="fbmtoidx" unique-key="abc" not-null="true"/>
</property>
<property name="integer">
- <column name="`@@##integer_*`" unique-key="abc" not-null="true"/>
+ <column name="`integer__`" unique-key="abc" not-null="true"/>
</property>
<property name="float">
<column name="float_" unique-key="abc" not-null="true" check="float_ > 0.0"/>
@@ -49,7 +49,7 @@
<index column="i"/>
<element column="byte_" type="byte"/>
</primitive-array>
-
+
<property name="date" type="date" column="date_"/>
<property name="timestamp" type="timestamp" column="timestamp_"/>
<property name="boolean" column="boolean_"/>
@@ -67,24 +67,24 @@
<property name="blob" type="org.hibernate.test.legacy.Foo$Struct" column="blobb_"/>
<property name="nullBlob" type="serializable"/>
<property name="binary" column="bin_"/>
- <property name="theLocale" access="field" column="`localeayzabc123!@#$`"/>
-
+ <property name="theLocale" access="field" column="`localeayzabc123`"/>
+
<property name="formula" formula="int_/2"/>
-
+
<property name="custom" type="org.hibernate.test.legacy.DoubleStringType" access="field">
<column name="first_name" length="66"/>
<column name="surname" length="66"/>
</property>
-
+
<component name="component">
- <property name="count" column="count_" type="int" not-null="true"/>
+ <property name="count" column="count_" type="int" not-null="true"/>
<property name="name">
- <column name="name_" length="32" not-null="true"/>
+ <column name="name_" length="32" not-null="true"/>
</property>
- <many-to-one name="glarch"
- column="g__"
- cascade="all"
- class="org.hibernate.test.legacy.Glarch"
+ <many-to-one name="glarch"
+ column="g__"
+ cascade="all"
+ class="org.hibernate.test.legacy.Glarch"
lazy="proxy"
outer-join="true"/>
<property name="null" column="cmpnt_null_"/>
@@ -96,11 +96,11 @@
<index column="i"/>
<element column="date_" type="time"/>
</array>
- <many-to-one name="fee"
- column="fee_sub"
- cascade="all"
- class="Fee"
- outer-join="false"
+ <many-to-one name="fee"
+ column="fee_sub"
+ cascade="all"
+ class="Fee"
+ outer-join="false"
access="field"/>
</component>
<array name="importantDates" table="foo_dates">
@@ -112,37 +112,37 @@
<component name="nullComponent">
<property name="name" column="null_cmpnt_"/>
</component>
-
+
<join table="jointable">
<key column="fooid" on-delete="cascade"/>
<property name="joinedProp"/>
</join>
-
+
<join table="foo_dep_table">
<key column="fooid"/>
- <many-to-one name="dependent"
- class="org.hibernate.test.legacy.Fee"
- cascade="all"
+ <many-to-one name="dependent"
+ class="org.hibernate.test.legacy.Fee"
+ cascade="all"
not-null="true"/>
</join>
-
+
<subclass
name="Trivial"
- proxy="FooProxy"
+ proxy="FooProxy"
discriminator-value="T"/>
-
- <subclass
- name="Abstract"
- proxy="AbstractProxy"
+
+ <subclass
+ name="Abstract"
+ proxy="AbstractProxy"
discriminator-value="null">
<set name="abstracts" batch-size="2">
<key column="abstract_id"/>
<one-to-many class="Abstract"/>
</set>
<property name="time" column="the_time"/>
-
- <subclass
- name="Bar"
+
+ <subclass
+ name="Bar"
proxy="BarProxy"
discriminator-value="B">
<many-to-one name="baz"/>
Modified: trunk/Hibernate3/test/org/hibernate/test/legacy/Glarch.hbm.xml
===================================================================
--- trunk/Hibernate3/test/org/hibernate/test/legacy/Glarch.hbm.xml 2006-11-08 12:10:08 UTC (rev 10773)
+++ trunk/Hibernate3/test/org/hibernate/test/legacy/Glarch.hbm.xml 2006-11-08 16:54:55 UTC (rev 10774)
@@ -1,14 +1,14 @@
<?xml version="1.0"?>
-<!DOCTYPE hibernate-mapping PUBLIC
+<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping default-lazy="false" package="org.hibernate.test.legacy">
- <class name="Glarch"
- table="`glarchez`"
+ <class name="Glarch"
+ table="`glarchez`"
proxy="GlarchProxy"
dynamic-update="true">
-
+
<!--cache-->
<id type="string" column="tha_key" length="32">
<generator class="uuid"/>
@@ -25,7 +25,7 @@
<property name="x"/>
<list name="strings">
<key column="glarch_key"/>
- <index column="`!@#i`"/>
+ <index column="`indx_`"/>
<element type="string" column="`tha_stryng`"/>
</list>
<list name="fooComponents" lazy="true" cascade="all">
18 years, 4 months
Hibernate SVN: r10773 - in branches/Branch_3_2/HibernateExt/tools/src: java/org/hibernate/tool/ant testsupport
by hibernate-commits@lists.jboss.org
Author: max.andersen(a)jboss.com
Date: 2006-11-08 07:10:08 -0500 (Wed, 08 Nov 2006)
New Revision: 10773
Modified:
branches/Branch_3_2/HibernateExt/tools/src/java/org/hibernate/tool/ant/JavaFormatterTask.java
branches/Branch_3_2/HibernateExt/tools/src/testsupport/javaformattest-build.xml
Log:
minor
Modified: branches/Branch_3_2/HibernateExt/tools/src/java/org/hibernate/tool/ant/JavaFormatterTask.java
===================================================================
--- branches/Branch_3_2/HibernateExt/tools/src/java/org/hibernate/tool/ant/JavaFormatterTask.java 2006-11-08 12:03:54 UTC (rev 10772)
+++ branches/Branch_3_2/HibernateExt/tools/src/java/org/hibernate/tool/ant/JavaFormatterTask.java 2006-11-08 12:10:08 UTC (rev 10773)
@@ -70,29 +70,36 @@
File[] files = getFiles();
+ int success = 0;
+ int failed = 0;
+
if(files.length>0) {
JavaFormatter formatter = new JavaFormatter(settings);
-
for (int i = 0; i < files.length; i++) {
File file = files[i];
try {
boolean ok = formatter.formatFile( file );
if(!ok) {
+ failed++;
getProject().log(this, "Formatting failed - skipping " + file, Project.MSG_WARN);
} else {
+ success++;
getProject().log(this, "Formatted " + file, Project.MSG_VERBOSE);
}
} catch(ExporterException ee) {
+ failed++;
if(failOnError) {
- throw new BuildException("Formatting failed on " + file, ee);
+ throw new BuildException("Java formatting failed on " + file, ee);
} else {
- getProject().log(this, "Formatting failed on " + file + ", " + ee.getLocalizedMessage(), Project.MSG_ERR);
+ getProject().log(this, "Java formatting failed on " + file + ", " + ee.getLocalizedMessage(), Project.MSG_ERR);
}
}
- }
+ }
}
+ getProject().log( this, "Java formatting of " + files.length + " files completed. Skipped " + failed + " file(s).", Project.MSG_INFO );
+
}
private File[] getFiles() {
Modified: branches/Branch_3_2/HibernateExt/tools/src/testsupport/javaformattest-build.xml
===================================================================
--- branches/Branch_3_2/HibernateExt/tools/src/testsupport/javaformattest-build.xml 2006-11-08 12:03:54 UTC (rev 10772)
+++ branches/Branch_3_2/HibernateExt/tools/src/testsupport/javaformattest-build.xml 2006-11-08 12:10:08 UTC (rev 10773)
@@ -37,7 +37,7 @@
<target name="fileset">
<taskdef name="javaformatter" classname="org.hibernate.tool.ant.JavaFormatterTask" classpathref="tasks.classpath"/>
- <javaformatter> <!-- config, failonerror, destdir -->
+ <javaformatter>
<fileset dir="${build.dir}">
<include name="formatting/**/*.java"/>
</fileset>
@@ -47,7 +47,7 @@
<target name="configtest">
<taskdef name="javaformatter" classname="org.hibernate.tool.ant.JavaFormatterTask" classpathref="tasks.classpath"/>
- <javaformatter configurationfile="emptyconfig.properties"> <!-- config, failonerror, destdir -->
+ <javaformatter configurationfile="emptyconfig.properties">
<fileset dir="${build.dir}">
<include name="formatting/**/*.java"/>
</fileset>
@@ -57,7 +57,7 @@
<target name="noconfigtest">
<taskdef name="javaformatter" classname="org.hibernate.tool.ant.JavaFormatterTask" classpathref="tasks.classpath"/>
- <javaformatter> <!-- config, failonerror, destdir -->
+ <javaformatter>
<fileset dir="${build.dir}">
<include name="formatting/**/*.java"/>
</fileset>
18 years, 4 months
Hibernate SVN: r10772 - branches/Branch_3_2/HibernateExt/tools/src/java/org/hibernate/tool/ant
by hibernate-commits@lists.jboss.org
Author: max.andersen(a)jboss.com
Date: 2006-11-08 07:03:54 -0500 (Wed, 08 Nov 2006)
New Revision: 10772
Modified:
branches/Branch_3_2/HibernateExt/tools/src/java/org/hibernate/tool/ant/JavaFormatterTask.java
Log:
only log if something goes wrong
Modified: branches/Branch_3_2/HibernateExt/tools/src/java/org/hibernate/tool/ant/JavaFormatterTask.java
===================================================================
--- branches/Branch_3_2/HibernateExt/tools/src/java/org/hibernate/tool/ant/JavaFormatterTask.java 2006-11-08 12:00:28 UTC (rev 10771)
+++ branches/Branch_3_2/HibernateExt/tools/src/java/org/hibernate/tool/ant/JavaFormatterTask.java 2006-11-08 12:03:54 UTC (rev 10772)
@@ -75,12 +75,13 @@
JavaFormatter formatter = new JavaFormatter(settings);
for (int i = 0; i < files.length; i++) {
- File file = files[i];
- getProject().log(this, "Formatting " + file, Project.MSG_INFO);
+ File file = files[i];
try {
boolean ok = formatter.formatFile( file );
if(!ok) {
getProject().log(this, "Formatting failed - skipping " + file, Project.MSG_WARN);
+ } else {
+ getProject().log(this, "Formatted " + file, Project.MSG_VERBOSE);
}
} catch(ExporterException ee) {
if(failOnError) {
18 years, 4 months