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:
+ *
+ * - One of the enum values of {@link org.apache.openjpa.jdbc.sql.BooleanRepresentation.BooleanRepresentations}
+ * , e.g.:
+ *
+ * <property name="openjpa.jdbc.DBDictionary" value="(BooleanRepresentation=STRING_YN)"/>
+ *
+ *
+ * -
+ * Two slash ({@code '/'}) separated true/false value strings:
+ *
+ * <property name="openjpa.jdbc.DBDictionary" value="(BooleanRepresentation=oui/non)"/>
+ *
+ *
+ * -
+ * A fully qualified class name of your own {@link org.apache.openjpa.jdbc.sql.BooleanRepresentation}
+ * implementation, e.g.:
+ *
+ * <property name="openjpa.jdbc.DBDictionary"
+ * value="(BooleanRepresentation=com.mycompany.MyOwnBoolRepresentation)"/>
+ *
+ *
+ *
+ *
+ *
+ *
+ * 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 extends BooleanRepresentation> booleanRepresentationClass
+ = (Class extends BooleanRepresentation>) 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.