A few nits, it looks good otherwise. * Newly added files need to have svn:eol-style=native set - BooleanRepresentation, TestBooleanRepresentation
* BooleanRepresentation - Still have a few TODOs regarding messages / logging to get cleaned up * ref_guide_dbsetup.xml - Remove/address TODO On Sun, Jan 18, 2015 at 8:30 AM, <strub...@apache.org> wrote: > Author: struberg > Date: Sun Jan 18 14:30:44 2015 > New Revision: 1652761 > > URL: http://svn.apache.org/r1652761 > Log: > 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 > <property name="openjpa.jdbc.DBDictionary" > > value="(BitTypeName=CHAR(1),BooleanTypeName=CHAR(1),BooleanRepresentation=STRING_10)"/> > > Added: > > openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/BooleanRepresentation.java > > openjpa/trunk/openjpa-jdbc/src/test/java/org/apache/openjpa/jdbc/sql/TestBooleanRepresentation.java > Modified: > > openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java > > openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionaryFactory.java > > openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PostgresDictionary.java > > openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/localizer.properties > openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_conf.xml > openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_dbsetup.xml > > Added: > openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/BooleanRepresentation.java > URL: > http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/BooleanRepresentation.java?rev=1652761&view=auto > > ============================================================================== > --- > openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/BooleanRepresentation.java > (added) > +++ > openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/BooleanRepresentation.java > Sun Jan 18 14:30:44 2015 > @@ -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; > + > +/** > + * <p>Defines how a {@code Boolean} or {@code boolean} value > + * gets stored in the database by default.</p> > + * > + * <p>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: > + * <pre> > + * <property name="openjpa.jdbc.DBDictionary" > + * > > value="(BitTypeName=CHAR(1),BooleanTypeName=CHAR(1),BooleanRepresentation=STRING_10)"/> > + * </pre> > + * > + * 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. > + * </p> > + * > + * <p>The following {@code BooleanRepresentation} configuration options > are possible: > + * <ul> > + * <li>One of the enum values of {@link > org.apache.openjpa.jdbc.sql.BooleanRepresentation.BooleanRepresentations} > + * , e.g.: > + * <pre> > + * <property name="openjpa.jdbc.DBDictionary" > value="(BooleanRepresentation=STRING_YN)"/> > + * </pre> > + * </li> > + * <li> > + * Two slash ({@code '/'}) separated true/false value strings: > + * <pre> > + * <property name="openjpa.jdbc.DBDictionary" > value="(BooleanRepresentation=oui/non)"/> > + * </pre> > + * </li> > + * <li> > + * A fully qualified class name of your own {@link > org.apache.openjpa.jdbc.sql.BooleanRepresentation} > + * implementation, e.g.: > + * <pre> > + * <property name="openjpa.jdbc.DBDictionary" > + * > value="(BooleanRepresentation=com.mycompany.MyOwnBoolRepresentation)"/> > + * </pre> > + * </li> > + * </ul> > + * > + * </p> > + * > + * <p>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.</p> > + */ > +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)); > + } > + }; > + > + } > + > +} > > Modified: > openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java > URL: > http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java?rev=1652761&r1=1652760&r2=1652761&view=diff > > ============================================================================== > --- > openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java > (original) > +++ > openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java > Sun Jan 18 14:30:44 2015 > @@ -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 <code>elem</code> to <code>selectSQL</code>. > * @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<String> 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); > } > > > Modified: > openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionaryFactory.java > URL: > http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionaryFactory.java?rev=1652761&r1=1652760&r2=1652761&view=diff > > ============================================================================== > --- > openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionaryFactory.java > (original) > +++ > openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionaryFactory.java > Sun Jan 18 14:30:44 2015 > @@ -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(); > > Modified: > openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PostgresDictionary.java > URL: > http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PostgresDictionary.java?rev=1652761&r1=1652760&r2=1652761&view=diff > > ============================================================================== > --- > openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PostgresDictionary.java > (original) > +++ > openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/PostgresDictionary.java > Sun Jan 18 14:30:44 2015 > @@ -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. > */ > > Modified: > openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/localizer.properties > URL: > http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/localizer.properties?rev=1652761&r1=1652760&r2=1652761&view=diff > > ============================================================================== > --- > openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/localizer.properties > (original) > +++ > openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/sql/localizer.properties > Sun Jan 18 14:30:44 2015 > @@ -231,4 +231,5 @@ jdbc4-setbinarystream-unsupported: The J > 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}. > > > Added: > openjpa/trunk/openjpa-jdbc/src/test/java/org/apache/openjpa/jdbc/sql/TestBooleanRepresentation.java > URL: > http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/test/java/org/apache/openjpa/jdbc/sql/TestBooleanRepresentation.java?rev=1652761&view=auto > > ============================================================================== > --- > openjpa/trunk/openjpa-jdbc/src/test/java/org/apache/openjpa/jdbc/sql/TestBooleanRepresentation.java > (added) > +++ > openjpa/trunk/openjpa-jdbc/src/test/java/org/apache/openjpa/jdbc/sql/TestBooleanRepresentation.java > Sun Jan 18 14:30:44 2015 > @@ -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 <T> void checkBooleanRepresentation(String representationKey, > final Class<T> 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<T> dummyPreparedStatement = new > DummyPreparedStatement<T>(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<T> extends > DelegatingPreparedStatement { > + private final Class<T> expectedType; > + private Object booleanRepresentationValue; > + > + > + public DummyPreparedStatement(Class<T> 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)); > + } > + } > +} > > Modified: openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_conf.xml > URL: > http://svn.apache.org/viewvc/openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_conf.xml?rev=1652761&r1=1652760&r2=1652761&view=diff > > ============================================================================== > --- openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_conf.xml > (original) > +++ openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_conf.xml Sun > Jan 18 14:30:44 2015 > @@ -3568,8 +3568,9 @@ openjpa.ConnectionDriverName</literal></ > <classname>org.apache.openjpa.jdbc.sql.DBDictionary</classname></ulink> > 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 > <xref linkend="ref_guide_dbsetup_dbsupport"/> for details. > </para> > </section> > > Modified: > openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_dbsetup.xml > URL: > http://svn.apache.org/viewvc/openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_dbsetup.xml?rev=1652761&r1=1652760&r2=1652761&view=diff > > ============================================================================== > --- openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_dbsetup.xml > (original) > +++ openjpa/trunk/openjpa-project/src/doc/manual/ref_guide_dbsetup.xml Sun > Jan 18 14:30:44 2015 > @@ -1142,6 +1142,34 @@ the <literal>INSERT/UPDATE</literal> ope > generated by the <literal>mappingtool</literal>. > </para> > </listitem> > + > + > + > + <!-- MSX TODO START DOCUMENT --> > + <listitem id="DBDictionary.BooleanRepresentation"> > + <para> > + <indexterm> > + <primary> > + DDL > + </primary> > + <secondary> > + BooleanRepresentation > + </secondary> > + </indexterm> > +<literal>BooleanRepresentation</literal>: > +The overridden default representation for > <literal>java.lang.Boolean</literal> or > +<literal>boolean</literal> fields in JPA Entities. A > +<ulink > url="../javadoc/org/apache/openjpa/jdbc/sql/BooleanRepresentation.html"> > > +<classname>org.apache.openjpa.jdbc.sql.BooleanRepresentation</classname></ulink> > +describes how Boolean values in entities get mapped into the database by > default. > +Note that you additionally might need to define the > <literal>BooleanTypeName</literal> > +<literal>BitTypeName</literal> settings to fit your selected > BooleanRepresenation. > + </para> > + </listitem> > + <!-- MSX TODO END DOCUMENT --> > + > + > + > <listitem id="DBDictionary.BooleanTypeName"> > <para> > <indexterm> > @@ -1152,9 +1180,9 @@ generated by the <literal>mappingtool</l > BooleanTypeName > </secondary> > </indexterm> > -<literal>BooleanTypeName</literal>: > -The overridden default column type for > -<literal>java.sql.Types.BOOLEAN</literal>. This is used only when the > schema > +<literal>BooleanTypeName</literal>: > +The overridden default column type for > +<literal>java.sql.Types.BOOLEAN</literal>. This is used only when the > schema > is generated by the <literal>mappingtool</literal>. > </para> > </listitem> > > > -- *Rick Curtis*