I'm having a weird issue, I need to connect a View (Workbench) with a table (FA_TICKET), to stablish this connection I use IN operator with subselect... This is going ok... But... When I try to add a new filter to it, it not resolves.
Subquery<Workbench> subFr = query.subquery(Workbench.class);
Root<FrTicket> rootFrTicket = subFr.from(FrTicket.class);
subFr.select(rootFrTicket.get("ticketId"));
Predicate pred = root.get("ticketNum").in(subFr);
criteriaBuilder.and(pred, criteriaBuilder.equal(root.get("type"), TicketType.FR_TICKET));
predicates.add(pred);
....
//predicates is defined at begining and have other filters
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
When I see the sub-query section, it should be like:
select .... from Workbench where ... and (ticketNum in (SUBSELECT) and type = ?)
But... The line about ticket type is never resolved and I see:
select .... from Workbench where ... and (ticketNum in (SUBSELECT))
If I change the order when define the and, I see this:
Predicate pred = criteriaBuilder.equal(root.get("type"), TicketType.FR_TICKET);
criteriaBuilder.and(pred, root.get("ticketNum").in(subFr));
select .... from Workbench where ... and (type = ?)
How I can include this type in the same group?
Because I need to stablish a filter with other kind of Ticket entities, with a different type.
Thanks
Looks like the problem apears with enums, they are problematic with CriteriaBuilder class.
So, if I make this change:
Subquery<Workbench> subFr = query.subquery(Workbench.class);
Root<FrTicket> rootFrTicket = subFr.from(FrTicket.class);
subFr.select(rootFrTicket.get("ticketId"));
Predicate pred = root.get("ticketNum").in(subFr);
criteriaBuilder.and(pred, criteriaBuilder.equal(root.get("type").as(String.class), TicketType.FR_TICKET).name());
predicates.add(pred);
....
//predicates is defined at begining and have other filters
return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
Works smooth.
Related
I am using Groovy Sql in Grails with named parameters to get results from a Postgres DB. My statement is generated dynamically, i.e. concatenated to become the final statement, with the params being added to a map as I go along.
sqlWhere += " AND bar = :namedParam1"
paramsMap.namedParam1 = "blah"
For readability, I am using the groovy string syntax which allows me to write my sql statement over multiple lines, like this:
sql = """
SELECT *
FROM foo
WHERE 1=1
${sqlWhere}
"""
The expression is evaluated as a string containing the linebreaks as \n:
SELECT *\n ...
This is not a problem when I pass params like this
results = sql.rows(sqlString, paramsMap)
but it does become one if paramsMap is empty (which happens since AND bar = :namedParam1 is not always concatenated into the query). I then get an error
org.postgresql.util.PSQLException: No hstore extension installed
which does not really seem to relate to the true nature of the problem. I have for now fixed this with an if...else
if (sqlQuery.params.size() > 0) {
results = sql.rows(sqlString, paramsMap)
} else {
results = sql.rows(sqlString.replace('\n',' '))
}
But this seems a bit weird (especially since it does not work if I use the replace in the if-branch as well).
My question is: why do I really get this error message and is there a better way to prevent it from occuring?
It's certainly a bug in groovy.sql.SQL implementation. The method rows() can't deal with an empty map passed as params. As a workaround, you can test for it and pass an empty list instead.
def paramsMap = [:]
...
if (paramsMap.isEmpty())
paramsMap= []
Issue created at https://issues.apache.org/jira/browse/GROOVY-8082
This is the query I am trying to run in PostgreSQL:
SELECT * FROM message WHERE id IN (
SELECT unnest(message_ids) "mid"
FROM session_messages WHERE session_id = '?' ORDER BY "mid" ASC
);
However, I am not able do something:
create.selectFrom(Tables.MESSAGE).where(Tables.MESSAGE.ID.in(
create.select(DSL.unnest(..))
Because DSL.unnest is a Table<?>, which makes sense since it is trying to take a List-like object (mostly a literal) and convert it to table.
I have a feeling that I need to find a way to wrap the function around my field name, but I have no clue as to how to proceed.
NOTE. The field message_ids is of type bigint[].
EDIT
So, this is how I am doing it now, and it works exactly as expected, but I am not sure if this is the best way to do it:
Field<Long> unnestMessageIdField = DSL.field(
"unnest(" + SESSION_MESSAGES.MESSAGE_IDS.getName() + ")",
Long.class)
.as("mid");
Field<Long> messageIdField = DSL.field("mid", Long.class);
MESSAGE.ID.in(
ctx.select(messageIdField).from(
ctx.select(unnestMessageIdField)
.from(Tables.CHAT_SESSION_MESSAGES)
.where(Tables.CHAT_SESSION_MESSAGES.SESSION_ID.eq(sessionId))
)
.where(condition)
)
EDIT2
After going through the code on https://github.com/jOOQ/jOOQ/blob/master/jOOQ/src/main/java/org/jooq/impl/DSL.java I guess the right way to do this would be:
DSL.function("unnest", SQLDataTypes.BIGINT.getArrayType(), SESSION_MESSAGES.MESSAGE_IDS)
EDIT3
Since as always lukas is here for my jOOQ woes, I am going to capitalize on this :)
Trying to generalize this function, in a signature of sort
public <T> Field<T> unnest(Field<T[]> arrayField) {
return DSL.function("unnest", <??>, arrayField);
}
I don't know how I can fetch the datatype. There seems to be a way to get DataType<T[]> from DataType<T> using DataType::getArrayDataType(), but the reverse is not possible. There is this class I found ArrayDataType, but it seems to be package-private, so I cannot use it (and even if I could, it does not expose the field elementType).
Old PostgreSQL versions had this funky idea that it is OK to produce a table from within the SELECT clause, and expand it into the "outer" table, as if it were declared in the FROM clause. That is a very obscure PostgreSQL legacy, and this example is a good chance to get rid of it, and use LATERAL instead. Your query is equivalent to this one:
SELECT *
FROM message
WHERE id IN (
SELECT "mid"
FROM session_messages
CROSS JOIN LATERAL unnest(message_ids) AS t("mid")
WHERE session_id = '?'
);
This can be translated to jOOQ much more easily as:
DSL.using(configuration)
.select()
.from(MESSAGE)
.where(MESSAGE.ID).in(
select(field(name("mid"), MESSAGE.ID.getDataType()))
.from(SESSION_MESSAGES)
.crossJoin(lateral(unnest(SESSION_MESSAGES.MESSAGE_IDS)).as("t", "mid"))
.where(SESSION_MESSAGES.SESSION_ID.eq("'?'"))
)
The Edit3 in the question is quite close to a decent solution for this problem.
We can create a custom generic unnest method for jOOQ which accepts Field and use it in jOOQ query normally.
Helper method:
public static <T> Field<T> unnest(Field<T[]> field) {
var type = (Class<T>) field.getType().getComponentType();
return DSL.function("unnest", type, field);
}
Usage:
public void query(SessionId sessionId) {
var field = unnest(SESSION_MESSAGES.MESSAGE_IDS, UUID.class);
dsl.select().from(MESSAGE).where(
MESSAGE.ID.in(
dsl.select(field).from(SESSION_MESSAGES)
.where(SESSION_MESSAGES.SESSION_ID.eq(sessionId.id))
.orderBy(field)
)
);
}
With the nDepend API, would something like the following be possible?
I want to keep a watch out for instances where our object factory has been bypassed and a concrete class is being instantiated directly.
Obviously I'd need to be able to filter out things like:
StringBuilder stringBuilder = new StringBuilder();
perhaps by adding to the Where clause type names to exclude, or namespaces in which to check, but I want to make sure we see:
IMyCustomType item = ObjectFactory.Get<IMyCustomType>();
and not this:
MyCustomType item = new MyCustomType();
Thanks.
Maybe such code rule below could help you, hopefully it is understandable enough to not have to comment it:
warnif count > 0
let ctors = Application.Namespaces.WithNameLike("Namespaces1*").ChildMethods().Where(m => m.IsConstructor)
let codeThatMustNotCallCtors = Application.Namespaces.WithNameLike("Namespaces2*").ChildMethods()
from m in codeThatMustNotCallCtors.UsingAny(ctors)
select new { m, ctorsCalled = m.MethodsCalled.Intersect(ctors ) }
I have this scenario:
A SQL Server table myTable with field1, xmlField (nvarchar(50) and xml sql server data type)
Linq to entities
Now I'd like to get a query like this:
SELECT Field1, XmlField
FROM MyTable
WHERE CAST(XmlField AS nvarchar(4000)) = '<myXml />'
Obviously this is a correct query in SQL Server but I can't find a solution to write this in L2E.
Please notify that this code doesn't work:
var query = from row in context.MyTables
where (string)row.XmlField == "<myXml />"
select row
and other cast methods too.
This just because in L2E the "ToString" does't work correctly.
Now my idea is this one: an extension method:
var query = from row in context.MyTables
select row
query = query.CompareXml("XmlField", "<myXml />")
and this is the extended method:
public static IQueryable<TSource> CompareXml<TSource>(this IQueryable<TSource> source, string xmlFieldName, string xmlToCompare)
{
ConstantExpression xmlValue = Expression.Constant(xmlToCompare);
ParameterExpression parameter = Expression.Parameter(typeof(TSource), source.ElementType.Name);
PropertyInfo propertyInfo = typeof(TSource).GetProperty(xmlFieldName);
MemberExpression memberAccess = Expression.MakeMemberAccess(parameter, propertyInfo);
var stringMember = Expression.Convert(memberAccess, typeof(string));
BinaryExpression clauseExpression = Expression.Equal(xmlValue, stringMember);
return source.Where(Expression.Lambda<Func<TSource, bool>>(clauseExpression, parameter));
}
and again this doesn't work too.
Now I'd like to understand how I can force a "Convert" using Cast so I can compare Xml and nvarchar.
Thanks in advance
Massimiliano
Unfortunately EF still doesn't properly support XML columns. I'm afraid pretty much the only choice I know of is to create a view that does the cast and map that to a different entity. This will probably make the code awkward but also offers additional possible scenarios; for example, with a lot of SQL code, you could map elements in the XML columns to actual columns in the view, allowing you to make queries on specific parts of the XML.
On the bright side, at least inserting values in an XML column works pretty much as expected.
I'm trying to use a select object to filter the results of a many to many rowset. This call works great:
$articles = $this->model->findArticlesViaArticlesUsers();
This however does not:
$articles = new Default_Model_Articles();
$articleSelect = $articles->select();
$articleSelect->where("status = 'published'")
->order("date_published DESC")
->limit(1);
$articles = $this->model->findArticlesViaArticlesUsers($articleSelect);
That throws the following error:
exception 'Zend_Db_Select_Exception'
with message 'You cannot define a
correlation name 'i' more than once'
I can't figure out how to successfully get "articles that have the status of 'published'" using the magic many-to-many relationship (nor findManyToManyRowset). I'm at the end of my rope and thinking of just writing the sql manually. Any ideas?
When defining the select statement, you must use the same object that you call findManyToManyRowset (or whatever magic function you use) on.
Ex:
$articles = new Default_Model_Articles();
$user = $articles->find($userId)->current();
$select = $user->select();
$select->where('status = ?', 'published');
$articles = $user->findArticlesViaArticlesUsers($select);
Notice the select statement and findArticlesViaArticlesUsers are both extending $user. Thats the key.
I think you've misunderstood how the relationships work.
See this manual page - you should call the magic method, findArticlesViaArticlesUsers, on a Row object. In this case, I think you want to find a User, and then call findArticles... on that.