diff --git a/querydsl-jpa/src/test/java/com/mysema/query/HibernateBase.java b/querydsl-jpa/src/test/java/com/mysema/query/HibernateBase.java index 4257bd720..c328f417a 100644 --- a/querydsl-jpa/src/test/java/com/mysema/query/HibernateBase.java +++ b/querydsl-jpa/src/test/java/com/mysema/query/HibernateBase.java @@ -1,6 +1,6 @@ /* * Copyright 2011, Mysema Ltd - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -23,6 +23,7 @@ import java.util.List; import org.hibernate.LockMode; import org.hibernate.Session; +import org.junit.After; import org.junit.Rule; import org.junit.Test; import org.junit.rules.MethodRule; @@ -44,23 +45,31 @@ import com.mysema.testutil.HibernateTestRunner; */ @RunWith(HibernateTestRunner.class) public class HibernateBase extends AbstractJPATest { - + private static final QCat cat = QCat.cat; - + @Rule public static MethodRule jpaProviderRule = new JPAProviderRule(); - + @Rule public static MethodRule targetRule = new TargetRule(); - + private Session session; + @After + public void tearDown() { + session.flush(); + session.clear(); + } + + @Override protected HibernateQuery query() { return new HibernateQuery(session, getTemplates()); } - + + @Override protected HibernateQuery testQuery() { - return new HibernateQuery(new DefaultSessionHolder(session), + return new HibernateQuery(new DefaultSessionHolder(session), getTemplates(), new DefaultQueryMetadata().noValidate()); } @@ -84,7 +93,7 @@ public class HibernateBase extends AbstractJPATest { assertNotNull(results); assertFalse(results.isEmpty()); } - + @Test public void WithComment() { query().from(cat).setComment("my comment").list(cat); @@ -94,7 +103,7 @@ public class HibernateBase extends AbstractJPATest { public void LockMode() { query().from(cat).setLockMode(cat, LockMode.PESSIMISTIC_WRITE).list(cat); } - + @Test public void FlushMode() { query().from(cat).setFlushMode(org.hibernate.FlushMode.AUTO).list(cat); @@ -125,6 +134,6 @@ public class HibernateBase extends AbstractJPATest { } rows.close(); } - + } diff --git a/querydsl-jpa/src/test/java/com/mysema/query/JPABase.java b/querydsl-jpa/src/test/java/com/mysema/query/JPABase.java index 933fb1a30..354fe1eb8 100644 --- a/querydsl-jpa/src/test/java/com/mysema/query/JPABase.java +++ b/querydsl-jpa/src/test/java/com/mysema/query/JPABase.java @@ -27,6 +27,7 @@ import javax.persistence.EntityManager; import javax.persistence.FlushModeType; import javax.persistence.LockModeType; +import org.junit.After; import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; @@ -64,6 +65,12 @@ public class JPABase extends AbstractJPATest { private EntityManager entityManager; + @After + public void tearDown() { + entityManager.flush(); + entityManager.clear(); + } + @Override protected JPAQuery query() { return new JPAQuery(entityManager); diff --git a/querydsl-jpa/src/test/java/com/mysema/query/jpa/domain/Show.java b/querydsl-jpa/src/test/java/com/mysema/query/jpa/domain/Show.java index 717905d0f..35368de2d 100644 --- a/querydsl-jpa/src/test/java/com/mysema/query/jpa/domain/Show.java +++ b/querydsl-jpa/src/test/java/com/mysema/query/jpa/domain/Show.java @@ -18,6 +18,7 @@ import java.util.Map; import javax.persistence.ElementCollection; import javax.persistence.Entity; import javax.persistence.Id; +import javax.persistence.MapKeyColumn; import javax.persistence.Table; /** @@ -31,6 +32,7 @@ public class Show { long id; @ElementCollection + @MapKeyColumn(name="acts_key") public Map acts; public Show() {} diff --git a/querydsl-jpa/src/test/java/com/mysema/query/jpa/support/TeradataDialect.java b/querydsl-jpa/src/test/java/com/mysema/query/jpa/support/TeradataDialect.java new file mode 100644 index 000000000..75cac0400 --- /dev/null +++ b/querydsl-jpa/src/test/java/com/mysema/query/jpa/support/TeradataDialect.java @@ -0,0 +1,424 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as + * indicated by the @author tags or express copyright attribution + * statements applied by the authors. All third-party contributions are + * distributed under license by Red Hat Middleware LLC. + * + * This copyrighted material is made available to anyone wishing to use, modify, + * copy, or redistribute it subject to the terms and conditions of the GNU + * Lesser General Public License, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License + * for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this distribution; if not, write to: + * Free Software Foundation, Inc. + * 51 Franklin Street, Fifth Floor + * Boston, MA 02110-1301 USA + * + */ +package com.mysema.query.jpa.support; + +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Types; + +import org.hibernate.HibernateException; +import org.hibernate.cfg.Environment; +import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.function.SQLFunctionTemplate; +import org.hibernate.dialect.function.VarArgsSQLFunction; +import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtracter; +import org.hibernate.exception.spi.ViolatedConstraintNameExtracter; +import org.hibernate.type.StandardBasicTypes; + +/** + * A dialect for the Teradata database created by MCR as part of the dialect + * certification process. + * + * @author Jay Nance + */ +public class TeradataDialect extends Dialect { + + /** + * Constructor + */ + public TeradataDialect() { + super(); + // registerColumnType data types + registerColumnType(Types.NUMERIC, "NUMERIC($p,$s)"); + registerColumnType(Types.DOUBLE, "DOUBLE PRECISION"); + registerColumnType(Types.BIGINT, "NUMERIC(18,0)"); + registerColumnType(Types.BIT, "BYTEINT"); + registerColumnType(Types.TINYINT, "BYTEINT"); + registerColumnType(Types.VARBINARY, "VARBYTE($l)"); + registerColumnType(Types.BINARY, "BYTEINT"); + registerColumnType(Types.LONGVARCHAR, "LONG VARCHAR"); + registerColumnType(Types.CHAR, "CHAR(1)"); + registerColumnType(Types.DECIMAL, "DECIMAL"); + registerColumnType(Types.INTEGER, "INTEGER"); + registerColumnType(Types.SMALLINT, "SMALLINT"); + registerColumnType(Types.FLOAT, "FLOAT"); + registerColumnType(Types.VARCHAR, "VARCHAR($l)"); + registerColumnType(Types.DATE, "DATE"); + registerColumnType(Types.TIME, "TIME"); + registerColumnType(Types.TIMESTAMP, "TIMESTAMP"); + registerColumnType(Types.BOOLEAN, "BYTEINT"); // hibernate seems to + // ignore this type... + registerColumnType(Types.BLOB, "BLOB"); + registerColumnType(Types.CLOB, "CLOB"); + + registerFunction("year", new SQLFunctionTemplate(StandardBasicTypes.INTEGER, + "extract(year from ?1)")); + registerFunction("length", new SQLFunctionTemplate(StandardBasicTypes.INTEGER, + "character_length(?1)")); + registerFunction("concat", + new VarArgsSQLFunction(StandardBasicTypes.STRING, "(", "||", ")")); + registerFunction("substring", new SQLFunctionTemplate(StandardBasicTypes.STRING, + "substring(?1 from ?2 for ?3)")); + registerFunction("locate", new SQLFunctionTemplate(StandardBasicTypes.STRING, + "position(?1 in ?2)")); + registerFunction("mod", new SQLFunctionTemplate(StandardBasicTypes.STRING, "?1 mod ?2")); + registerFunction("str", new SQLFunctionTemplate(StandardBasicTypes.STRING, + "cast(?1 as varchar(255))")); + + // bit_length feels a bit broken to me. We have to cast to char in order + // to + // pass when a numeric value is supplied. But of course the answers + // given will + // be wildly different for these two datatypes. 1234.5678 will be 9 + // bytes as + // a char string but will be 8 or 16 bytes as a true numeric. + // Jay Nance 2006-09-22 + registerFunction("bit_length", new SQLFunctionTemplate(StandardBasicTypes.INTEGER, + "octet_length(cast(?1 as char))*4")); + + // The preference here would be + // SQLFunctionTemplate( Hibernate.TIMESTAMP, "current_timestamp(?1)", + // false) + // but this appears not to work. + // Jay Nance 2006-09-22 + registerFunction("current_timestamp", new SQLFunctionTemplate(StandardBasicTypes.TIMESTAMP, + "current_timestamp")); + registerFunction("current_time", new SQLFunctionTemplate(StandardBasicTypes.TIME, + "current_time")); + registerFunction("current_date", new SQLFunctionTemplate(StandardBasicTypes.DATE, + "current_date")); + // IBID for current_time and current_date + + registerKeyword("account"); + registerKeyword("alias"); + registerKeyword("class"); + registerKeyword("column"); + registerKeyword("first"); + registerKeyword("map"); + registerKeyword("month"); + registerKeyword("password"); + registerKeyword("role"); + registerKeyword("summary"); + registerKeyword("title"); + registerKeyword("type"); + registerKeyword("value"); + registerKeyword("year"); + + // Tell hibernate to use getBytes instead of getBinaryStream + getDefaultProperties().setProperty(Environment.USE_STREAMS_FOR_BINARY, "false"); + // No batch statements + getDefaultProperties().setProperty(Environment.STATEMENT_BATCH_SIZE, NO_BATCH); + } + + /** + * Does this dialect support the FOR UPDATE syntax? + * + * @return empty string ... Teradata does not support + * FOR UPDATE syntax + */ + @Override + public String getForUpdateString() { + return ""; + } + + @Override + public boolean supportsIdentityColumns() { + return false; + } + + @Override + public boolean supportsSequences() { + return false; + } + + @Override + public String getAddColumnString() { + return "Add"; + } + + @Override + public boolean supportsTemporaryTables() { + return true; + } + + @Override + public String getCreateTemporaryTableString() { + return "create global temporary table"; + } + + @Override + public String getCreateTemporaryTablePostfix() { + return " on commit preserve rows"; + } + + @Override + public Boolean performTemporaryTableDDLInIsolation() { + return Boolean.TRUE; + } + + @Override + public boolean dropTemporaryTableAfterUse() { + return false; + } + + /** + * Get the name of the database type associated with the given + * java.sql.Types typecode. + * + * @param code + * java.sql.Types typecode + * @param length + * the length or precision of the column + * @param precision + * the precision of the column + * @param scale + * the scale of the column + * + * @return the database type name + * + * @throws HibernateException + */ + public String getTypeName(int code, int length, int precision, int scale) + throws HibernateException { + /* + * We might want a special case for 19,2. This is very common for money + * types and here it is converted to 18,1 + */ + float f = precision > 0 ? (float) scale / (float) precision : 0; + int p = (precision > 18 ? 18 : precision); + int s = (precision > 18 ? (int) (18.0 * f) : (scale > 18 ? 18 : scale)); + + return super.getTypeName(code, length, p, s); + } + + @Override + public boolean supportsCascadeDelete() { + return false; + } + + @Override + public boolean supportsCircularCascadeDeleteConstraints() { + return false; + } + + @Override + public boolean areStringComparisonsCaseInsensitive() { + return true; + } + + @Override + public boolean supportsEmptyInList() { + return false; + } + + @Override + public String getSelectClauseNullString(int sqlType) { + String v = "null"; + + switch (sqlType) { + case Types.BIT: + case Types.TINYINT: + case Types.SMALLINT: + case Types.INTEGER: + case Types.BIGINT: + case Types.FLOAT: + case Types.REAL: + case Types.DOUBLE: + case Types.NUMERIC: + case Types.DECIMAL: + v = "cast(null as decimal)"; + break; + case Types.CHAR: + case Types.VARCHAR: + case Types.LONGVARCHAR: + v = "cast(null as varchar(255))"; + break; + case Types.DATE: + case Types.TIME: + case Types.TIMESTAMP: + v = "cast(null as timestamp)"; + break; + case Types.BINARY: + case Types.VARBINARY: + case Types.LONGVARBINARY: + case Types.NULL: + case Types.OTHER: + case Types.JAVA_OBJECT: + case Types.DISTINCT: + case Types.STRUCT: + case Types.ARRAY: + case Types.BLOB: + case Types.CLOB: + case Types.REF: + case Types.DATALINK: + case Types.BOOLEAN: + break; + } + return v; + } + + @Override + public String getCreateMultisetTableString() { + return "create multiset table "; + } + + @Override + public boolean supportsLobValueChangePropogation() { + return false; + } + + @Override + public boolean doesReadCommittedCauseWritersToBlockReaders() { + return true; + } + + @Override + public boolean doesRepeatableReadCauseReadersToBlockWriters() { + return true; + } + + @Override + public boolean supportsBindAsCallableArgument() { + return false; + } + + @Override + public ViolatedConstraintNameExtracter getViolatedConstraintNameExtracter() { + return EXTRACTER; + } + + private static 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. + */ + @Override + public String extractConstraintName(SQLException sqle) { + String constraintName = null; + + int errorCode = sqle.getErrorCode(); + if (errorCode == 27003) { + constraintName = extractUsingTemplate("Unique constraint (", ") violated.", + sqle.getMessage()); + } + + if (constraintName != null) { + int i = constraintName.indexOf('.'); + if (i != -1) { + constraintName = constraintName.substring(i + 1); + } + } + return constraintName; + } + }; + + @Override + public boolean supportsNotNullUnique() { + return false; + } + + @Override + public boolean supportsExpectedLobUsagePattern() { + return true; + } + + @Override + public boolean supportsUnboundedLobLocatorMaterialization() { + return false; + } + + public boolean supportsDropPreProcess() { + return true; + } + + public String performDropPreProcess(Statement stmt, String dropSql) throws SQLException { + + String alterStr = "alter"; + String tableStr = "table"; + String dropStr = "drop"; + String constraintStr = "constraint"; + + java.util.StringTokenizer st = new java.util.StringTokenizer(dropSql); + if (alterStr.equalsIgnoreCase(st.nextToken()) && tableStr.equalsIgnoreCase(st.nextToken())) { + String tableName = st.nextToken(); + + if ((tableName.startsWith("\"")) && (!tableName.endsWith("\""))) { + String next = null; + while (true) { + next = st.nextToken(); + tableName += " " + next; + if (next.endsWith("\\\"")) { + continue; + } + if (next.endsWith("\"")) { + break; + } + } + + } + if (dropStr.equalsIgnoreCase(st.nextToken()) + && constraintStr.equalsIgnoreCase(st.nextToken())) { + String constraintName = st.nextToken(); + + // Table name might have whitespace characters within name so + // just take whatever lies between + // "alter table " and "drop constraint" + + int idx_start = dropSql.indexOf(tableStr, 0) + 5; + int idx_end = dropSql.lastIndexOf(dropStr); + tableName = dropSql.substring(idx_start, idx_end).trim(); + + if (tableName.startsWith("\"") && tableName.endsWith("\"")) { + tableName = tableName.substring(1, tableName.length() - 1); + } + + String arrStr = null; + String queryStr = "sel IndexId, ChildTable, IndexName from dbc.RI_Distinct_ChildrenV where IndexName = '" + + constraintName + "'"; + java.sql.ResultSet rs = stmt.executeQuery(queryStr); + + if (rs.next()) { + arrStr = "drop table \"" + tableName + "_" + rs.getString(1) + "\""; + rs.close(); + return arrStr; + } + } + } + return null; + } + + public void performDropPostProcess(Statement stmt, String dropSql) throws SQLException { + if (dropSql == null) { + return; + } + stmt.executeUpdate(dropSql); + } +} \ No newline at end of file diff --git a/querydsl-jpa/src/test/java/com/mysema/query/suites/Teradata.java b/querydsl-jpa/src/test/java/com/mysema/query/suites/Teradata.java new file mode 100644 index 000000000..2c5854676 --- /dev/null +++ b/querydsl-jpa/src/test/java/com/mysema/query/suites/Teradata.java @@ -0,0 +1,24 @@ +package com.mysema.query.suites; + +import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; +import javax.persistence.Persistence; + +public class Teradata { + + public static void main(String[] args) { + EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("teradata"); + try { + EntityManager entityManager = entityManagerFactory.createEntityManager(); + try { + entityManager.getTransaction().begin(); + entityManager.getTransaction().commit(); + } finally { + entityManager.close(); + } + } finally { + entityManagerFactory.close(); + } + } + +} diff --git a/querydsl-jpa/src/test/resources/META-INF/persistence.xml b/querydsl-jpa/src/test/resources/META-INF/persistence.xml index 59c97f3f6..5bbc033e4 100644 --- a/querydsl-jpa/src/test/resources/META-INF/persistence.xml +++ b/querydsl-jpa/src/test/resources/META-INF/persistence.xml @@ -277,7 +277,7 @@ org.hibernate.ejb.HibernatePersistence - + @@ -285,6 +285,7 @@ + diff --git a/querydsl-jpa/src/test/resources/com/mysema/testutil/teradata.properties b/querydsl-jpa/src/test/resources/com/mysema/testutil/teradata.properties index 246b08954..a621b9899 100644 --- a/querydsl-jpa/src/test/resources/com/mysema/testutil/teradata.properties +++ b/querydsl-jpa/src/test/resources/com/mysema/testutil/teradata.properties @@ -1,4 +1,4 @@ -hibernate.dialect=org.hibernate.dialect.TeradataDialect +hibernate.dialect=com.mysema.query.jpa.support.TeradataDialect hibernate.connection.driver_class=com.teradata.jdbc.TeraDriver hibernate.connection.url=jdbc:teradata://teradata/dbc hibernate.connection.username=querydsl @@ -6,4 +6,5 @@ hibernate.connection.password=querydsl #hibernate.show_sql=true hibernate.flushMode=FLUSH_AUTO -hibernate.hbm2ddl.auto=update \ No newline at end of file +hibernate.hbm2ddl.auto=update +hibernate.globally_quoted_identifiers=true \ No newline at end of file