Building tree like structure for feeding Freemarker from JDBC - jdbc

I want to generate code with Groovy and Freemarker (especially GFreeMarker) - I want to generate a Java wrapper for calling stored procedures. So I query ORACLE with this statement:
select PACKAGE_NAME, OBJECT_NAME, POSITION, ARGUMENT_NAME, DATA_TYPE, IN_OUT
from USER_ARGUMENTS
This data is denormalized (each parameter is on a line) and hierarchical:
- Package (PACKAGE_NAME)
- Function (OBJECT_NAME)
- Arguments (ARGUMENT_NAME, DATA_TYPE, IN_OUT)
I want to store it into an hierarchical structure to feed it to Freemarker to generate code. This means that for every row I have to create a new Package instance or select an existing on, then create a new Function instance or select an existing one and create a new argument.
Can this be done via an ObjectGraphBuilder or should I go into a combination with maps and lists?
How is this done in a "groovy way"? Thanks for your help!

Groovy or not, I did it the following way, please provide better answers and I will accept it!
First I build the following structure:
Map packages = new LinkedHashMap()
class Package {
String name
Map functions = [:]
}
class Function {
String name
String returnType
boolean isFunction = false
Map parameters = [:]
}
class Parameter {
String name
String dataType
String inOut
}
Then I read it with
sql.eachRow("""\
select PACKAGE_NAME, OBJECT_NAME, POSITION, ARGUMENT_NAME, DATA_TYPE, IN_OUT
from USER_ARGUMENTS
order by PACKAGE_NAME, OBJECT_NAME, POSITION""")
{
// create or get package
def thePackage = packages[it.PACKAGE_NAME ?: '']
if (thePackage == null) {
thePackage = new Package(name : it.PACKAGE_NAME ?: '')
packages[it.PACKAGE_NAME ?: ''] = thePackage
}
// create or get function
def theFunction = thePackage.functions[it.OBJECT_NAME ?: '']
if (theFunction == null) {
theFunction = new Function(name: it.OBJECT_NAME ?: '')
thePackage.functions[it.OBJECT_NAME ?: ''] = theFunction
}
// Position 0 is the return value
if (it.POSITION == 0) {
theFunction.isFunction = true
theFunction.returnType = it.DATA_TYPE
}
else {
// create the argument
def theParameter = new Parameter(
name: it.ARGUMENT_NAME, dataType: it.DATA_TYPE, inOut: it.IN_OUT)
theFunction.parameters[it.ARGUMENT_NAME ?: ''] = theParameter
}
}

Related

EclipseLink: call oracle StoredFunc with IN table type

I'm trying to call an oracle stored function using eclipselink 2.6.5, the storedfunction has an oracle type input.
This is te stored func:
create or replace function TEST_FUNC(PAR1 IN VARCHAR2, PAR2 IN MY_TYPE_T)
return varchar2 is
begin
if PAR2 is null then
return (PAR1 || ' 0');
else
return(PAR1 || ' ' || PAR2.count);
end if;
end TEST_FUNC;
This is type declaration:
CREATE OR REPLACE MY_TYPE_T as table of MY_TYPE_R
CREATE OR REPLACE TYPE MY_TYPE_R as object
(
COD_P VARCHAR2(3),
COD_S VARCHAR2(10)
)
This is the type mapping:
#Embeddable
#Struct(name="MY_TYPE_R", fields= {"COD_P", "COD_S"})
#PLSQLTable(
name="MY_TYPE_R",
compatibleType="MY_TYPE_T",
nestedType="MY_TYPE_R"
)
public class MyRecordType {
#Column(name="COD_P")
private String codP;
#Column(name="COD_S")
private String codS;
//costructors, getter and setter...
}
This is the stored mapping:
#NamedPLSQLStoredFunctionQuery(
name="testFunc",
functionName="TEST_FUNC",
returnParameter=#PLSQLParameter(
name="RESULT",
databaseType = "VARCHAR_TYPE"),
parameters = {
#PLSQLParameter(name = "firstParam", queryParameter="PAR1", databaseType = "VARCHAR_TYPE"),
#PLSQLParameter(name = "secondParam", queryParameter="PAR2", databaseType = "MY_TYPE_T"),
}
)
This is the call:
#Test
public void testFuncTest() {
List<MyRecordType> recTypeList = new ArrayList<MyRecordType>();
MyRecordType rec = new MyRecordType();
rec.setCodP("PValue");
rec.setCodS("SValue");
recTypeList.add(rec);
Query query = getEM().createNamedQuery("testFunc");
query.setParameter("firstParam", "FOO");
query.setParameter("secondParam", recTypeList);
Assert.assertEquals("FOO 1", query.getSingleResult());
}
When I execute the test I get:
PLS-00306: wrong number or types of arguments in call to 'TEST_FUNC'
Actually reading the documentation on #PLSQTable I'm a bit confused on the tree parameters:
name
compatibleType
nestedType
Anyone has some suggestion?
First you'll want to use the OracleObject annotation to define your fields into an Oracle object. It's somewhat duplicate to Struct but required anyways.
#OracleObject(
name = "MY_TYPE_R"
, javaType = MyRecordType.class
, fields = {
#PLSQLParameter(name = "COD_P")
,#PLSQLParameter(name = "COD_S")
}
)
For the PLSQLTable you want
#NamedPLSQLStoredFunctionQuery(
name="testFunc",
functionName="TEST_FUNC",
returnParameter=#PLSQLParameter(
name="RESULT",
databaseType = "VARCHAR_TYPE"),
parameters = {
#PLSQLParameter(name = "PAR1", queryParameter="PAR1", databaseType = "VARCHAR_TYPE"),
#PLSQLParameter(name = "PAR2", queryParameter="PAR2", databaseType = "MY_TYPE_T"),
}
)
so the name needs to match what's in your function. The PLSQL table piece looks right, I believe the OracleObject part was your missing component and causing your error.

Linq in C# using IEnumerable

Appearently, I got an error if using the following code. It said:
Cannot implicity converrt type System.Linq.IQueryable<AnonymousType> to System.Collection.Generic.IEnumerable.
Please advise how I can fix this?
public IEnumerable<Session> GetAllListDetailConsumer(string refId)
{
ObjectQuery<Session> sessions = db.Sessions;
ObjectQuery<SessionsList> sessionsLists = db.SessionsList;
var query =
from s in sessions
join sList in sessionsLists on s.ReferralListID equals sList.ReferralListID
where s.ReferralListID == new Guid(refId)
select new SessionConsumerList
{
ReferralListID = s.ReferralListID,
SessionServerId = s.SessionServerID,
ApplicationID = s.ApplicationID,
// ...
ConsumerID = sList.ConsumerID,
ConsumerFirstName = sList.ConsumerFirstName,
ConsumerFamilyName = sList.ConsumerFamilyName,
// ...
};
return query.ToList();
}
You are selecting using select new, which would create an anonymous type, you need to project to class Session in your query like.
select new Session
{
....
But remember if your Session class is a representing a table in your database/data context, then you can't project to that class, instead you may have to create a temporary class and project the selection to that class.
EDIT (Since the question now has been edited)
Now you are selecting new SessionConsumerList and you are returning IEnumerable<Session>, you need to modify method signature to return IEnumerable<SessionConsumerList>
Why not separate the creation of the SessionConsumerList in another method? Makes the code a lot cleaner. Like this:
public static SessionConsumerList CreateSessionConsumerList(
Session s,
SessionsList sList)
{
return new SessionConsumerList
{
ReferralListID = s.ReferralListID,
SessionServerId = s.SessionServerID,
ApplicationID = s.ApplicationID,
// ...
ConsumerID = sList.ConsumerID,
ConsumerFirstName = sList.ConsumerFirstName,
ConsumerFamilyName = sList.ConsumerFamilyName,
// ...
};
}
And then:
var query =
from s in sessions
join sList in sessionsLists on s.ReferralListID equals sList.ReferralListID
where s.ReferralListID == new Guid(refId)
select CreateSessionConsumerList(s, sList);

Alternative coding to a conditional var inferred-type query LINQ to XML?

This is a follow up on a related topic found here
https://stackoverflow.com/questions/1987485/conditionally-assign-c-var-as-elegant-as-it-gets
if I am doing the following:
var query = (SearchString == "" ?
(
from MEDIA in xdoc.Descendants("MEDIA")
select new
{
PLAY = MEDIA.Element("PLAY").Value,
PIC = MEDIA.Element("PIC").Value,
TTL = MEDIA.Element("TTL").Value
}
):
from MEDIA in xdoc.Descendants("MEDIA")
where MEDIA.Element("TTL").ToString().ToLower().Contains(SearchString)
select new
{
PLAY = MEDIA.Element("PLAY").Value,
PIC = MEDIA.Element("PIC").Value,
TTL = MEDIA.Element("TTL").Value
}
) ;
How would I declare the query type to make it static at the class level?
Alternatively, in the referenced post Marc Gravell point out a different approach
IQueryable<Part> query = db.Participant;
if(email != null) query = query.Where(p => p.EmailAddress == email);
if(seqNr != null) query = query.Where(p => p.SequenceNumber == seqNr);
...
How would I declare/recode the query in my case?
Here is my wild attempts :)
IEnumerable<XElement> query = xdoc.Descendants("MEDIA");
if (SearchString != "" )
query = query.Where(m => m.Element("TTL").ToString().ToLower().Contains(SearchString));
Thank you.
How would I declare the query type to make it static at the class level?
You can't. Anonymous types are, well, anonymous... so they don't have a name you can use to declare variables. Your query is of type IEnumerable<something>, but you can't refer to something in your code. So you need to create a specific class that represent the results of your query, and use it instead of the anonymous type.

Only primitive types ('such as Int32, String, and Guid') are supported in this context when I try updating my viewmodel

I am having some trouble with a linq query I am trying to write.
I am trying to use the repository pattern without to much luck. Basically I have a list of transactions and a 2nd list which contains the description field that maps against a field in my case StoreItemID
public static IList<TransactionViewModel> All()
{
var result = (IList<TransactionViewModel>)HttpContext.Current.Session["Transactions"];
if (result == null)
{
var rewardTypes = BusinessItemRepository.GetItemTypes(StoreID);
HttpContext.Current.Session["Transactions"] =
result =
(from item in new MyEntities().TransactionEntries
select new TransactionViewModel()
{
ItemDescription = itemTypes.FirstOrDefault(r=>r.StoreItemID==item.StoreItemID).ItemDescription,
TransactionDate = item.PurchaseDate.Value,
TransactionAmount = item.TransactionAmount.Value,
}).ToList();
}
return result;
}
public static List<BusinessItemViewModel>GetItemTypes(int storeID)
{
var result = (List<BusinessItemViewModel>)HttpContext.Current.Session["ItemTypes"];
if (result == null)
{
HttpContext.Current.Session["ItemTypes"] = result =
(from items in new MyEntities().StoreItems
where items.IsDeleted == false && items.StoreID == storeID
select new BusinessItemViewModel()
{
ItemDescription = items.Description,
StoreID = items.StoreID,
StoreItemID = items.StoreItemID
}).ToList();
}
return result;
However I get this error
Unable to create a constant value of type 'MyMVC.ViewModels.BusinessItemViewModel'. Only primitive types ('such as Int32, String, and Guid') are supported in this context.
I know its this line of code as if I comment it out it works ok
ItemDescription = itemTypes.FirstOrDefault(r=>r.StoreItemID==item.StoreItemID).ItemDescription,
How can I map ItemDescription against my list of itemTypes?
Any help would be great :)
This line has a problem:
ItemDescription = itemTypes.FirstOrDefault(r=>r.StoreItemID==item.StoreItemID)
.ItemDescription,
Since you are using FirstOrDefault you will get null as default value for a reference type if there is no item that satifies the condition, then you'd get an exception when trying to access ItemDescription - either use First() if there always will be at least one match or check and define a default property value for ItemDescription to use if there is none:
ItemDescription = itemTypes.Any(r=>r.StoreItemID==item.StoreItemID)
? itemTypes.First(r=>r.StoreItemID==item.StoreItemID)
.ItemDescription
: "My Default",
If itemTypes is IEnumerable then it can't be used in your query (which is what the error message is telling you), because the query provider doesn't know what to do with it. So assuming the that itemTypes is based on a table in the same db as TransactionEntities, then you can use a join to achieve the same goal:
using (var entities = new MyEntities())
{
HttpContext.Current.Session["Transactions"] = result =
(from item in new entities.TransactionEntries
join itemType in entities.ItemTypes on item.StoreItemID equals itemType.StoreItemID
select new TransactionViewModel()
{
ItemDescription = itemType.ItemDescription,
TransactionDate = item.PurchaseDate.Value,
TransactionAmount = item.TransactionAmount.Value,
CustomerName = rewards.CardID//TODO: Get customer name
}).ToList();
}
I don't know the structure of your database, but hopefully you get the idea.
I had this error due a nullable integer in my LINQ query.
Adding a check within my query it solved my problem.
query with problem:
var x = entities.MyObjects.FirstOrDefault(s => s.Obj_Id.Equals(y.OBJ_ID));
query with problem solved:
var x = entities.MyObjects.FirstOrDefault(s => s.Obj_Id.HasValue && s.Obj_Id.Value.Equals(y.OBJ_ID));

How to dynamically add OR operator to WHERE clause in LINQ

I have a variable size array of strings, and I am trying to programatically loop through the array and match all the rows in a table where the column "Tags" contains at least one of the strings in the array. Here is some pseudo code:
IQueryable<Songs> allSongMatches = musicDb.Songs; // all rows in the table
I can easily query this table filtering on a fixed set of strings, like this:
allSongMatches=allSongMatches.Where(SongsVar => SongsVar.Tags.Contains("foo1") || SongsVar.Tags.Contains("foo2") || SongsVar.Tags.Contains("foo3"));
However, this does not work (I get the following error: "A lambda expression with a statement body cannot be converted to an expression tree")
allSongMatches = allSongMatches.Where(SongsVar =>
{
bool retVal = false;
foreach(string str in strArray)
{
retVal = retVal || SongsVar.Tags.Contains(str);
}
return retVal;
});
Can anybody show me the correct strategy to accomplish this? I am still new to the world of LINQ :-)
You can use the PredicateBuilder class:
var searchPredicate = PredicateBuilder.False<Songs>();
foreach(string str in strArray)
{
var closureVariable = str; // See the link below for the reason
searchPredicate =
searchPredicate.Or(SongsVar => SongsVar.Tags.Contains(closureVariable));
}
var allSongMatches = db.Songs.Where(searchPredicate);
LinqToSql strange behaviour
I recently created an extension method for creating string searches that also allows for OR searches. Blogged about here
I also created it as a nuget package that you can install:
http://www.nuget.org/packages/NinjaNye.SearchExtensions/
Once installed you will be able to do the following
var result = db.Songs.Search(s => s.Tags, strArray);
If you want to create your own version to allow the above, you will need to do the following:
public static class QueryableExtensions
{
public static IQueryable<T> Search<T>(this IQueryable<T> source, Expression<Func<T, string>> stringProperty, params string[] searchTerms)
{
if (!searchTerms.Any())
{
return source;
}
Expression orExpression = null;
foreach (var searchTerm in searchTerms)
{
//Create expression to represent x.[property].Contains(searchTerm)
var searchTermExpression = Expression.Constant(searchTerm);
var containsExpression = BuildContainsExpression(stringProperty, searchTermExpression);
orExpression = BuildOrExpression(orExpression, containsExpression);
}
var completeExpression = Expression.Lambda<Func<T, bool>>(orExpression, stringProperty.Parameters);
return source.Where(completeExpression);
}
private static Expression BuildOrExpression(Expression existingExpression, Expression expressionToAdd)
{
if (existingExpression == null)
{
return expressionToAdd;
}
//Build 'OR' expression for each property
return Expression.OrElse(existingExpression, expressionToAdd);
}
}
Alternatively, take a look at the github project for NinjaNye.SearchExtensions as this has other options and has been refactored somewhat to allow other combinations
There is another, somewhat easier method that will accomplish this. ScottGu's blog details a dynamic linq library that I've found very helpful in the past. Essentially, it generates the query from a string you pass in. Here's a sample of the code you'd write:
Dim Northwind As New NorthwindDataContext
Dim query = Northwind.Products _
.Where("CategoryID=2 AND UnitPrice>3") _
.OrderBy("SupplierId")
Gridview1.DataSource = query
Gridview1.DataBind()
More info can be found at scottgu's blog here.
Either build an Expression<T> yourself, or look at a different route.
Assuming possibleTags is a collection of tags, you can make use of a closure and a join to find matches. This should find any songs with at least one tag in possibleTags:
allSongMatches = allSongMatches.Where(s => (select t from s.Tags
join tt from possibleTags
on t == tt
select t).Count() > 0)

Resources