Permalink
Browse files

[core] Fallback for untyped null parameter, configured with connectio…

…n property:

```java
import java.util.Properties;

Properties props = new Properties();
props.put("acolyte.parameter.untypedNull", "true"); // default: false

DriverManager.getConnection(jdbcUrl, props);
```
  • Loading branch information...
cchantep
cchantep committed Dec 4, 2013
1 parent b93d185 commit 3b7d9151ab1d09e994f3fbbcfd7bf0610343d4f4
@@ -91,7 +91,7 @@
/**
* Owner connection
*/
- protected final Connection connection;
+ protected final acolyte.Connection connection;
// --- Constructors ---
@@ -109,7 +109,7 @@ protected AbstractStatement() {
*
* @param handler Statement handler (not null)
*/
- protected AbstractStatement(final Connection connection,
+ protected AbstractStatement(final acolyte.Connection connection,
final StatementHandler handler) {
if (connection == null) {
@@ -59,7 +59,7 @@
* @param sql SQL statement
* @param handler Statement handler (not null)
*/
- protected CallableStatement(final Connection connection,
+ protected CallableStatement(final acolyte.Connection connection,
final String sql,
final StatementHandler handler) {
@@ -107,7 +107,7 @@
* Bulk constructor.
*
* @param url JDBC URL
- * @param props JDBC properties
+ * @param props JDBC properties (immutable)
* @param handler Acolyte handler
* @see Driver#connect
*/
@@ -126,8 +126,12 @@ public Connection(final String url,
// ---
this.url = url;
- this.props = props;
+ this.props = new Properties();
this.handler = handler;
+
+ if (props != null) {
+ this.props.putAll(props);
+ } // end of if
} // end of <init>
// --- Connection impl ---
@@ -751,6 +755,13 @@ public boolean isWrapperFor(final Class<?> iface) throws SQLException {
// ---
+ /**
+ * Returns connection properties.
+ */
+ public Properties getProperties() {
+ return this.props;
+ } // end of getProperties
+
/**
* Throws a SQLException("Connection is closed") if connection is closed.
*/
@@ -51,7 +51,7 @@
* @throws IllegalArgumentException if |info| doesn't contain handler
* (ConnectionHandler) for property "connection.handler".
*/
- public Connection connect(final String url, final Properties info)
+ public acolyte.Connection connect(final String url, final Properties info)
throws SQLException {
if (!acceptsURL(url)) {
@@ -134,7 +134,21 @@ public Logger getParentLogger() throws SQLFeatureNotSupportedException {
*
* @throws IllegalArgumentException if handler is null
*/
- public static Connection connection(final ConnectionHandler handler) {
+ public static acolyte.Connection connection(ConnectionHandler handler) {
+ return connection(handler, null);
+ } // end of connection
+
+ /**
+ * Direct connection, with given |handler| and random URL.
+ *
+ * @param handler Connection handler
+ * @param info Connection properties (optional)
+ * @throws IllegalArgumentException if handler is null
+ * @see #connection(acolyte.ConnectionHandler)
+ */
+ public static acolyte.Connection connection(final ConnectionHandler handler,
+ final Properties info) {
+
if (handler == null) {
throw new IllegalArgumentException();
} // end of if
@@ -143,26 +157,33 @@ public static Connection connection(final ConnectionHandler handler) {
format("jdbc:acolyte:direct-%d",
System.identityHashCode(handler));
- return new acolyte.Connection(url, null, handler);
+ return new acolyte.Connection(url, info, handler);
} // end of connection
/**
* Direct connection, with given |handler| and random URL.
*
* @throws IllegalArgumentException if handler is null
*/
- public static Connection connection(final StatementHandler handler) {
+ public static acolyte.Connection connection(StatementHandler handler) {
+ return connection(handler, null);
+ } // end of connection
+
+ /**
+ * Direct connection, with given |handler| and random URL.
+ *
+ * @param handler Statement handler
+ * @param info Connection properties (optional)
+ * @throws IllegalArgumentException if handler is null
+ */
+ public static acolyte.Connection connection(final StatementHandler handler,
+ final Properties info) {
+
if (handler == null) {
throw new IllegalArgumentException();
} // end of if
- final String url = String.
- format("jdbc:acolyte:direct-%d",
- System.identityHashCode(handler));
-
- final ConnectionHandler ch = new ConnectionHandler.Default(handler);
-
- return new acolyte.Connection(url, null, ch);
+ return connection(new ConnectionHandler.Default(handler), info);
} // end of connection
// ---
@@ -115,7 +115,7 @@
* @param sql SQL statement
* @param handler Statement handler (not null)
*/
- protected PreparedStatement(final Connection connection,
+ protected PreparedStatement(final acolyte.Connection connection,
final String sql,
final StatementHandler handler) {
@@ -389,6 +389,15 @@ public void setObject(final int parameterIndex,
final Object x) throws SQLException {
if (x == null) {
+ if ("true".equals(connection.getProperties().
+ get("acolyte.parameter.untypedNull"))) {
+
+ // Fallback to String-VARCHAR
+ setObject(parameterIndex, null, Types.VARCHAR);
+
+ return;
+ } // end of if
+
throw new SQLException("Cannot set parameter from null object");
} // end of if
@@ -299,7 +299,7 @@ object CallableStatementSpec
statement().wasNull aka "check" must throwA[SQLException]("No result")
}
- "fail is statement is closed" in {
+ "fail if statement is closed" in {
val stmt = statement()
stmt.close()
@@ -59,6 +59,20 @@ object ConnectionSpec extends Specification with ConnectionFixtures {
}
}
+
+ "set immutable properties on new connection" in {
+ val props = new java.util.Properties()
+ props.put("_test", "_1")
+
+ val con =
+ connection(url = jdbcUrl, props = props, handler = defaultHandler)
+
+ (con.getProperties aka "properties" mustEqual props).
+ and {
+ props.put("_test", "_2")
+ con.getProperties.get("_test") aka "property" mustEqual "_1"
+ }
+ }
}
"Type-map setter" should {
@@ -56,14 +56,34 @@ object DriverSpec extends Specification with DriverUtils with DriverFixtures {
}
+ "accept connection properties" in {
+ val props = new java.util.Properties()
+ props.put("_test", "_val")
+
+ acolyte.Driver.register(handlerId, defaultHandler)
+
+ (new acolyte.Driver().connect(jdbcUrl, props).getProperties.
+ aka("connection 1 properties") mustEqual props).
+ and(acolyte.Driver.connection(defaultHandler, props).
+ getProperties aka "connection 2 properties" mustEqual props).
+ and(acolyte.Driver.connection(CompositeHandler.empty, props).
+ getProperties aka "connection 3 properties" mustEqual props)
+ }
+
"not open connection without handler" in {
(directConnect("jdbc:acolyte:test").
aka("connection") must throwA[IllegalArgumentException](
message = "Invalid handler ID: null")).
- and(acolyte.Driver.connection(null.asInstanceOf[ConnectionHandler]).
- aka("direct connection 1") must throwA[IllegalArgumentException]).
- and(acolyte.Driver.connection(null.asInstanceOf[StatementHandler]).
- aka("direct connection 1") must throwA[IllegalArgumentException])
+ and(acolyte.Driver.connection(null.asInstanceOf[ConnectionHandler]).
+ aka("direct connection 1") must throwA[IllegalArgumentException]).
+ and(acolyte.Driver.connection(null.asInstanceOf[StatementHandler]).
+ aka("direct connection 2") must throwA[IllegalArgumentException]).
+ and(acolyte.Driver.
+ connection(null.asInstanceOf[ConnectionHandler], null).
+ aka("direct connection 3") must throwA[IllegalArgumentException]).
+ and(acolyte.Driver.
+ connection(null.asInstanceOf[StatementHandler], null).
+ aka("direct connection 4") must throwA[IllegalArgumentException])
}
@@ -160,6 +160,20 @@ trait StatementSpecification[S <: PreparedStatement] extends Setters {
}
+ "fallback null object to null string parameter" in {
+ val ps = new java.util.Properties()
+ ps.put("acolyte.parameter.untypedNull", "true")
+
+ val s = statement(connection(ps))
+ s.setObject(1, null)
+
+ lazy val m = s.getParameterMetaData
+
+ (m.getParameterCount aka "count" mustEqual 1).
+ and(m.getParameterType(1) aka "SQL type" mustEqual Types.VARCHAR)
+
+ }
+
"be NULL as SQL" in {
(executeUpdate("TEST ?, y", Types.VARCHAR, null).
aka("SQL update") mustEqual ("TEST ?, y" -> null)).
@@ -1271,7 +1285,10 @@ trait StatementSpecification[S <: PreparedStatement] extends Setters {
def statement(c: Connection = defaultCon, s: String = "TEST", h: StatementHandler = defaultHandler.getStatementHandler): S
- lazy val defaultCon = new acolyte.Connection(jdbcUrl, null, defaultHandler)
+ def connection(ps: java.util.Properties) =
+ new acolyte.Connection(jdbcUrl, ps, defaultHandler)
+
+ lazy val defaultCon = connection(null)
lazy val defaultHandler = EmptyConnectionHandler
}
View
@@ -133,9 +133,22 @@ If you just need/want to directly get connection from `acolyte.Driver`, without
```java
// handler: acolyte.ConnectionHandler or acolyte.StatementHandler instance
-Connection con = new acolyte.Driver().connection(handler);
+Connection con = acolyte.Driver.connection(handler);
```
+### Connection properties
+
+JDBC allows to pass properties to driver to customize connection creation:
+
+```java
+Connection con = DriverManager.getConnection(jdbcUrl, someJavaUtilProps);
+Connection con = acolyte.Driver.connection(handler, someJavaUtilProps);
+```
+
+Acolyte specific properties are:
+
+- `acolyte.parameter.untypedNull`: If `"true"`, Acolyte fallbacks untyped null from `statement.setObject(p, null)` to null string (default: false).
+
#### Query result creation
Acolyte provides [Row](http://cchantep.github.io/acolyte/apidocs/acolyte/Row.html) and [RowList](http://cchantep.github.io/acolyte/apidocs/acolyte/RowList.html) classes (and their sub-classes) to allow easy and typesafe creation of result.
View
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="ISO-8859-1"?>
-<project name="acolyte">
- <body>
- <links>
- <item name="GitHub project" href="http://github.com/cchantep/acolyte" />
- </links>
-
- <menu name="User guide">
- <item name="Java" href="./index.html#Java" />
- <item name="Scala" href="./index.html#Scala" />
- <item name="Playframework" href="./index.html#Playframework" />
- </menu>
-
- <menu name="Reference">
- <item name="Javadoc" href="./apidocs/index.html" />
- </menu>
-
- <menu ref="reports" />
- </body>
-
- <skin>
- <groupId>org.apache.maven.skins</groupId>
- <artifactId>maven-fluido-skin</artifactId>
- <version>1.3.0</version>
- </skin>
-</project>

0 comments on commit 3b7d915

Please sign in to comment.