Nested foreach in MyBatis 3 for a HashMap parameter - foreach

I am trying to figure out a solution to the following problem using MyBatis 3.0.6:
I need to build a dynamic select statement based on a series of parameters, one of which is of type HashMap<String, List<String>>. The challenge is to figure out how to make MyBatis iterate over all the keys in an outer foreach loop as well as iterate over the elements of the value list in the inner loop.
To illustrate, suppose my hash map parameter called filter contains states (lists of state codes, each list being the value) per country (country code as the key) like so:
'US' -> {'CO','NY','MI','AZ'};
'CA' -> {'ON','BC','QC'}
I need my dynamic SQL to look like this (in a grossly simplified form):
SELECT *
FROM Table1
WHERE ... some static criteria goes here...
AND RowId IN (SELECT RowId FROM Table2 WHERE Country = 'US' AND State IN ('CO','NY','MI','AZ')
AND RowId IN (SELECT RowId FROM Table2 WHERE Country = 'CA' AND State IN ('ON','BC,'QC')
I would imagine my mapper XML should look something like this:
<select id="getData" resultType="QueryResult">
SELECT *
FROM Table1
WHERE ... some static criteria goes here...
<if test="filter != null">
<foreach item="country" index="i" collection="filter" separator="AND">
RowId IN (SELECT RowId
FROM Table2
WHERE Country = #{country} AND State IN
<foreach item="state" index="j" collection="country.states" separator="," open="(" close=")">
#{state}
</foreach>
</foreach>
</if>
</select>
So the question is, what's the proper syntax to get the country.states to iterate over in the nested foreach loop?
UPDATE
After some tinkering I couldn't get MyBatis to play nicely with the HashMap-based approach, so I ended up adding a new class that maps multiple values to their parent value, then passing a list of such objects to MyBatis. Using the countries/states example above, the class looks like so:
public class Filter {
private String country;
private ArrayList<String> states;
// ... public get accessors here ...
}
The DAO method:
public void QueryResult[] getResults( #Param("criteria") List<Filter> criteria) ...
And the MyBatis mapping:
<select id="getData" resultType="QueryResult">
SELECT *
FROM Table1
WHERE ... some static criteria goes here...
<if test="criteria!= null">
<foreach item="filter" index="i" collection="criteria" separator="AND" open="AND">
RowId IN (SELECT RowId
FROM Table2
WHERE Country = #{filter.country} AND State IN
<foreach item="state" index="j" collection="filter.states" separator="," open="(" close=")">
#{state}
</foreach>
</foreach>
</if>
</select>
Works like a charm.

Actually, you can use the country value to look it up in the filter map and make it work as you initially had it. In the second loop, your collection will be defined as filter.get(country) and that should be good. This is of course considering I'm interpreting your question correctly.

I had something to be inserted as a map where each map key maps to a list.
I wrote the query this way.
INSERT INTO TB_TEST (group_id, student_id) VALUES
<foreach collection="idMap.entrySet()" item="element" index="index" separator=",">
<foreach collection="element.value" item="item" separator="," >
( #{element.key} #{item} )
</foreach>
</foreach>

As of now, I can do this.
<foreach collection="myMap" index="key" item="value">
<foreach collection="value" item="element">
... = #{key} ...
... = #{element})
</foreach>
</foreach>

Related

How to search in SQL with XML data using Entity model?

I have a SQL database where one of the value is a XML string.
Now I'm trying to write a search function to check is it xml string contains a searchstring.
Standard SQL command would be like this:
SELECT
[ID], [DisplayName], [XML_String],
[CreatedTime], [ModifiedTime]
FROM
[Simple_SQL_DB].[dbo].[Simple_Table]
WHERE
CAST(XML_String AS nvarchar(max)) LIKE "%SearchString%"
FROM
Simple_Table
ORDER BY
ModifiedTime DESC)
ORDER BY Displayname
I tried something like this:
private SimpleTableEntities db = new SimpleTableEntities ();
var query = db.Simple_Table
.Where(w => w.XML_String.Cast<string>().Contains("%SearchString%"))
.OrderByDescending(x => x.ModifiedTime);
Unfortunately I get this error:
System.ArgumentException: 'DbExpressionBinding requires an input expression with a collection ResultType.
Can somebody help me to write a correct query?

Strip string from a field with defined separator in invoice report

first of all - I'm still newbie in Odoo so this is maybe explained wrong but I will try.
In inherited invoice_report xml document i have conditional field that needs to be shown - if column (field) in db is equal to another column. To be more precise - if invoice_origin from account_move is equal to name in sale_order.
This is it's code:
<t t-foreach="request.env['sale.order'].search([('name', '=', o.invoice_origin)])" t-as="obj">
For example in database this invoice_origin is [{'invoice_origin': 'S00151-2022'}]
On invoices that are created from more than one sales orders it is this [{'invoice_origin': 'S00123-2022, S00066-2022'}]
How can I strip this data to use in foreach separately the part [{'invoice_origin': 'S00123-2022'}] and separately [{'invoice_origin': 'S00066-2022'}]
Thank you.
You can try to split up the invoice origin and use the result for your existing code:
<t t-set="origin_list" t-value="o.invoice_origin and o.invoice_origin.split(', ') or []" />
<t t-foreach="request.env['sale.order'].search([('name', 'in', origin_list)])" t-as="obj">
<!-- do something -->
</t>

Querying over nested assets in Hyperledger Composer

I want to write a specific query for my Hyperledger Composer app. Below, I have 2 assets and a transaction. Asset1 has a field called contents, which is an array of type Asset2. The associated code is below:
namespace org.acme.biznet
asset Asset1 identified by Asset1Id {
o String Asset1Id
--> Asset2[] contents
}
asset Asset2 identified by Asset2Id {
o String Asset2Id
}
transaction Transact {
--> Asset1 asset1
}
I want to select all instances of Transact where the associated Asset1 has a specified Asset2 inside. The closest solution I came to the query below, which did not work.
query GetTransactionsThatHaveAsset2 {
description: ""
statement:
SELECT org.acme.biznet.Transact
WHERE (asset1.contents CONTAINS (Asset2Id == _$propId))
}
The thing is, I have also written the query below.
query GetAsset1sThatHaveAsset2 {
description: ""
statement:
SELECT org.acme.biznet.Asset1
WHERE (contents CONTAINS (Asset2Id == _$propId))
}
This query behaves as intended, but it's selecting over Asset1. I want to select over Transact. How would I write this query?
no, you can't presently nest the query like you propose, nested named queries are not currently implemented in Composer (CouchDB is not a relational DB, and hence Composer query language can't translate the nested query presently to be translated to CouchDB) 2) Transact is a transaction - it contains a relationship identifier(s) you defined in your model, not the nested data stored in the related asset. You would have to define a query that searches for all transactions that matches the asset1 identifier field you passed to the trxn - then in your code, you can check transact.asset1.contents contains the 'something' (passed in to the trxn too?) using a javascript match - quite straightforward). Alternatively, you could use the REST API filters (loopback filters as opposed to queries) where (form your app code) you can resolve the relationships of transaction (Transact) back to asset1 (and its contents) using the REST call with filter eg
{"where":{"asset1":"resource:org.acme.net.Asset1#1"}, "include":"resolve"} . Hope this helps, maybe its nesting you're looking for exclusively..
In my case, I have these assets
// Define assets
asset Product identified by productId {
o String productId
o String description
o String serialNumber
o String modelNumber
o String status // TRANSFERED, RECEIVED, RECLAMED
o DateTime joinTime
--> Trader previousOwner
--> Trader currentOwner
--> Trader newOwner
}
// Trade, moves product from to a new owner.
transaction Trade {
--> Product product
--> Trader newOwner
o String trade_type
}
Executing a trade transaction results in the record:
{
"$class": "org.sp.network.Trade",
"product": "resource:org.sp.network.Product#123",
"newOwner": "resource:org.sp.network.Trader#6694",
"trade_type": "Trade",
"transactionId": "e39a86ed4748a3ab73b5e9c023f6bb0ca025098af09b8b5b2dca8f5f7ef0db67",
"timestamp": "2019-06-13T12:04:20.180Z"
}
And to query all Trade transactions that contain a product is
query ProductPath{
description: "Selete all Trade transactions for a specific ProductId"
statement:
SELECT org.sp.network.Trade
WHERE (_$productId==product)
}
Using the rest server: the value of _$productId is resource:org.sp.network.Product#123

NHibernate QueryOver: How to join unrelated entities?

I have the following query working which gets the results I want:
int associatedId = 123;
MyObject alias = null;
var subQuery = QueryOver.Of<DatabaseView>()
.Where(view => view.AssociatedId == associatedId)
.And(view => view.ObjectId == alias.ObjectId)
.Select(view => view.ObjectId);
var results = session.QueryOver<MyObject>(() => alias)
.WithSubquery.WhereExists(subQuery)
.List();
The DatabaseView has been mapped as an actual NHibernate entity (so I can use it with QueryOver), but it is not associated to MyObject in the HBM mappings.
This query returns an IList<MyObject> using a SELECT ... FROM MyObject WHERE EXISTS (subquery for DatabaseView here). How can I re-write this to return the same data but using a JOIN instead of sub query?
In NHibernate 5.1+ it's possible for QueryOver/Criteria via Entity Join:
int associatedId = 123;
MyObject alias = null;
DatabaseView viewAlias = null;
var results = session.QueryOver<MyObject>(() => alias)
.JoinEntityAlias(() => viewAlias, () => viewAlias.ObjectId == alias.ObjectId && viewAlias.AssociatedId == associatedId)
.List();
Criteria example:
int associatedId = 123;
var results = session.CreateCriteria<MyObject>("alias")
.CreateEntityAlias(
"viewAlias",
Restrictions.EqProperty("viewAlias.ObjectId", "alias.ObjectId")
&& Restrictions.Eq("viewAlias.AssociationId", associatedId),
JoinType.InnerJoin,
typeof(DatabaseView).FullName)
.List();
You can join onto unrelated entities with Linq in NHibernate 3+
Funnily enough you use the join query expression element:
from type1 in Repository.Query<MyType1>()
join type2 in Repository.Query<MyType2>()
on type1.Id equals type2.Id
Note: Repository.Query is just returning an IQueryable Query from the session
I'm hoping there is a solution for QueryOver as I don't always want to model two-way relationships in my domain but they are still useful for querying.
Also, you can map a Access="noop" 2 way relationship using Criteria API without putting into your POCO classes:
http://ayende.com/blog/4054/nhibernate-query-only-properties
I realize this question is 5 years old, and the "correct" answer is definitely that you can't do this with QueryOver, as the other answers indicate. However, if you really need this functionality (as I did), there is a decent workaround that I found.
The solution is to use a "loader query" with native SQL in your mapping XML to produce a related collection (see http://nhibernate.info/doc/nhibernate-reference/querysql.html#querysql-load). In the OP's specific example, you would go ahead and map your DatabaseView as an entity as suggested, and then write the following in your mapping:
<class name="MyObject"...>
...
<set name="MyViews" inverse="true">
<key column="ObjectId" foreign-key="none"/>
<one-to-many class="MyObject"/>
<loader query-ref="myObjectViewsLoadQuery"/>
</set>
</class>
Then we just need to define our named myObjectViewsLoadQuery in raw SQL to explain to NH how to join the two:
<sql-query name="myObjectViewsLoadQuery">
<load-collection alias="view" role="MyObject.MyViews"/>
SELECT view.*
FROM DatabaseView view
WHERE view.ObjectId = :id
</sql-query>
We can now pretend like there is a "real" collection named MyViews relating MyObject to DatabaseView in our query:
MyObject alias = null;
DatabaseView view = null;
var results = session.QueryOver<MyObject>(() => alias)
.JoinAlias( () => alias.MyViews, () => view )
//.Where( () => view.Property == "myValue" ) // optionally, restrict the view etc.
.List();
Certainly, this is a lot of trouble to go through if you only care about an "elegant" query. However, if the reason you are using QueryOver is that you want to be able to accept arbitrary input Expressions to filter your DatabaseView by, or various similar activities, this works very nicely.

Set selection of typesafe JPA 2 query with joins

I have simple query with two joins:
SELECT p.id, BU.ID, BU.TEXTLANG as Title FROM PROJEKT P
JOIN Text BT ON BT.ID = P.TITEL_ID
JOIN Uebersetzung BU ON BU.TEXT_ID = BT.ID
WHERE BU.TEXTLANG LIKE '%Spatial%';
This query must be converted to a typesafe query, what i tried is:
final CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
final CriteriaQuery<Projekt> query = criteriaBuilder.createQuery(Projekt.class);
final Root<Projekt> projekt = query.from(Projekt.class);
Join<Projekt, Text> bt = projekt.join(Projekt_.titel);
Join<Text, Uebersetzung> bu = bt.join(Text_.uebersetzungen);
Predicate condition = criteriaBuilder.like(bu.get(Uebersetzung_.textLang),"%Spatial%");
query.where(condition);
query.distinct(true);
query.orderBy(criteriaBuilder.asc(projekt.get(Projekt_.id)));
My problem is, that i want to get the field Uebersetzung_.textLang as selection. But when i try to add selection to the query, for example:
query.multiselect(bu.get(Uebersetzung_.textLang));
i get an error message:
org.apache.openjpa.persistence.OptimisticLockException: Unable to obtain an object lock on "null".
I need this field, because i want to access it from the jsp page. Is there any other way to get this field in the selection?

Resources