mirror of https://github.com/apache/openjpa.git
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)"/> git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@1652761 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
4ca9dbd741
commit
c5e4fac841
|
@ -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));
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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}.
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3568,8 +3568,9 @@ openjpa.ConnectionDriverName</literal></link>
|
|||
<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>
|
||||
|
|
|
@ -1142,6 +1142,34 @@ the <literal>INSERT/UPDATE</literal> operations with an
|
|||
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</literal>.
|
|||
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>
|
||||
|
|
Loading…
Reference in New Issue