From c5e4fac8410be1dbd0a97f413c49c513def20036 Mon Sep 17 00:00:00 2001 From: Mark Struberg Date: Sun, 18 Jan 2015 14:30:44 +0000 Subject: [PATCH] OPENJPA-2558 implement BooleanRepresentation which can be switched via config Each DBDictionary has it's own default BooleanRepresentation but can easily get changed by the user e.g. via git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@1652761 13f79535-47bb-0310-9956-ffa450edef68 --- .../jdbc/sql/BooleanRepresentation.java | 303 ++++++++++++++++++ .../apache/openjpa/jdbc/sql/DBDictionary.java | 58 ++-- .../openjpa/jdbc/sql/DBDictionaryFactory.java | 3 +- .../openjpa/jdbc/sql/PostgresDictionary.java | 10 +- .../openjpa/jdbc/sql/localizer.properties | 1 + .../jdbc/sql/TestBooleanRepresentation.java | 159 +++++++++ .../src/doc/manual/ref_guide_conf.xml | 5 +- .../src/doc/manual/ref_guide_dbsetup.xml | 34 +- 8 files changed, 537 insertions(+), 36 deletions(-) create mode 100644 openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/BooleanRepresentation.java create mode 100644 openjpa-jdbc/src/test/java/org/apache/openjpa/jdbc/sql/TestBooleanRepresentation.java diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/BooleanRepresentation.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/BooleanRepresentation.java new file mode 100644 index 000000000..66e58c70f --- /dev/null +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/BooleanRepresentation.java @@ -0,0 +1,303 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.jdbc.sql; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Arrays; + +import org.apache.openjpa.lib.util.Localizer; +import org.apache.openjpa.util.UserException; + +/** + *

Defines how a {@code Boolean} or {@code boolean} value + * gets stored in the database by default.

+ * + *

The {@link DBDictionary} defines a default representation for {@code Boolean} + * and {@code boolean} fields in JPA entities. The {@link org.apache.openjpa.jdbc.sql.OracleDictionary} + * for example uses a {@code NUMBER(1)} with the values {@code (int) 1} and {@code (int) 0} by default. + * However, sometimes you like to use a different default representation for Boolean values in your database. + * If your application likes to store boolean values in a {@code CHAR(1)} field with {@code "T"} and + * {@code "F"} values then you might configure the {@link org.apache.openjpa.jdbc.sql.DBDictionary} + * to use the {@link org.apache.openjpa.jdbc.sql.BooleanRepresentation.BooleanRepresentations#STRING_TF} + * BooleanRepresentation: + *

+ * <property name="openjpa.jdbc.DBDictionary"
+ *     value="(BitTypeName=CHAR(1),BooleanTypeName=CHAR(1),BooleanRepresentation=STRING_10)"/>
+ * 
+ * + * Please note that you still need to adopt the mapping separately by setting the + * {@code BitTypeName} and/or {@code BooleanTypeName} (depending on your database) to + * the desired type in the database. + *

+ * + *

The following {@code BooleanRepresentation} configuration options are possible: + *

+ * + *

+ * + *

If a single column uses a different representation then they + * still can tweak this for those columns with the + * {@code org.apache.openjpa.persistence.ExternalValues} annotation.

+ */ +public interface BooleanRepresentation { + + /** + * Set the boolean value into the statement + * @param stmnt + * @param columnIndex + * @param val the boolean value to set + * @throws SQLException + */ + public void setBoolean(PreparedStatement stmnt, int columnIndex, boolean val) throws SQLException; + + public boolean getBoolean(ResultSet rs, int columnIndex) throws SQLException; + + + public static class Factory { + public static BooleanRepresentation valueOf(String booleanRepresentationKey, ClassLoader cl) { + BooleanRepresentation booleanRepresentation = null; + + // 1st step, try to lookup the BooleanRepresentation from the default ones + try { + booleanRepresentation = BooleanRepresentations.valueOf(booleanRepresentationKey); + } + catch (IllegalArgumentException iae) { + // nothing to do + } + + if (booleanRepresentation == null && booleanRepresentationKey.contains("/")) { + // if the key contains a '/' then the first value is the key for 'true', the 2nd value is for 'false' + String[] vals = booleanRepresentationKey.split("/"); + if (vals.length == 2) { + booleanRepresentation = new StringBooleanRepresentation(vals[0], vals[1]); + } + } + else { + // or do a class lookup for a custom BooleanRepresentation + try { + Class booleanRepresentationClass + = (Class) cl.loadClass(booleanRepresentationKey); + booleanRepresentation = booleanRepresentationClass.newInstance(); + } + catch (Exception e) { + // nothing to do + //X TODO probably log some error? + } + } + + + if (booleanRepresentation == null) { + Localizer _loc = Localizer.forPackage(BooleanRepresentation.class); + throw new UserException(_loc.get("unknown-booleanRepresentation", + new Object[]{booleanRepresentationKey, + Arrays.toString(BooleanRepresentation.BooleanRepresentations.values())} + )); + + } + else { + //X TODO add logging about which one got picked up finally + } + + return booleanRepresentation; + } + } + + /** + * BooleanRepresentation which takes 2 strings for true and false representations + * as constructor parameter; + */ + public static class StringBooleanRepresentation implements BooleanRepresentation { + private final String trueRepresentation; + private final String falseRepresentation; + + public StringBooleanRepresentation(String trueRepresentation, String falseRepresentation) { + this.trueRepresentation = trueRepresentation; + this.falseRepresentation = falseRepresentation; + } + + @Override + public void setBoolean(PreparedStatement stmnt, int idx, boolean val) throws SQLException{ + stmnt.setString(idx, val ? trueRepresentation : falseRepresentation); + } + + @Override + public boolean getBoolean(ResultSet rs, int columnIndex) throws SQLException { + return trueRepresentation.equals(rs.getString(columnIndex)); + } + + @Override + public String toString() { + return "StringBooleanRepresentation with the following values for true and false: " + + trueRepresentation + " / " + falseRepresentation; + } + } + + public enum BooleanRepresentations implements BooleanRepresentation { + + /** + * Booleans are natively supported by this very database. + * The database column is e.g. a NUMBER(1) + * OpenJPA will use preparedStatement.setBoolean(..) for it + */ + BOOLEAN { + @Override + public void setBoolean(PreparedStatement stmnt, int idx, boolean val) throws SQLException { + stmnt.setBoolean(idx, val); + } + + @Override + public boolean getBoolean(ResultSet rs, int columnIndex) throws SQLException { + return rs.getBoolean(columnIndex); + } + }, + + /** + * Booleans are stored as numeric int 1 and int 0 values. + * The database column is e.g. a NUMBER(1) + * OpenJPA will use preparedStatement.setInt(..) for it + */ + INT_10 { + @Override + public void setBoolean(PreparedStatement stmnt, int idx, boolean val) throws SQLException{ + stmnt.setInt(idx, val ? 1 : 0); + } + + @Override + public boolean getBoolean(ResultSet rs, int columnIndex) throws SQLException { + return rs.getInt(columnIndex) > 0; + } + }, + + /** + * Booleans are stored as String "1" for {@code true} + * and String "0" for {@code false}. + * The database column is e.g. a CHAR(1) or VARCHAR(1) + * OpenJPA will use preparedStatement.setString(..) for it + */ + STRING_10 { + @Override + public void setBoolean(PreparedStatement stmnt, int idx, boolean val) throws SQLException{ + stmnt.setString(idx, val ? "1" : "0"); + } + + @Override + public boolean getBoolean(ResultSet rs, int columnIndex) throws SQLException { + return "1".equals(rs.getString(columnIndex)); + } + }, + + /** + * Booleans are stored as String "Y" for {@code true} + * and String "N" for {@code false}. + * The database column is e.g. a CHAR(1) or VARCHAR(1) + * OpenJPA will use preparedStatement.setString(..) for it + */ + STRING_YN { + @Override + public void setBoolean(PreparedStatement stmnt, int idx, boolean val) throws SQLException{ + stmnt.setString(idx, val ? "Y" : "N"); + } + + @Override + public boolean getBoolean(ResultSet rs, int columnIndex) throws SQLException { + return "Y".equals(rs.getString(columnIndex)); + } + }, + + /** + * Booleans are stored as String "y" for {@code true} + * and String "n" for {@code false}. + * The database column is e.g. a CHAR(1) or VARCHAR(1) + * OpenJPA will use preparedStatement.setString(..) for it + */ + STRING_YN_LOWERCASE { + @Override + public void setBoolean(PreparedStatement stmnt, int idx, boolean val) throws SQLException{ + stmnt.setString(idx, val ? "y" : "n"); + } + + @Override + public boolean getBoolean(ResultSet rs, int columnIndex) throws SQLException { + return "y".equals(rs.getString(columnIndex)); + } + }, + + /** + * Booleans are stored as String "T" for {@code true} + * and String "F" for {@code false}. + * The database column is e.g. a CHAR(1) or VARCHAR(1) + * OpenJPA will use preparedStatement.setString(..) for it + */ + STRING_TF { + @Override + public void setBoolean(PreparedStatement stmnt, int idx, boolean val) throws SQLException{ + stmnt.setString(idx, val ? "T" : "F"); + } + + @Override + public boolean getBoolean(ResultSet rs, int columnIndex) throws SQLException { + return "T".equals(rs.getString(columnIndex)); + } + + }, + + /** + * Booleans are stored as String "t" for {@code true} + * and String "f" for {@code false}. + * The database column is e.g. a CHAR(1) or VARCHAR(1) + * OpenJPA will use preparedStatement.setString(..) for it + */ + STRING_TF_LOWERCASE { + @Override + public void setBoolean(PreparedStatement stmnt, int idx, boolean val) throws SQLException{ + stmnt.setString(idx, val ? "t" : "f"); + } + + @Override + public boolean getBoolean(ResultSet rs, int columnIndex) throws SQLException { + return "t".equals(rs.getString(columnIndex)); + } + }; + + } + +} diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java index af7ac1165..d064e1c91 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java @@ -178,9 +178,9 @@ public class DBDictionary private static final Localizer _loc = Localizer.forPackage(DBDictionary.class); // Database version info preferably set from Connection metadata - private int major; - private int minor; - + private int major; + private int minor; + // schema data public String platform = "Generic"; public String databaseProductName = ""; @@ -326,7 +326,14 @@ public class DBDictionary */ public enum DateMillisecondBehaviors { DROP, ROUND, RETAIN }; private DateMillisecondBehaviors dateMillisecondBehavior; - + + /** + * Defines how {@code Boolean} and {@code boolean} values get represented + * in OpenJPA. Default to {@link org.apache.openjpa.jdbc.sql.BooleanRepresentation.BooleanRepresentations#INT_10} + * for backward compatibility. + */ + protected BooleanRepresentation booleanRepresentation = BooleanRepresentation.BooleanRepresentations.INT_10; + public int characterColumnSize = 255; public String arrayTypeName = "ARRAY"; public String bigintTypeName = "BIGINT"; @@ -703,7 +710,7 @@ public class DBDictionary */ public boolean getBoolean(ResultSet rs, int column) throws SQLException { - return rs.getBoolean(column); + return booleanRepresentation.getBoolean(rs, column); } /** @@ -1054,10 +1061,9 @@ public class DBDictionary /** * Set the given value as a parameter to the statement. */ - public void setBoolean(PreparedStatement stmnt, int idx, boolean val, - Column col) + public void setBoolean(PreparedStatement stmnt, int idx, boolean val, Column col) throws SQLException { - stmnt.setInt(idx, (val) ? 1 : 0); + booleanRepresentation.setBoolean(stmnt, idx, val); } /** @@ -1851,8 +1857,6 @@ public class DBDictionary /** * Helper method that inserts a size clause for a given SQL type. * - * @see appendSize - * * @param typeName The SQL type e.g. INT * @param size The size clause e.g. (10) * @return The typeName + size clause. Usually the size clause will @@ -2776,12 +2780,11 @@ public class DBDictionary /** * Append elem to selectSQL. * @param selectSQL The SQLBuffer to append to. - * @param alias A {@link SQLBuffer} or a {@link String} to append. + * @param elem A {@link SQLBuffer} or a {@link String} to append. * * @since 1.1.0 */ - protected void appendSelect(SQLBuffer selectSQL, Object elem, Select sel, - int idx) { + protected void appendSelect(SQLBuffer selectSQL, Object elem, Select sel, int idx) { if (elem instanceof SQLBuffer) selectSQL.append((SQLBuffer) elem); else @@ -3154,7 +3157,7 @@ public class DBDictionary * getValidColumnName method of the DB dictionary should be invoked to make * it valid. * - * @see getValidColumnName + * @see #getValidColumnName(org.apache.openjpa.jdbc.identifier.DBIdentifier, org.apache.openjpa.jdbc.schema.Table) */ public final Set getInvalidColumnWordSet() { return invalidColumnWordSet; @@ -5396,11 +5399,10 @@ public class DBDictionary * Validate that the given name is not longer than given maximum length. Uses the unqualified name * from the supplied {@link DBIdentifier} by default.. * - * @param identifer The database identifier to check. + * @param identifier The database identifier to check. * @param length Max length for this type of identifier * @param msgKey message identifier for the exception. - * @param qualified If true the qualified name of the DBIdentifier will be used. - * + * * @throws {@link UserException} with the given message key if the given name is indeed longer. * @return the same name. */ @@ -5412,7 +5414,7 @@ public class DBDictionary * Validate that the given name is not longer than given maximum length. Conditionally uses the unqualified name * from the supplied {@link DBIdentifier}. * - * @param identifer The database identifier to check. + * @param identifier The database identifier to check. * @param length Max length for this type of identifier * @param msgKey message identifier for the exception. * @param qualified If true the qualified name of the DBIdentifier will be used. @@ -5465,7 +5467,7 @@ public class DBDictionary } /** - * @param metadata the DatabaseMetaData to use to determine whether delimiters can be supported + * @param metaData the DatabaseMetaData to use to determine whether delimiters can be supported */ private void setSupportsDelimitedIdentifiers(DatabaseMetaData metaData) { try { @@ -5673,7 +5675,23 @@ public class DBDictionary } } - protected boolean isUsingRange(long start, long end) { + public BooleanRepresentation getBooleanRepresentation() { + return booleanRepresentation; + } + + public void setBooleanRepresentation(String booleanRepresentationKey) { + BooleanRepresentation evaluatedBooleanRepresentation = null; + if (booleanRepresentationKey != null && booleanRepresentationKey.length() > 0) { + ClassLoader cl = conf.getUserClassLoader(); + evaluatedBooleanRepresentation = BooleanRepresentation.Factory.valueOf(booleanRepresentationKey, cl); + } + + booleanRepresentation = evaluatedBooleanRepresentation != null + ? evaluatedBooleanRepresentation + : BooleanRepresentation.BooleanRepresentations.INT_10; + } + + protected boolean isUsingRange(long start, long end) { return isUsingOffset(start) || isUsingLimit(end); } diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionaryFactory.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionaryFactory.java index 213513579..f9a8d6e88 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionaryFactory.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionaryFactory.java @@ -215,8 +215,7 @@ public class DBDictionaryFactory { /** * Guess the dictionary class name to use based on the product string. */ - private static String dictionaryClassForString(String prod - , JDBCConfiguration conf) { + private static String dictionaryClassForString(String prod, JDBCConfiguration conf) { if (StringUtils.isEmpty(prod)) return null; prod = prod.toLowerCase(); diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PostgresDictionary.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PostgresDictionary.java index 2fbf7b6fb..343c428be 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PostgresDictionary.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PostgresDictionary.java @@ -187,6 +187,7 @@ public class PostgresDictionary "SET", "FLOAT4", "FLOAT8", "ABSTIME", "RELTIME", "TINTERVAL", "MONEY", })); + booleanRepresentation = BooleanRepresentation.BooleanRepresentations.BOOLEAN; supportsLockingWithDistinctClause = false; supportsQueryTimeout = false; @@ -303,15 +304,6 @@ public class PostgresDictionary } } - @Override - public void setBoolean(PreparedStatement stmnt, int idx, boolean val, - Column col) - throws SQLException { - // postgres actually requires that a boolean be set: it cannot - // handle a numeric argument. - stmnt.setBoolean(idx, val); - } - /** * Handle XML and bytea/oid columns in a PostgreSQL way. */ diff --git a/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/localizer.properties b/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/localizer.properties index 3b700765d..cfb122bad 100644 --- a/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/localizer.properties +++ b/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/localizer.properties @@ -231,4 +231,5 @@ jdbc4-setbinarystream-unsupported: The JRE or JDBC level in use does not support sequence-cache-warning: Setting the useNativeSequenceCache property on the DBDictionary no longer has an \ effect. Code has been added to allow, by default, the functionality provided in previous releases \ via the useNativeSequenceCache property. +unknown-booleanRepresentation: Unknown BooleanRepresentation {0}. Value must be one of {1}. diff --git a/openjpa-jdbc/src/test/java/org/apache/openjpa/jdbc/sql/TestBooleanRepresentation.java b/openjpa-jdbc/src/test/java/org/apache/openjpa/jdbc/sql/TestBooleanRepresentation.java new file mode 100644 index 000000000..2ab4c1ebe --- /dev/null +++ b/openjpa-jdbc/src/test/java/org/apache/openjpa/jdbc/sql/TestBooleanRepresentation.java @@ -0,0 +1,159 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.jdbc.sql; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.concurrent.atomic.AtomicBoolean; + +import junit.framework.Assert; +import junit.framework.TestCase; +import org.apache.openjpa.lib.jdbc.DelegatingPreparedStatement; + +/** + * Test for the {@link org.apache.openjpa.jdbc.sql.BooleanRepresentation} factory and default impls + */ +public class TestBooleanRepresentation extends TestCase { + + + public void testBooleanRepresentation() throws Exception { + + checkBooleanRepresentation("BOOLEAN", Boolean.class, Boolean.TRUE, Boolean.FALSE); + checkBooleanRepresentation("INT_10", Integer.class, 1, 0); + checkBooleanRepresentation("STRING_10", String.class, "1", "0"); + + checkBooleanRepresentation("STRING_YN", String.class, "Y", "N"); + checkBooleanRepresentation("STRING_YN_LOWERCASE", String.class, "y", "n"); + + checkBooleanRepresentation("STRING_TF", String.class, "T", "F"); + checkBooleanRepresentation("STRING_TF_LOWERCASE", String.class, "t", "f"); + + // and now up to more sophisticated ones: + checkBooleanRepresentation("oui/non", String.class, "oui", "non"); + + checkBooleanRepresentation( + "org.apache.openjpa.jdbc.sql.TestBooleanRepresentation$DummyTestBooleanRepresentation", + String.class, "somehowtrue", "somehowfalse"); + } + + private void checkBooleanRepresentation(String representationKey, final Class expectedType, + final T yesRepresentation, final T noRepresentation) + throws Exception { + ClassLoader cl = TestBooleanRepresentation.class.getClassLoader(); + BooleanRepresentation booleanRepresentation = BooleanRepresentation.Factory.valueOf(representationKey, cl); + Assert.assertNotNull(booleanRepresentation); + + DummyPreparedStatement dummyPreparedStatement = new DummyPreparedStatement(expectedType); + + booleanRepresentation.setBoolean(dummyPreparedStatement, 1, true); + Assert.assertEquals(yesRepresentation, dummyPreparedStatement.getBooleanRepresentationValue()); + + booleanRepresentation.setBoolean(dummyPreparedStatement, 1, false); + Assert.assertEquals(noRepresentation, dummyPreparedStatement.getBooleanRepresentationValue()); + + + // and also test getBoolean! + ResultSet yesRs = (ResultSet) Proxy.newProxyInstance(cl, new Class[]{ResultSet.class}, + new InvocationHandler() { + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (String.class.equals(expectedType) && !"getString".equals(method.getName()) || + Boolean.class.equals(expectedType) && !"getBoolean".equals(method.getName()) || + Integer.class.equals(expectedType) && !"getInt".equals(method.getName())) { + Assert.fail("wrong ResultSet method " + method.getName() + + "for expectedType " + expectedType.getName()); + } + return yesRepresentation; + } + }); + Assert.assertTrue(booleanRepresentation.getBoolean(yesRs, 1)); + + ResultSet noRs = (ResultSet) Proxy.newProxyInstance(cl, new Class[]{ResultSet.class}, + new InvocationHandler() { + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (String.class.equals(expectedType) && !"getString".equals(method.getName()) || + Boolean.class.equals(expectedType) && !"getBoolean".equals(method.getName()) || + Integer.class.equals(expectedType) && !"getInt".equals(method.getName())) { + Assert.fail("wrong ResultSet method " + method.getName() + + "for expectedType " + expectedType.getName()); + } + return noRepresentation; + } + }); + Assert.assertFalse(booleanRepresentation.getBoolean(noRs, 1)); + } + + + /** + * A small trick to 'intercept' the PreparedStatement call inside the BooleanRepresentation + */ + public static class DummyPreparedStatement extends DelegatingPreparedStatement { + private final Class expectedType; + private Object booleanRepresentationValue; + + + public DummyPreparedStatement(Class expectedType) { + super(null, null); + this.expectedType = expectedType; + } + + public T getBooleanRepresentationValue() { + return (T) booleanRepresentationValue; + } + + public void setBooleanRepresentationValue(T booleanRepresentationValue) { + this.booleanRepresentationValue = booleanRepresentationValue; + } + + @Override + public void setBoolean(int idx, boolean b) throws SQLException { + Assert.assertEquals(Boolean.class, expectedType); + booleanRepresentationValue = b; + } + + @Override + public void setString(int idx, String s) throws SQLException { + Assert.assertEquals(String.class, expectedType); + booleanRepresentationValue = s; + } + + @Override + public void setInt(int idx, int i) throws SQLException { + Assert.assertEquals(Integer.class, expectedType); + booleanRepresentationValue = i; + } + } + + public static class DummyTestBooleanRepresentation implements BooleanRepresentation { + @Override + public void setBoolean(PreparedStatement stmnt, int columnIndex, boolean val) throws SQLException { + stmnt.setString(columnIndex, val ? "somehowtrue" : "somehowfalse"); + } + + @Override + public boolean getBoolean(ResultSet rs, int columnIndex) throws SQLException { + return "somehowtrue".equals(rs.getString(columnIndex)); + } + } +} diff --git a/openjpa-project/src/doc/manual/ref_guide_conf.xml b/openjpa-project/src/doc/manual/ref_guide_conf.xml index 28dea2e00..94555f0bf 100644 --- a/openjpa-project/src/doc/manual/ref_guide_conf.xml +++ b/openjpa-project/src/doc/manual/ref_guide_conf.xml @@ -3568,8 +3568,9 @@ openjpa.ConnectionDriverName org.apache.openjpa.jdbc.sql.DBDictionary to use for database interaction. OpenJPA typically auto-configures the dictionary based on the JDBC URL, but you may have to set this property explicitly if you are -using an unrecognized driver, or to plug in your own dictionary for a database -OpenJPA does not support out-of-the-box. See +using an unrecognized driver, to plug in your own dictionary for a database +OpenJPA does not support out-of-the-box, or if you like to change the default +configuration of an existing dictionary. See for details. diff --git a/openjpa-project/src/doc/manual/ref_guide_dbsetup.xml b/openjpa-project/src/doc/manual/ref_guide_dbsetup.xml index c6263b809..56a08cf4a 100644 --- a/openjpa-project/src/doc/manual/ref_guide_dbsetup.xml +++ b/openjpa-project/src/doc/manual/ref_guide_dbsetup.xml @@ -1142,6 +1142,34 @@ the INSERT/UPDATE operations with an generated by the mappingtool. + + + + + + + + + DDL + + + BooleanRepresentation + + +BooleanRepresentation: +The overridden default representation for java.lang.Boolean or +boolean fields in JPA Entities. A + +org.apache.openjpa.jdbc.sql.BooleanRepresentation +describes how Boolean values in entities get mapped into the database by default. +Note that you additionally might need to define the BooleanTypeName +BitTypeName settings to fit your selected BooleanRepresenation. + + + + + + @@ -1152,9 +1180,9 @@ generated by the mappingtool. BooleanTypeName -BooleanTypeName: -The overridden default column type for -java.sql.Types.BOOLEAN. This is used only when the schema +BooleanTypeName: +The overridden default column type for +java.sql.Types.BOOLEAN. This is used only when the schema is generated by the mappingtool.