-
Notifications
You must be signed in to change notification settings - Fork 30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added classes Connect.Call and StoredProcedureOutcome #53
Changes from 3 commits
3c0b50f
933e054
fecf961
3daf9cf
48aad25
7b2e751
bda699c
06b31f1
fd88534
5586e23
bc49e7d
d50b286
a4362ef
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,3 +2,8 @@ target/ | |
.idea/ | ||
*.iml | ||
.DS_Store | ||
/bin/ | ||
.classpath | ||
.project | ||
.settings/ | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @amihaiemil unnecessary newline |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -106,6 +106,7 @@ | |
* @author Yegor Bugayenko ([email protected]) | ||
* @version $Id$ | ||
* @since 0.1.8 | ||
* @checkstyle ClassDataAbstractionCouplingCheck (500 lines) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @amihaiemil along with that we should add puzzle to break down this class, as it became too big |
||
*/ | ||
@ToString | ||
@EqualsAndHashCode(of = { "source", "connection", "args", "auto", "query" }) | ||
|
@@ -308,6 +309,25 @@ public <T> T update(final Outcome<T> outcome) | |
); | ||
} | ||
|
||
/** | ||
* Call an SQL stored procedure. | ||
* | ||
* <p>JDBC connection is opened and, optionally, closed by this method. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @amihaiemil does this method really close connection? where? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mkordas the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @amihaiemil OK, let it be. But I still does not get There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mkordas Yes. In There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @amihaiemil right, so can you put the above information to Javadoc? It's more accurate than saying |
||
* | ||
* @param <T> Type of result expected | ||
* @param outcome Outcome of the operation | ||
* @return This object | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @amihaiemil what does it mean? It's not just |
||
* @throws SQLException If fails | ||
*/ | ||
public <T> T call(final Outcome<T> outcome) | ||
throws SQLException { | ||
return this.run( | ||
outcome, | ||
new Connect.Call(this.query), | ||
Request.EXECUTE_UPDATE | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @amihaiemil can we join these lines? |
||
); | ||
} | ||
|
||
/** | ||
* Make SQL request expecting no response from the server. | ||
* | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
/** | ||
* Copyright (c) 2012-2015, jcabi.com | ||
* All rights reserved. | ||
* | ||
* Redistribution and use in source and binary forms, with or without | ||
* modification, are permitted provided that the following conditions | ||
* are met: 1) Redistributions of source code must retain the above | ||
* copyright notice, this list of conditions and the following | ||
* disclaimer. 2) Redistributions in binary form must reproduce the above | ||
* copyright notice, this list of conditions and the following | ||
* disclaimer in the documentation and/or other materials provided | ||
* with the distribution. 3) Neither the name of the jcabi.com nor | ||
* the names of its contributors may be used to endorse or promote | ||
* products derived from this software without specific prior written | ||
* permission. | ||
* | ||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT | ||
* NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND | ||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL | ||
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, | ||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | ||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED | ||
* OF THE POSSIBILITY OF SUCH DAMAGE. | ||
*/ | ||
package com.jcabi.jdbc; | ||
|
||
import com.jcabi.aspects.Immutable; | ||
import java.sql.CallableStatement; | ||
import java.sql.ResultSet; | ||
import java.sql.SQLException; | ||
import java.sql.Statement; | ||
import lombok.EqualsAndHashCode; | ||
import lombok.ToString; | ||
|
||
/** | ||
* Outcome of a stored procedure with OUT parameters. | ||
* @author Mihai Andronache ([email protected]) | ||
* @version $Id$ | ||
* @since 0.7 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @amihaiemil should be |
||
* @param <T> Type of items | ||
*/ | ||
@Immutable | ||
@ToString | ||
@EqualsAndHashCode(of = { "outpidx" }) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @amihaiemil do we need these curly braces here? |
||
public final class StoredProcedureOutcome<T> implements Outcome<T> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @amihaiemil can we have a test for this class? Other There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mkordas is it ok of I add a todo?? Please make up ur mind about this in the beginning next time. U focus on indentation first and then u stretch my work for days... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @amihaiemil for me to-do is OK, however pdd is rather about making test first and puzzling implementation - otherwise we allow untested code for some time There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mkordas I tried creating a unit test, but it does not work. From what I read on SO and the official site, H2 database does not support stored procedures per se, only java functions declared as aliases: http://www.h2database.com/html/features.html#user_defined_functions I will leave this as it is since it's already well tested in the IT; is that ok with u? The effort of finding another mock DB only for this is simply not worth it IMO. |
||
|
||
/** | ||
* Indexes of the OUT params. | ||
*/ | ||
@Immutable.Array | ||
private final transient int[] outpidx; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @amihaiemil why it is called There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mkordas it's short for "output parameter's indexes". It says in the javadoc :D There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @amihaiemil gosh, so cryptic :) I'd prefer just |
||
|
||
/** | ||
* Ctor. | ||
* @param nrop Number of OUT params. Has to be > 0. | ||
* If this ctor is used, it is assumed that the OUT parameters | ||
* are from index 1 to including nrop. | ||
*/ | ||
public StoredProcedureOutcome(final int nrop) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @amihaiemil do you know http://www.yegor256.com/2015/01/12/compound-name-is-code-smell.html article? |
||
if (nrop <= 0) { | ||
throw new IllegalArgumentException( | ||
"Nr of out params has to be a positive int!" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @amihaiemil for debugging usually it's good idea to print what was the provided argument (more context) |
||
); | ||
} | ||
this.outpidx = new int[nrop]; | ||
for (int idx = 0; idx < nrop; ++idx) { | ||
this.outpidx[idx] = idx + 1; | ||
} | ||
} | ||
|
||
/** | ||
* Ctor. | ||
* @param opidx Indexes of the OUT params. | ||
* <b>Index count starts from 1</b>. | ||
*/ | ||
public StoredProcedureOutcome(final int... opidx) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @amihaiemil here we have cryptic param name as well |
||
if (opidx == null || opidx.length == 0) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @amihaiemil do we really need to check for null here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mkordas I say yes, to avoid NPE; I need to check the length since it's mandatory to have at least one output parameter. What do you think? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @amihaiemil in all projects we try to eliminate |
||
throw new IllegalArgumentException( | ||
"At least 1 OUT param's index needs to be specified!" | ||
); | ||
} | ||
final int size = opidx.length; | ||
this.outpidx = new int[size]; | ||
for (int idx = 0; idx < size; ++idx) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @amihaiemil this looks like code duplication with the above one There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mkordas I see... it really is, and it cannot be removed. Usually we call one ctor from another one using I removed the first ctor and updated the PR description accordingly. It was in fact a convenience ctor and I figured it's not worth the duplication. |
||
this.outpidx[idx] = opidx[idx]; | ||
} | ||
} | ||
|
||
@Override | ||
@SuppressWarnings("unchecked") | ||
public T handle( | ||
final ResultSet rset, final Statement stmt | ||
) throws SQLException { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @amihaiemil paired brackets notation does not apply to method declarations |
||
final int nrop = this.outpidx.length; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @amihaiemil what is |
||
final Object[] outs = new Object[nrop]; | ||
if (stmt instanceof CallableStatement) { | ||
for (int idx = 0; idx < nrop; ++idx) { | ||
outs[idx] = ((CallableStatement) stmt).getObject( | ||
this.outpidx[idx] | ||
); | ||
} | ||
} | ||
return (T) outs; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @amihaiemil is this always safe casting? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mkordas Right - I see in other Outcomes they solved the issue by requesting from the user the In my case I would let it fail and say in the Javadoc that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @amihaiemil maybe it's silly question, but why There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mkordas It doesn't get any better than this - I tried every combination of T and |
||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,7 +31,13 @@ | |
|
||
import com.jcabi.aspects.Tv; | ||
import com.jolbox.bonecp.BoneCPDataSource; | ||
import java.sql.CallableStatement; | ||
import java.sql.PreparedStatement; | ||
import java.sql.SQLException; | ||
import java.sql.Types; | ||
import javax.sql.DataSource; | ||
import org.hamcrest.MatcherAssert; | ||
import org.hamcrest.Matchers; | ||
import org.junit.Assume; | ||
import org.junit.Before; | ||
import org.junit.Test; | ||
|
@@ -40,6 +46,7 @@ | |
* Integration case for {@link JdbcSession}. | ||
* @author Yegor Bugayenko ([email protected]) | ||
* @version $Id$ | ||
* @checkstyle StringLiteralsConcatenationCheck (300 lines) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @amihaiemil we shouldn't be suppressing this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mkordas there's no other way to do it: if I concatenate with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mkordas And I wouldn't read it from a file just for this. Would you? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
*/ | ||
public final class JdbcSessionITCase { | ||
|
||
|
@@ -90,6 +97,82 @@ public void changesTransactionIsolationLevel() throws Exception { | |
new JdbcSession(source).sql("VACUUM").execute(); | ||
} | ||
|
||
/** | ||
* JdbcSession can run a function (stored procedure) with | ||
* output parameters. | ||
* @throws Exception If something goes wrong | ||
*/ | ||
@Test | ||
public void callsFunctionWithOutParam() throws Exception { | ||
final DataSource dsrc = JdbcSessionITCase.source(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @amihaiemil can we name it just |
||
new JdbcSession(dsrc).autocommit(false).sql( | ||
"CREATE TABLE IF NOT EXISTS users (name VARCHAR(50))" | ||
).execute().sql("INSERT INTO users (name) VALUES (?)") | ||
.set("Jeff Charles").execute().sql( | ||
"CREATE OR REPLACE FUNCTION getUser(username OUT text)" | ||
+ " AS $$ BEGIN SELECT name INTO username from users; END;" | ||
+ " $$ LANGUAGE plpgsql;" | ||
).execute().commit(); | ||
final Object[] result = new JdbcSession(dsrc) | ||
.sql("{call getUser(?)}") | ||
.prepare( | ||
new Preparation() { | ||
@Override | ||
public void prepare( | ||
final PreparedStatement stmt | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @amihaiemil paired brackets notation does not apply here as well |
||
) throws SQLException { | ||
((CallableStatement) stmt) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @amihaiemil can we somehow avoid this cast here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mkordas Not at the moment, since the class type tree is as follows: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mkordas In fact, I don't even know how to get rid of it; make a redesign and change stuff, add flow just for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @amihaiemil OK, let it be |
||
.registerOutParameter(1, Types.VARCHAR); | ||
} | ||
} | ||
) | ||
.call(new StoredProcedureOutcome<Object[]>(1)); | ||
MatcherAssert.assertThat(result.length, Matchers.is(1)); | ||
MatcherAssert.assertThat( | ||
result[0].toString(), | ||
Matchers.containsString("Charles") | ||
); | ||
} | ||
|
||
/** | ||
* JdbcSession can run a function (stored procedure) with | ||
* input and output parameters. | ||
* @throws Exception If something goes wrong | ||
*/ | ||
@Test | ||
public void callsFunctionWithInOutParam() throws Exception { | ||
final DataSource dsrc = JdbcSessionITCase.source(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @amihaiemil same here, just |
||
new JdbcSession(dsrc).autocommit(false).sql( | ||
"CREATE TABLE IF NOT EXISTS usersid (id INTEGER, name VARCHAR(50))" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @amihaiemil what is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mkordas no, I meant There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @amihaiemil so why not |
||
).execute().sql("INSERT INTO usersid (id, name) VALUES (?, ?)") | ||
.set(1).set("Marco Polo").execute().sql( | ||
"CREATE OR REPLACE FUNCTION getUserById(uid IN INTEGER," | ||
+ " usrnm OUT text) AS $$ BEGIN" | ||
+ " SELECT name INTO usrnm FROM usersid WHERE id=uid;" | ||
+ " END; $$ LANGUAGE plpgsql;" | ||
).execute().commit(); | ||
final Object[] result = new JdbcSession(dsrc) | ||
.sql("{call getUserById(?, ?)}") | ||
.set(1) | ||
.prepare( | ||
new Preparation() { | ||
@Override | ||
public void prepare( | ||
final PreparedStatement stmt | ||
) throws SQLException { | ||
((CallableStatement) stmt) | ||
.registerOutParameter(2, Types.VARCHAR); | ||
} | ||
} | ||
) | ||
.call(new StoredProcedureOutcome<Object[]>(new int[] {2})); | ||
MatcherAssert.assertThat(result.length, Matchers.is(1)); | ||
MatcherAssert.assertThat( | ||
result[0].toString(), | ||
Matchers.containsString("Polo") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @amihaiemil why we check There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @mkordas because then it complains of douvle literals and it is safe enough to have a contains, rahter than declare a variable for it... all the tests in all the jcabi libs are like this. Contains is used instead of equals wjere appropriate There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @amihaiemil IMO it breaks test data coupling rule, but let's follow convention then |
||
); | ||
} | ||
|
||
/** | ||
* Get data source. | ||
* @return Source | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@amihaiemil do we need this first slash here?