how to run stored procedure from groovy that returns multiple resultsets - groovy

I couldnt find any good example of doing this online.
Can someone please show how to run a stored procedure (that returns multiple resultsets) from groovy?
Basically I am just trying to determine how many resultsets the stored procedure returns..

I have written a helper which allows me to work with stored procedures that return a single ResultSet in a way that is similar to working with queries with groovy.sql.Sql. This could easily be adapted to process multiple ResultSets (I assume each would need it's own closure).
Usage:
Sql sql = Sql.newInstance(dataSource)
SqlHelper helper = new SqlHelper(sql);
helper.eachSprocRow('EXEC sp_my_sproc ?, ?, ?', ['a', 'b', 'c']) { row ->
println "foo=${row.foo}, bar=${row.bar}, baz=${row.baz}"
}
Code:
class SqlHelper {
private Sql sql;
SqlHelper(Sql sql) {
this.sql = sql;
}
public void eachSprocRow(String query, List parameters, Closure closure) {
sql.cacheConnection { Connection con ->
CallableStatement proc = con.prepareCall(query)
try {
parameters.eachWithIndex { param, i ->
proc.setObject(i+1, param)
}
boolean result = proc.execute()
boolean found = false
while (!found) {
if (result) {
ResultSet rs = proc.getResultSet()
ResultSetMetaData md = rs.getMetaData()
int columnCount = md.getColumnCount()
while (rs.next()) {
// use case insensitive map
Map row = new TreeMap(String.CASE_INSENSITIVE_ORDER)
for (int i = 0; i < columnCount; ++ i) {
row[md.getColumnName(i+1)] = rs.getObject(i+1)
}
closure.call(row)
}
found = true;
} else if (proc.getUpdateCount() < 0) {
throw new RuntimeException("Sproc ${query} did not return a result set")
}
result = proc.getMoreResults()
}
} finally {
proc.close()
}
}
}
}

All Java classes are usable from Groovy. If Groovy does not give you a way to do it, then you can do it Java-way using JDBC callable statements.

I just stumbled across what could possibly be a solution to your problem, if an example was what you were after, have a look at the reply to this thread

Related

is it possible to connect to java jOOQ DB?

I discovered a new interesting service and I'm trying to understand how it works. Please explain how to connect to my jOOQ database from another program?
MockDataProvider provider = new MyProvider();
MockConnection connection = new MockConnection(provider);
DSLContext create = DSL.using(connection, SQLDialect.H2);
Field<Integer> id = field(name("BOOK", "ID"), SQLDataType.INTEGER);
Field<String> book = field(name("BOOK", "NAME"), SQLDataType.VARCHAR);
So, I create but can I connect to it?
Here I have added your code, Lukas.
try (Statement s = connection.createStatement();
ResultSet rs = s.executeQuery("SELECT ...")
) {
while (rs.next())
System.out.println(rs.getString(1));
}
This example was found here
https://www.jooq.org/doc/3.7/manual/tools/jdbc-mocking/
public class MyProvider implements MockDataProvider {
#Override
public MockResult[] execute(MockExecuteContext ctx) throws SQLException {
// You might need a DSLContext to create org.jooq.Result and org.jooq.Record objects
//DSLContext create = DSL.using(SQLDialect.ORACLE);
DSLContext create = DSL.using(SQLDialect.H2);
MockResult[] mock = new MockResult[1];
// The execute context contains SQL string(s), bind values, and other meta-data
String sql = ctx.sql();
// Dynamic field creation
Field<Integer> id = field(name("AUTHOR", "ID"), SQLDataType.INTEGER);
Field<String> lastName = field(name("AUTHOR", "LAST_NAME"), SQLDataType.VARCHAR);
// Exceptions are propagated through the JDBC and jOOQ APIs
if (sql.toUpperCase().startsWith("DROP")) {
throw new SQLException("Statement not supported: " + sql);
}
// You decide, whether any given statement returns results, and how many
else if (sql.toUpperCase().startsWith("SELECT")) {
// Always return one record
Result<Record2<Integer, String>> result = create.newResult(id, lastName);
result.add(create
.newRecord(id, lastName)
.values(1, "Orwell"));
mock[0] = new MockResult(1, result);
}
// You can detect batch statements easily
else if (ctx.batch()) {
// [...]
}
return mock;
}
}
I'm not sure what lines 3-5 of your example are supposed to do, but if you implement your MockDataProvider and put that into a MockConnection, you just use that like any other JDBC connection, e.g.
try (Statement s = connection.createStatement();
ResultSet rs = s.executeQuery("SELECT ...")
) {
while (rs.next())
System.out.println(rs.getString(1));
}

JOOQ DSL using SqlConnection

I am using AsyncSQLClient to get an async. connection to my database in vertx. Now i am struggling how to use JOOQs DSL. I am trying the following:
client.getConnection(res -> {
if (res.succeeded()) {
SQLConnection connection = res.result();
DSL dsl = DSL.using(connection, SQLDialect.POSTGRES_9_4);
connection.close();
client.close();
} else {
}
});
That is not working because using needs a Connection and not an SQLConnection. Is there any way to use an SQLConnection with JOOQ? Is there any other way to create an async connection for JOOQ?
No you can't use JOOQ with the Vert.x AsyncSQL client.
But someone in the Vert.x community has created a jOOQ CodeGenerator to create vertxified DAOs and POJOs
You could implement (and open source!) a "proxy" for the vert.x SQLConnection type. For instance, if you want to run the following vert.x method:
interface SQLConnection {
SQLConnection queryWithParams(
String sql,
JsonArray params,
Handler<AsyncResult<ResultSet>> resultHandler
);
}
Your proxy would expose a method like this instead:
class jOOQSQLConnection {
final SQLConnection delegate;
<R extends Record> jOOQSQLConnection query(
ResultQuery<R> sql,
Handler<AsyncResult<Result<R>>> resultHandler
) {
wrapInjOOQSQLConnection(
// Extract the SQL string from the jOOQ query
delegate.query(sql.getSQL()),
// Extract the bind variables from the jOOQ query and wrap them in the
// JsonArray type, as requested by vert.x
wrapjOOQParamsInJsonArray(sql.getBindValues()),
// This is a handler that receives a vert.x ResultSet and wraps / transforms
// it into a jOOQ result (which contains the <R> type for type safety)
r -> resultHandler.handle(wrapInjOOQResult(r.result()))
);
}
}
The above probably won't work out of the box, but it gives you an idea of how to wrap things.
using (SqlConnection con = connection.getconnection()){
SqlCommand cmd = new SqlCommand("empreg", con);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#empid", txtempid.Text);
cmd.Parameters.AddWithValue("#empname", txtempname.Text);
cmd.Parameters.AddWithValue("#salary", txtsalary.Text);
cmd.Parameters.AddWithValue("#tell", txttell.Text);
cmd.Parameters.AddWithValue("#address", txtaddress.Text);
cmd.Parameters.AddWithValue("#blog", txtblog.Text);
cmd.Parameters.AddWithValue("#gender", cmbgender.Text);
cmd.Parameters.AddWithValue("#hiredate", dtpdate.Value);
cmd.Parameters.AddWithValue("#op", op);
int i = cmd.ExecuteNonQuery();
//If i > 0 AND op = "insert"
if (i > 0 && op == "insert"){
MessageBox.Show("1 row is saved sucessfuly ");
submode.readdgv("emp", DGV2);
submode.autoid(txtempid, "emp");
txtempname.Clear();
txtsalary.Clear();
txttell.Clear();
txtaddress.Clear();
txtblog.Clear();
cmbgender.SelectedIndex = -1;
dtpdate.Value = DateTime.Now;
txtempname.Focus();
}
else if (i >= 0 && op == "update"){
MessageBox.Show("1 row is updated sucessfuly ");
submode.readdgv("emp", DGV2);
submode.autoid(txtempid, "emp");
}
else if (i >= 0 && op == "delete"){
MessageBox.Show("1 row is deleted sucessfuly");
submode.readdgv("emp", DGV2);
submode.autoid(txtempid, "emp");
}
else{
MessageBox.Show("process is failed");
submode.readdgv("emp", DGV2);
submode.autoid(txtempid, "emp");
}
}

how to get values from SqlDataReader to forloop execution?

Here I coding for get each and every StudyUID(as string) from database to SqlDataReader,but i need to know how the reader value call to forloop execution.
Get to read each and every StudyUID for execution.Here is the code :.
public void automaticreport()
{
//string autsdyid="";
SqlConnection con = new SqlConnection(constr);
con.Open();
string autoquery = "Select StudyUID From StudyTable Where status='2'";
SqlCommand cmd = new SqlCommand(autoquery, con);
SqlDataReader rdr = cmd.ExecuteReader();
for()
{
//how to call each StudyUId from database through for loop
if (!this.reportchk)
{
Reportnew cf = new Reportnew();
ThreadPool.QueueUserWorkItem((WaitCallback)(o => cf.ReportRetrive(this, autsdyid, true)));
}
else
{
int num = (int)System.Windows.Forms.MessageBox.Show("Reports checking in progress, Please wait sometime and try again later", "OPTICS", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
}
con.Close();
}
Like #R.T and others have mentioned you can use the Read method on the data reader. Looking at your sample code you might want to refactor it slightly to meet more of the SOLID principles and make sure you're not leaking database connections
Here's an example of code that has been refactored a bit.
public void automaticreport()
{
foreach (var autsdyid in LoadStudyIdentifiers())
{
if (!this.reportchk)
{
Reportnew cf = new Reportnew();
ThreadPool.QueueUserWorkItem((WaitCallback)(o => cf.ReportRetrive(this, autsdyid, true)));
}
else
{
int num = (int)System.Windows.Forms.MessageBox.Show("Reports checking in progress, Please wait sometime and try again later", "OPTICS", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
}
}
}
private string[] LoadStudyIdentifiers()
{
var results = new List<string>();
// adding a using statement will close the database connection if there are any errors
// avoiding consuming the database connection pool
using (var con = new SqlConnection(constr))
{
conn.Open();
var autoquery = "Select StudyUID From StudyTable Where status='2'";
using (var cmd = new SqlCommand(autoquery, con))
{
SqlDataReader rdr = cmd.ExecuteReader();
while(rdr.Read())
{
results.Add(rdr.GetString(rdr.GetOrdinal("StudyUID")));
}
}
}
return results.ToArray();
}
Note: I wrote this in notepad so there is no guarantee it will compile but should give an indication as to how you could refactor your code.
if (rdr.HasRows)
{
while (rdr.Read())
{
Console.WriteLine(rdr.getString("columnName"));
}
}
You can use something like:
while (reader.Read())
{
string value = reader.getString("columnName");
}
You may use the while loop like this:
while (rdr.Read())
{
string s = rdr.GetString(rdr.GetOrdinal("Column"));
//Apply logic to retrieve here
}

Anonymous type and getting values out side of method scope

I am building an asp.net site in .net framework 4.0, and I am stuck at the method that supposed to call a .cs class and get the query result back here is my method call and method
1: method call form aspx.cs page:
helper cls = new helper();
var query = cls.GetQuery(GroupID,emailCap);
2: Method in helper class:
public IQueryable<VariablesForIQueryble> GetQuery(int incomingGroupID, int incomingEmailCap)
{
var ctx = new some connection_Connection();
ObjectSet<Members1> members = ctx.Members11;
ObjectSet<groupMember> groupMembers = ctx.groupMembers;
var query = from m in members
join gm in groupMembers on m.MemberID equals gm.MemID
where (gm.groupID == incomingGroupID) && (m.EmailCap == incomingEmailCap)
select new VariablesForIQueryble(m.MemberID, m.MemberFirst, m.MemberLast, m.MemberEmail, m.ValidEmail, m.EmailCap);
//select new {m.MemberID, m.MemberFirst, m.MemberLast, m.MemberEmail, m.ValidEmail, m.EmailCap};
return query ;
}
I tried the above code with IEnumerable too without any luck. This is the code for class VariablesForIQueryble:
3:Class it self for taking anonymouse type and cast it to proper types:
public class VariablesForIQueryble
{
private int _emailCap;
public int EmailCap
{
get { return _emailCap; }
set { _emailCap = value; }
}`....................................
4: and a constructor:
public VariablesForIQueryble(int memberID, string memberFirst, string memberLast, string memberEmail, int? validEmail, int? emailCap)
{
this.EmailCap = (int) emailCap;
.........................
}
I can't seem to get the query result back, first it told me anonymous type problem, I made a class after reading this: link text; and now it tells me constructors with parameters not supported. Now I am an intermediate developer, is there an easy solution to this or do I have to take my query back to the .aspx.cs page.
If you want to project to a specific type .NET type like this you will need to force the query to actually happen using either .AsEnumerable() or .ToList() and then use .Select() against linq to objects.
You could leave your original anonymous type in to specify what you want back from the database, then call .ToList() on it and then .Select(...) to reproject.
You can also clean up your code somewhat by using an Entity Association between Groups and Members using a FK association in the database. Then the query becomes a much simpler:
var result = ctx.Members11.Include("Group").Where(m => m.Group.groupID == incomingGroupID && m.EmailCap == incomingEmailCap);
You still have the issue of having to do a select to specify which columns to return and then calling .ToList() to force execution before reprojecting to your new type.
Another alternative is to create a view in your database and import that as an Entity into the Entity Designer.
Used reflection to solve the problem:
A: Query, not using custom made "VariablesForIQueryble" class any more:
//Method in helper class
public IEnumerable GetQuery(int incomingGroupID, int incomingEmailCap)
{
var ctx = new some_Connection();
ObjectSet<Members1> members = ctx.Members11;
ObjectSet<groupMember> groupMembers = ctx.groupMembers;
var query = from m in members
join gm in groupMembers on m.MemberID equals gm.MemID
where ((gm.groupID == incomingGroupID) && (m.EmailCap == incomingEmailCap)) //select m;
select new { m.MemberID, m.MemberFirst, m.MemberLast, m.MemberEmail, m.ValidEmail, m.EmailCap };
//select new VariablesForIQueryble (m.MemberID, m.MemberFirst, m.MemberLast, m.MemberEmail, m.ValidEmail, m.EmailCap);
//List<object> lst = new List<object>();
//foreach (var i in query)
//{
// lst.Add(i.MemberEmail);
//}
//return lst;
//return query.Select(x => new{x.MemberEmail,x.MemberID,x.ValidEmail,x.MemberFirst,x.MemberLast}).ToList();
return query;
}
B:Code to catch objects and conversion of those objects using reflection
helper cls = new helper();
var query = cls.GetQuery(GroupID,emailCap);
if (query != null)
{
foreach (var objRow in query)
{
System.Type type = objRow.GetType();
int memberId = (int)type.GetProperty("MemberID").GetValue(objRow, null);
string memberEmail = (string)type.GetProperty("MemberEmail").GetValue(objRow, null);
}
else
{
something else....
}

Creating Subsonic Dynamic Query with numerous where clause

I have the following code which is in a base class.
MyApp.MyDB = new MyApp.MyDB ();
IRepository<T> repo = new SubSonicRepository<T>(db);
CurrentTable = repo.GetTable();
var s = db.SelectColumns(columnList.ToArray()).
From(CurrentTable.Name).
OrderAsc(CurrentTable.Descriptor.Name);
The idea is that all my classes can call this method.
I have just realised that I may need to a 'where' statement and there could be numerous columns names and values to test for.
What's the best approach for this?
UPDATE: I found this works below but is it best practice?
//WhereClause is a Dictionary<string, string>
int count = 0;
foreach (var whereitem in WhereClause)
{
if (count == 0)
{
s.Where(whereitem.Key).IsEqualTo(whereitem.Value);
}
else
{
s.And(whereitem.Key).IsEqualTo(whereitem.Value);
}
count++;
}
This simplifies the logic slightly: For the where clause, do something like this:
s.Where(1).IsEqualTo(1);
For all your other whereitem's, you can use And's.

Resources