OPENJPA-2558 add getRepresentation to allow BooleanRepresentation be used in SQLBuffer

git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@1654352 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Mark Struberg 2015-01-23 19:56:22 +00:00
parent c5e4fac841
commit 8c6b968190
7 changed files with 239 additions and 235 deletions

View File

@ -21,10 +21,6 @@ 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
@ -36,8 +32,7 @@ import org.apache.openjpa.util.UserException;
* 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:
* to use the {@code "STRING_TF"} BooleanRepresentation:
* <pre>
* &lt;property name="openjpa.jdbc.DBDictionary"
* value="(BitTypeName=CHAR(1),BooleanTypeName=CHAR(1),BooleanRepresentation=STRING_10)"/&gt
@ -50,7 +45,8 @@ import org.apache.openjpa.util.UserException;
*
* <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}
* <li>One of the values of
* {@link org.apache.openjpa.jdbc.sql.BooleanRepresentationFactory#BUILTIN_BOOLEAN_REPRESENTATIONS}
* , e.g.:
* <pre>
* &lt;property name="openjpa.jdbc.DBDictionary" value="(BooleanRepresentation=STRING_YN)"/&gt
@ -77,227 +73,25 @@ import org.apache.openjpa.util.UserException;
* <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>
* @param <REPRESENTATION_TYPE> the java type which is used to store the Boolean in the database,
* e.g. {@code String} or {@code Integer}
*/
public interface BooleanRepresentation {
public interface BooleanRepresentation<REPRESENTATION_TYPE> {
/**
* 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;
/**
* Read the boolean from the given ResultSet
*/
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;
* @return return the representation for {@code true} and {@code false}
*/
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));
}
};
}
public REPRESENTATION_TYPE getRepresentation(boolean bool);
}

View File

@ -0,0 +1,214 @@
/*
* 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 java.util.HashMap;
import java.util.Map;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.util.UserException;
/**
* Factory which is being used to create the active {@link org.apache.openjpa.jdbc.sql.BooleanRepresentation}.
*/
public class BooleanRepresentationFactory {
public static BooleanRepresentation INT_10 = new Int10BooleanRepresentation();
public static BooleanRepresentation BOOLEAN = new BooleanBooleanRepresentation();
/**
* {@link org.apache.openjpa.jdbc.sql.BooleanRepresentation}s built in by default.
* Key is their config name, value is the BooleanRepresentation.
*/
static final Map<String, BooleanRepresentation<?>> BUILTIN_BOOLEAN_REPRESENTATIONS;
static {
BUILTIN_BOOLEAN_REPRESENTATIONS = new HashMap<String, 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
*/
BUILTIN_BOOLEAN_REPRESENTATIONS.put("BOOLEAN", BOOLEAN);
/**
* 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
*/
BUILTIN_BOOLEAN_REPRESENTATIONS.put("INT_10", INT_10);
/**
* 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
*/
BUILTIN_BOOLEAN_REPRESENTATIONS.put("STRING_10", new StringBooleanRepresentation("1", "0"));
/**
* 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
*/
BUILTIN_BOOLEAN_REPRESENTATIONS.put("STRING_YN", new StringBooleanRepresentation("Y", "N"));
/**
* 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
*/
BUILTIN_BOOLEAN_REPRESENTATIONS.put("STRING_YN_LOWERCASE", new StringBooleanRepresentation("y", "n"));
/**
* 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
*/
BUILTIN_BOOLEAN_REPRESENTATIONS.put("STRING_TF", new StringBooleanRepresentation("T", "F"));
/**
* 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
*/
BUILTIN_BOOLEAN_REPRESENTATIONS.put("STRING_TF_LOWERCASE", new StringBooleanRepresentation("t", "f"));
}
public static BooleanRepresentation valueOf(String booleanRepresentationKey, ClassLoader cl) {
// 1st step, try to lookup the BooleanRepresentation from the default ones
BooleanRepresentation booleanRepresentation = BUILTIN_BOOLEAN_REPRESENTATIONS.get(booleanRepresentationKey);
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(BUILTIN_BOOLEAN_REPRESENTATIONS.keySet().toArray(new String[]{}))}
));
}
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<String> {
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 getRepresentation(boolean bool) {
return bool ? trueRepresentation : falseRepresentation;
}
@Override
public String toString() {
return "StringBooleanRepresentation with the following values for true and false: "
+ trueRepresentation + " / " + falseRepresentation;
}
}
public static class BooleanBooleanRepresentation implements BooleanRepresentation<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);
}
@Override
public Boolean getRepresentation(boolean bool) {
return bool;
}
}
public static class Int10BooleanRepresentation implements BooleanRepresentation<Integer> {
@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;
}
@Override
public Integer getRepresentation(boolean bool) {
return bool ? 1 : 0;
}
}
}

View File

@ -329,10 +329,10 @@ public class DBDictionary
/**
* Defines how {@code Boolean} and {@code boolean} values get represented
* in OpenJPA. Default to {@link org.apache.openjpa.jdbc.sql.BooleanRepresentation.BooleanRepresentations#INT_10}
* in OpenJPA. Default to {@code INT_10}.
* for backward compatibility.
*/
protected BooleanRepresentation booleanRepresentation = BooleanRepresentation.BooleanRepresentations.INT_10;
protected BooleanRepresentation booleanRepresentation = BooleanRepresentationFactory.INT_10;
public int characterColumnSize = 255;
public String arrayTypeName = "ARRAY";
@ -5683,12 +5683,12 @@ public class DBDictionary
BooleanRepresentation evaluatedBooleanRepresentation = null;
if (booleanRepresentationKey != null && booleanRepresentationKey.length() > 0) {
ClassLoader cl = conf.getUserClassLoader();
evaluatedBooleanRepresentation = BooleanRepresentation.Factory.valueOf(booleanRepresentationKey, cl);
evaluatedBooleanRepresentation = BooleanRepresentationFactory.valueOf(booleanRepresentationKey, cl);
}
booleanRepresentation = evaluatedBooleanRepresentation != null
? evaluatedBooleanRepresentation
: BooleanRepresentation.BooleanRepresentations.INT_10;
: BooleanRepresentationFactory.INT_10;
}
protected boolean isUsingRange(long start, long end) {

View File

@ -187,7 +187,7 @@ public class PostgresDictionary
"SET", "FLOAT4", "FLOAT8", "ABSTIME", "RELTIME", "TINTERVAL",
"MONEY",
}));
booleanRepresentation = BooleanRepresentation.BooleanRepresentations.BOOLEAN;
booleanRepresentation = BooleanRepresentationFactory.BOOLEAN;
supportsLockingWithDistinctClause = false;
supportsQueryTimeout = false;

View File

@ -369,7 +369,7 @@ public final class SQLBuffer
} else if (type == Boolean.class) {
Boolean b = (Boolean) o;
// We store B(b)ooleans as ints. Convert
_sql.append((b.booleanValue() ? "1" : "0"));
_sql.append(_dict.getBooleanRepresentation().getRepresentation(b.booleanValue()));
} else {
_sql.append(o.toString());
}

View File

@ -24,7 +24,6 @@ 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;
@ -60,7 +59,7 @@ public class TestBooleanRepresentation extends TestCase {
final T yesRepresentation, final T noRepresentation)
throws Exception {
ClassLoader cl = TestBooleanRepresentation.class.getClassLoader();
BooleanRepresentation booleanRepresentation = BooleanRepresentation.Factory.valueOf(representationKey, cl);
BooleanRepresentation booleanRepresentation = BooleanRepresentationFactory.valueOf(representationKey, cl);
Assert.assertNotNull(booleanRepresentation);
DummyPreparedStatement<T> dummyPreparedStatement = new DummyPreparedStatement<T>(expectedType);
@ -145,15 +144,20 @@ public class TestBooleanRepresentation extends TestCase {
}
}
public static class DummyTestBooleanRepresentation implements BooleanRepresentation {
public static class DummyTestBooleanRepresentation implements BooleanRepresentation<String> {
@Override
public void setBoolean(PreparedStatement stmnt, int columnIndex, boolean val) throws SQLException {
stmnt.setString(columnIndex, val ? "somehowtrue" : "somehowfalse");
stmnt.setString(columnIndex, getRepresentation(val));
}
@Override
public boolean getBoolean(ResultSet rs, int columnIndex) throws SQLException {
return "somehowtrue".equals(rs.getString(columnIndex));
}
@Override
public String getRepresentation(boolean bool) {
return bool ? "somehowtrue" : "somehowfalse";
}
}
}

View File

@ -1142,10 +1142,6 @@ 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>
@ -1166,10 +1162,6 @@ Note that you additionally might need to define the <literal>BooleanTypeName</li
<literal>BitTypeName</literal> settings to fit your selected BooleanRepresenation.
</para>
</listitem>
<!-- MSX TODO END DOCUMENT -->
<listitem id="DBDictionary.BooleanTypeName">
<para>
<indexterm>