how to many to one with mybatis annotation - mybatis

I hope you will understand what I'm looking for in myBatis annotation
The table A return me many values. The table B return me one value for each values of the table A.
And I would like to store all the values from the table B in a list.
I can do it by making to query. One from the table A, get the list, and make a "if" with the query for table B and store the result in a list.
But I'm try to see if I can do something like this:
#Select(SELECT id FROM table_A WHERE a_value = #{aValue})
#Results({
#Result(property = "images", javaType = List.class, column="id",
one= #One(select = "findOneValueInTableB"))})
list<String> fingManyThingsInTableA(string aValue);
#Select(SELECT value FROM table_B WHERE id_table_A = #{id})
String findOnValueInTableB(int id);
Unfortunately this doesn't work and give me the following error:
"nested exception is org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 2"
Edited to show tables and the result I'm expecting
Expected result:
[value1, value2, value3, value 4]
Thanks for your help and your suggestions.

Related

CriteriaBuilder - Subquery returns more than one result, when the expected result is only one

I am basically converting this query snippet:
I have a table A, and a table B (fk a.id), with a relationship where A can have many Bs.
I want to find A that is present in table B, which has values between X and Y.
Basically this query:
SELECT * FROM TABELA A AS a WHERE a.id IN (SELECT b.a_id FROM TABELA B AS B WHERE b.number_residents >= 100 AND b.number_residents <= 500)
Converting to criteria builder, I have this:
Subquery<HisCarAauuEntity> sub = cq.subquery(HisCarAauuEntity.class);
Root<HisCarAauuEntity> subRoot = sub.from(HisCarAauuEntity.class);
predicados.add(cb.in(aauu.get("id")).value(sub.select(subRoot.get("aId")).where(cb.and((cb.greaterThanOrEqualTo(subRoot.get("nResidents"), filtro.getNcargendesde()))), cb.lessThanOrEqualTo(subRoot.get("nResidents"), filtro.getNcargenhasta()))));
This works partially, because it returns me the result A that has the filter criteria, but if A has other rows in table B (that don't match the filter) it returns me that result too...
How do I make object A return the list of object B that contains ONLY the row that matches the filter sent?

Single Value Expression in When Then Aggregate Function TSQL

I am trying to map a certain value of a column based on its count on another table. If the count of [Location] i.e a column of IMPORT.DATA_SCRAP table in each row. For now for location static value i.e Utah and Kathmandu is supplied for test purpose only is equal to 1, then only i need to get the result in the select statement i.e only single value expression must be returned but here n rows of table with value is returned.
For. eg. In the below query,total rows of IMPORT.DATA_SCRAP gets returned, i only need the single first row value in my case.
I came to know whether cursor or CTE will acheive my result but i am unable to figure it out.
Here,
select
case
when
((SELECT COUNT(stateName) FROM Location.tblState where stateName = 'Utah')=1)
then (select stateName, CountryName from Location.tblState where stateName= 'Utah')
end as nameof
from IMPORT.DATA_SCRAP
The relation between country, state, city is as below:
select
case
when
((SELECT COUNT(cityName) FROM Location.tblCity where cityName = 'Kathmandu')=1)
then (select ct.countryName from Location.tblCity c
inner join Location.tblState s
on c.stateID = s.StateID
inner join Location.tblCountry ct
on ct.countryId = s.CountryId
where c.cityName = 'Kathmandu'
)
end as nameof
from IMPORT.DATA_SCRAP
How can i return only a single value expresion despite of multiple nmax rows of IMPORT.DATA_SCRAP row in the result.
If i comment out the -- from IMPORT.DATA_SCRAP in the above query i would get the desired single result expression in my case, but unable how can i acheive it in other ways or suggest me the appropriate way to do these types of situation.

Select all values from 2 tables

I have 3 tables code first, A and B and a join table (one to many relationship to link A and B), I would like to get all the results, not duplicate of the tables A and B and return it as selectList:
var a = from s in db.A
join ss in db.B on s.ps_id equals ss.ss_id
orderby s.ps_label
select new SelectListItem
{
Text = s.ps_id.ToString(),
Value = s.ps_label
};
return a;
This only returns results from the A table, but not from B as well. What is wrong, and what is your advice for best practice and performance for this?

Multiple WHERE clause for same Columns in TSQL

I am trying to query two tables that are in 1-to-many relationship.
What I've done is create a View knowing that i might end up with multiple records for the first table.
My scenario is as follows: I have a table "Items" and table "Properties".
"Properties" table contains an ItemsId column, PropertyId, PropertyValueId columns.
"Items" table/object contains a list of "Properties".
How would I query that "View" such that, I want to get all "Items" records that have a combination of "PropertyId" & "PropertyValueId" values.
In other words something similar to:
WHERE
(PropertyId = #val1 AND PropertyValueId = #val2) OR
(PropertyId = #val3 AND PropertyValueId = #val4) OR
(PropertyId = #val5 AND PropertyValueId = #val6)
WHERE clause is just a loop over "Items.Properties" collection.
"Items" represents a table of Items being stored in the database. Each & every Item has some dynamic properties, one or more. That's why I have another table called "Properties". Properties table contains columns:
ItemId, PropertyId, PropertyValue
"Item" object has a collection of Properties/Values. Prop1:val1, Prop2:val2, etc ...
Thanks
I may not have understood your requirement (despite the update) - if this or any other answer doesn't solve the problem please add some sample data for Items, Properties and the output and then hopefully it would become clear.
If Items is a specification of the property name-value pairs that you need (and has nothing to do with ItemId on Properties which seems strange...)
select p.itemid
from properties p
where exists (select 1 from items i where i.propertyId = p.propertyId and i.propertyValueId = p.propertyValueId)
group by p.itemid
having count(distinct p.propertyid) = (select count(*) from items)
This returns a set of itemids that have one (and only one) property value for each property defined in items. You can put the items count into a variable if you want.
I would use a query like this:
SELECT ItemId
FROM ItemView
WHERE (PropertyId = #val1 AND PropertyValueId = #val2)
OR (PropertyId = #val3 AND PropertyValueId = #val4)
OR (PropertyId = #val5 AND PropertyValueId = #val6)
GROUP BY ItemId
HAVING COUNT(*) = 3
The WHERE clause is the same as in your question, it only allows a row to be selected if the row has a matching property. You only need to make sure additionally that the items obtained have all the properties in the filter, which is done in the above query with the help of the HAVING clause: you are requesting items with 3 specific properties, therefore the number of properties per item in your result set (COUNT(*)) should be equal to 3.
In a more general case, when the number of properties queried may be arbitrary, you should probably consider passing the arguments in the form of a table and join the view to it:
…
FROM ItemView v
INNER JOIN RequestedProperties r ON v.PropertyId = r.Id
AND v.PropertyValueId = r.ValueId
GROUP BY v.ItemId
HAVING COUNT(*) = (SELECT COUNT(*) FROM RequestedProperties)

SqlResultSetMapping with self join table

I have a query with a self join that looks like this,
select t1., t2. from table t1
left outer join table t2 on t2.LFT < t1.LFT
and t2.RGT > t1.RGT
AND t2.REG_CODE_PAR = 'ALL'
AND t1.STATUS_CODE = 'A'
AND t2.STATUS_CODE = 'A'
I'm using #NamedNativeQuery with a result set mapping to get the result.
#NamedNativeQuery(
name="findTree",
query="..... the query above",
resultSetMapping = "regionT")
With the following result set mapping
#SqlResultSetMapping(name = "regionT" , entities ={
#EntityResult(
entityClass = Tree.class
fields = {
#FieldResult(name = "regCode", column = "REG_CODE")
#FieldResult(name = "rgt", column = "RGT"),
#FieldResult(name = "lft", column = "LFT"),
#FieldResult(name = "name", column = "NAME"),
#FieldResult(name = "regCodePar", column = "REG_CODE_PAR"),
#FieldResult(name = "statusCode", column = "STATUS_CODE")
}
),
#EntityResult(
entityClass = TreeSelf.class
fields = {
#FieldResult(name = "regCode1", column = "REG_CODE")
#FieldResult(name = "rgt1", column = "RGT"),
#FieldResult(name = "lft1", column = "LFT"),
#FieldResult(name = "name1", column = "NAME"),
#FieldResult(name = "regCodePar1", column = "REG_CODE_PAR"),
#FieldResult(name = "statusCode1", column = "STATUS_CODE")
}
)
})
The entity class contains looks like this.
#NamedNativeQuery(...)
#SqlResultSetMapping(...)
#Entity
#Table(name = "table")
public class Tree implements Serializable {
#Id
#Column(name = "REG_CODE")
private String regCode; ... ..getters and setters...}
When I run the query using em.createQuery("findTree"), I get the exact same object in both
the 1st and 2nd elements of the returned object array.
Even if I create a class called TreeSelf that is identical to Tree and use it as the 2nd
EntityResult instead of having 2 EntityResults using the same entityClass, I get the same
result.
Can someone point out what's wrong with the configuration?
Let's see if I understand your question. You're expecting to capture two Tree entities from each native query result row. The first entity should be formed from t1's columns. The second entity should be formed from t2's columns. Contrary to expectation, you actually receive two instances formed from t1. No instances from t2 appear. You made a doppelganger Entity for Tree called TreeSelf while debugging, but TreeSelf is ultimately unnecessary and you want to get rid of it. Stop me if any of that was wrong.
I think the problem is due to ambiguous column names. Each column name in the native query appears twice, once from t1 and once from t2. The result mapper seems to be arbitrarily picking the first occurrence of each ambiguous column name for both Tree entities. I'm surprised that works at all. I would have expected an SQLException complaining about column reference ambiguity.
Also, are you sure you want a left outer join? What if no match is found for a t1 row? It will be paired with all NULL in t2's columns. Then you have a null-valued Tree entity. I think. I don't even know what the result mapper would do in that case. Perhaps you want an inner join?
Consider translating this native query into a JPQL query. (JPA Criteria API is just as well, but I find it more cumbersome for examples.) Here's a JPQL version of the native query:
SELECT t1, t2
FROM Tree t1, Tree t2
WHERE t2.lft < t1.lft AND t2.rgt > t1.rgt AND t2.regCodePar = 'ALL' AND
t1.statusCode = 'A' AND t2.statusCode = 'A'
N.B.: This changes the join semantics to inner instead of left outer.
Here's a sketch of code that could run this query:
EntityManager em = ... // EntityManager by injection, EntityManagerFactory, etc.
String jpql = ... // Like example above
TypedQuery<Object[]> q = em.createQuery(jpql, Object[].class);
for (Object[] oa : q.getResultList()) {
Tree t1 = (Tree)oa[0];
Tree t2 = (Tree)oa[1];
}
In case you are stuck with the native query for whatever reason, here's how you can work around the column name ambiguity. Instead of starting the native query like select t1.*, t2.*, alias each column with AS. The SELECT clause would resemble this:
SELECT t1.REG_CODE AS t1_REG_CODE, t1.RGT AS t1_RGT, (... rest of t1 cols ...),
t2.REG_CODE AS t2_REG_CODE, t2.RGT AS t2_RGT, (... rest of t2 cols ...)
The column attribute in each FieldResult must change accordingly. So the column attributes under the first EntityResult should all start with t1_ and the second's should all start with t2_.
I'd humbly recommend deleting the native query and sql result mapper and using JPA Query Language or Criteria API, if you can find a way.
Update: As confirmed in your comments, a useful answer to your question must preserve left (outer) join semantics. Unfortunately, JPQL and the Criteria API don't support complex left join conditions. There is no way to qualify a JPQL left join with an explicit ON condition.
To my knowledege, the only way to do a left outer join under the spec is by traversing an entity relationship. The JPA implementation then generates an ON condition that tests identity equality. The relevant spec bits are 4.4.5 "Joins" and 4.4.5.2 "Left Outer Joins".
To satisfy this constraint, each Tree you want to left-join to its ultimate parent must have an additional column storing the ultimate parent's id. You might be able to cheat around this constraint in a variety of ways (views?). But the path of least resistance seems to be modifying the native query to use aliased arguments, deleting TreeSelf, and updating the result mapper accordingly. Cleverer solutions welcome, though...