mybatis return result map with duplicate entry - mybatis

I am using mybatis to retrieve data from DB, the data returned is containing duplicate entries.
Required result : Column Name , Value
Expected result is : column1 value A
But returned result is : COLUMN1 value A , column1 value A.
Hope able to clarify my doubt.
Can anybody tell me why it is happening?
<select id="getContentMap" resultType="map" parameterType="map">
select planId,location_qualifier from disclaimer_disclosure_content where
<choose>
<when test="plan_id != null">
plan_id = #{plan_id}
</when>
<when test="product_id != null">
product_id = #{product_id}
</when>
<otherwise>
issuer_id = #{issuer_id}
</otherwise>
</choose>
and effective_date >= #{effective_date}
and location_qualifier LIKE CONCAT('%' , #{location_qualifier} , '%')
</select>

The issue you are seeing is a bug in MyBatis 3 up until release 3.0.6: http://code.google.com/p/mybatis/issues/detail?id=303.
After that release you get the answer I outlined in my other answer (which was done with MyBatis 3.1.1).
You have four options:
Just ignore it and only grab the uppercase or lowercase entries
Upgrade to at least 3.0.6
Stop using map as resultType and move to a POJO domain object
Use the workaround below:
workaround for MyBatis < 3.0.6
Use full uppercase column aliases and they will only show up once (as uppercase) in your map:
<select id="getContentMap" resultType="map" parameterType="map">
select plan_id as PLAN_ID, location_qualifier as LOCATION_QUALIFIER from disclaimer_disclosure_content
where
<!-- SNIP: is the same as you had -->
</select>
This results in the output:
{PLAN_ID=2, LOCATION_QUALIFIER=Bar}
(or something similar depending on exactly how your select looks).

You will probably need to report more information, such as:
what database are you using?
what version of MyBatis 3 are you using (or are you still using iBATIS)?
what does your table structure look like?
In any case, I tried out a slightly simplified version of your query using MySQL 5.1 and MyBatis-3.1.1 and it worked fine - meaning that I only got back one entry of the column name in the result map. I provide my setup below so you can try to reproduce it or diagnose where your code may be wrong.
First, you have a error in your select statement. You have
SELECT planId
but then you have:
WHERE ... plan_id = #{plan_id}
so you probably meant SELECT plan_id in the SELECT clause.
Here is what worked for me.
My slightly simplified MyBatis select mapping is:
<select id="getContentMap" resultType="map" parameterType="map">
SELECT plan_id, location_qualifier FROM disclaimer_disclosure_content
WHERE
<choose>
<when test="plan_id != null">
plan_id = #{plan_id}
</when>
<otherwise>
product_id = #{product_id}
</otherwise>
</choose>
AND location_qualifier LIKE CONCAT('%' , #{location_qualifier} , '%')
</select>
Second, my MySQL table for this query:
mysql> select * from disclaimer_disclosure_content;
+---------+--------------------+------------+
| plan_id | location_qualifier | product_id |
+---------+--------------------+------------+
| 1 | Foo | 101 |
| 2 | Bar | 102 |
| 3 | Baz | 103 |
| 4 | Quux | 104 |
+---------+--------------------+------------+
4 rows in set (0.01 sec)
Third, my Java code to use the mapping:
#Test
public void testForSO() throws Exception {
Map<String, Object> paramMap = new HashMap<String, Object>();
paramMap.put("plan_id", 2);
paramMap.put("location_qualifier", "Ba");
List<Map<String,Object>> lmap = session.selectList("getContentMap", paramMap);
assertNotNull(lmap);
Map<String,Object> m = lmap.get(0);
assertNotNull(m);
System.out.println(m.toString());
}
This passes and prints out:
{location_qualifier=Bar, plan_id=2}
I also tried it with
Map<String,Object> m = session.selectOne("getContentMap", paramMap);
and get the same expected result.

Related

Querying jsonb field with #> through Postgrex adapter

I'm trying to query jsonb field via Postgrex adapter, however I receive errors I cannot understand.
Notification schema
def all_for(user_id, external_id) do
from(n in __MODULE__,
where: n.to == ^user_id and fragment("? #> '{\"external_id\": ?}'", n.data, ^external_id)
)
|> order_by(desc: :id)
end
it generates the following sql
SELECT n0."id", n0."data", n0."to", n0."inserted_at", n0."updated_at" FROM "notifications"
AS n0 WHERE ((n0."to" = $1) AND n0."data" #> '{"external_id": $2}') ORDER BY n0."id" DESC
and then I receive the following error
↳ :erl_eval.do_apply/6, at: erl_eval.erl:680
** (Postgrex.Error) ERROR 22P02 (invalid_text_representation) invalid input syntax for type json. If you are trying to query a JSON field, the parameter may need to be interpolated. Instead of
p.json["field"] != "value"
do
p.json["field"] != ^"value"
query: SELECT n0."id", n0."data", n0."to", n0."inserted_at", n0."updated_at" FROM "notifications" AS n0 WHERE ((n0."to" = $1) AND n0."data" #> '{"external_id": $2}') ORDER BY n0."id" DESC
Token "$" is invalid.
(ecto_sql 3.9.1) lib/ecto/adapters/sql.ex:913: Ecto.Adapters.SQL.raise_sql_call_error/1
(ecto_sql 3.9.1) lib/ecto/adapters/sql.ex:828: Ecto.Adapters.SQL.execute/6
(ecto 3.9.2) lib/ecto/repo/queryable.ex:229: Ecto.Repo.Queryable.execute/4
(ecto 3.9.2) lib/ecto/repo/queryable.ex:19: Ecto.Repo.Queryable.all/3
however if I just copypaste generated sql to psql console and run it, it will succeed.
SELECT n0."id", n0."data", n0."to", n0."inserted_at", n0."updated_at" FROM "notifications" AS n0 WHERE ((n0."to" = 233) AND n0."data" #> '{"external_id": 11}') ORDER BY n0."id" DESC
notifications-# ;
id | data | to | inserted_at | updated_at
----+---------------------+-----+---------------------+---------------------
90 | {"external_id": 11} | 233 | 2022-12-15 14:07:44 | 2022-12-15 14:07:44
(1 row)
data is jsonb column
Column | Type | Collation | Nullable | Default
-------------+--------------------------------+-----------+----------+-------------------------------------------
data | jsonb | | | '{}'::jsonb
What am I missing in my elixir notification query code?
Searching for solution I came across only using raw sql statement, as I couldn't figure out what's wrong with my query when it gets passed through Postgrex
so as a solution I found the following:
def all_for(user_id, external_ids) do
{:ok, result} =
Ecto.Adapters.SQL.query(
Notifications.Repo,
search_by_external_id_query(user_id, external_ids)
)
Enum.map(result.rows, &Map.new(Enum.zip(result.columns, &1)))
end
defp search_by_external_id_query(user_id, external_id) do
"""
SELECT * FROM "notifications" AS n0 WHERE ((n0."to" = #{user_id})
AND n0.data #> '{\"external_id\": #{external_id}}')
ORDER BY n0."id" DESC
"""
end
But as a result I'm receiving Array with Maps inside not with Ecto.Schema as if I've been using Ecto.Query through Postgrex, so be aware.

Variable in comma separated in the IN CLAUSE of SP of DB2 for Z/OS

I have a SP in which I have a In clause like mentioned below
value1 is a Int,Variable1 is varchar
Suppose SP started
Variable1=(value1,value2,value3)--getting from another table
Select * from tableA where Column1 in (Variable1).
The just above statement is not working ,needed a work around for this ,Please help
Here is a small example of using an XML list to select an arbitrary number of values:
SELECT * FROM "tableA"
WHERE "Column1" IN (
SELECT * FROM
XMLTABLE('$X/set/row' PASSING XMLPARSE('
<set>
<row item="1"/>
<row item="2"/>
<row item="3"/>
<!-- add as many "row" as you need here -->
</set>
')
AS "X"
COLUMNS
"item" INT PATH '#item'
) AS X
)
Then you can parameterize the query, where ? is a parameter of type XML
SELECT * FROM "tableA"
WHERE "Column1" IN (
SELECT * FROM
XMLTABLE('$X/set/row' PASSING ? AS "X" COLUMNS "item" INT PATH '#item') AS X
)

How to replace '?' in openjpa query with a real parameters?

Is there any way to get replaced '?' in printed out by openjpa query?
TRACE openjpa.Query - Executing query: Query: org.apache.openjpa.kernel.QueryImpl#246923;
TRACE openjpa.jdbc.SQL
SELECT * FROM TEST_TABLE WHERE ID = ? AND ROWNUM <= ?
[params=(long) 111, (long) 5001]
I verified different combinations with following entries:
< entry key="openjpa.Log" value="DefaultLevel=WARN, Runtime=INFO, Tool=INFO, SQL=TRACE, Query=TRACE, JDBC=TRACE"/>
and
< entry key="openjpa.ConnectionFactoryProperties" value="PrettyPrint=true, PrettyPrintLineLength=72, PrintParameters=true" />
but it seems to be not working, according to expectation.
Is there any way to get replaced '?' in printed out by openjpa query?
I assume you want something like this : SELECT * FROM TEST_TABLE WHERE ID = 111 AND ROWNUM <= 5001? If so, no that is not possible. The reason for this is that OpenJPA will always favor setting parameters. Bummer if that isn't what you are looking for.

Split a string and populate a table for all records in table in SQL Server 2008 R2

I have a table EmployeeMoves:
| EmployeeID | CityIDs
+------------------------------
| 24 | 23,21,22
| 25 | 25,12,14
| 29 | 1,2,5
| 31 | 7
| 55 | 11,34
| 60 | 7,9,21,23,30
I'm trying to figure out how to expand the comma-delimited values from the EmployeeMoves.CityIDs column to populate an EmployeeCities table, which should look like this:
| EmployeeID | CityID
+------------------------------
| 24 | 23
| 24 | 21
| 24 | 22
| 25 | 25
| 25 | 12
| 25 | 14
| ... and so on
I already have a function called SplitADelimitedList that splits a comma-delimited list of integers into a rowset. It takes the delimited list as a parameter. The SQL below will give me a table with split values under the column Value:
select value from dbo.SplitADelimitedList ('23,21,1,4');
| Value
+-----------
| 23
| 21
| 1
| 4
The question is: How do I populate EmployeeCities from EmployeeMoves with a single (even if complex) SQL statement using the comma-delimited list of CityIDs from each row in the EmployeeMoves table, but without any cursors or looping in T-SQL? I could have 100 records in the EmployeeMoves table for 100 different employees.
This is how I tried to solve this problem. It seems to work and is very quick in performance.
INSERT INTO EmployeeCities
SELECT
em.EmployeeID,
c.Value
FROM EmployeeMoves em
CROSS APPLY dbo.SplitADelimitedList(em.CityIDs) c;
UPDATE 1:
This update provides the definition of the user-defined function dbo.SplitADelimitedList. This function is used in above query to split a comma-delimited list to table of integer values.
CREATE FUNCTION dbo.fn_SplitADelimitedList1
(
#String NVARCHAR(MAX)
)
RETURNS #SplittedValues TABLE(
Value INT
)
AS
BEGIN
DECLARE #SplitLength INT
DECLARE #Delimiter VARCHAR(10)
SET #Delimiter = ',' --set this to the delimiter you are using
WHILE len(#String) > 0
BEGIN
SELECT #SplitLength = (CASE charindex(#Delimiter, #String)
WHEN 0 THEN
datalength(#String) / 2
ELSE
charindex(#Delimiter, #String) - 1
END)
INSERT INTO #SplittedValues
SELECT cast(substring(#String, 1, #SplitLength) AS INTEGER)
WHERE
ltrim(rtrim(isnull(substring(#String, 1, #SplitLength), ''))) <> '';
SELECT #String = (CASE ((datalength(#String) / 2) - #SplitLength)
WHEN 0 THEN
''
ELSE
right(#String, (datalength(#String) / 2) - #SplitLength - 1)
END)
END
RETURN
END
Preface
This is not the right way to do it. You shouldn't create comma-delimited lists in SQL Server. This violates first normal form, which should sound like an unbelievably vile expletive to you.
It is trivial for a client-side application to select rows of employees and related cities and display this as a comma-separated list. It shouldn't be done in the database. Please do everything you can to avoid this kind of construction in the future. If at all possible, you should refactor your database.
The Right Answer
To get the list of cities, properly expanded, from a table containing lists of cities, you can do this:
INSERT dbo.EmployeeCities
SELECT
M.EmployeeID,
C.CityID
FROM
EmployeeMoves M
CROSS APPLY dbo.SplitADelimitedList(M.CityIDs) C
;
The Wrong Answer
I wrote this answer due to a misunderstanding of what you wanted: I thought you were trying to query against properly-stored data to produce a list of comma-separated CityIDs. But I realize now you wanted the reverse: to query the list of cities using existing comma-separated values already stored in a column.
WITH EmployeeData AS (
SELECT
M.EmployeeID,
M.CityID
FROM
dbo.SplitADelimitedList ('23,21,1,4') C
INNER JOIN dbo.EmployeeMoves M
ON Convert(int, C.Value) = M.CityID
)
SELECT
E.EmployeeID,
CityIDs = Substring((
SELECT ',' + Convert(varchar(max), CityID)
FROM EmployeeData C
WHERE E.EmployeeID = C.EmployeeID
FOR XML PATH (''), TYPE
).value('.[1]', 'varchar(max)'), 2, 2147483647)
FROM
(SELECT DISTINCT EmployeeID FROM EmployeeData) E
;
Part of my difficulty in understanding is that your question is a bit disorganized. Next time, please clearly label your example data and show what you have, and what you're trying to work toward. Since you put the data for EmployeeCities last, it looked like it was what you were trying to achieve. It's not a good use of people's time when questions are not laid out well.

JPA perform part of an query if a value is not null

in Java the following is a common idiom:
if( null != obj && obj.getSomeNumber() > 0 ) { ... }
This will perform the length check only, if null != obj is true.
However, in a JPA NamedQuery this does not work:
#NamedQuery(
name = "query"
query = "SELECT o FROM SomeObjectList o WHERE o.someObject is not null AND o.someObject.someNumber > 0")
(This is what I've expected, as this would also not work in SQL.)
o.someObject is either 'null' or an foreign key to a table where SomeObjects are stored. (Each column of the table corresponds to an attribute of SomeObject.)
------------------------- ----------------------------
| Table: SomeObjectList | | Table: SomeObject |
------------------------- ----------------------------
| id | someObject | | id | number | name |
------------------------- ----------------------------
| 1 | 4 | | 3 | -4 | foo |
------------------------- ----------------------------
| 2 | null | | 4 | 2 | bar |
------------------------- ----------------------------
So I'd like to create a NamedQuery which will return all Objects from SomeObjectList which either have no object (someObject == null) or where SomeObject.number > 0.
Currently, I obtain all objects and check myself if someObject is set.
But is there a way to get an similar behavior in JPA or does I have to perform the checks on the returned objects?
edit: Added graphics and clarified the problem. (Thanks to James.)
You need to use an outer join for this.
Note your query is wrong, you want seem to want an OR not an AND, otherwise just remove the null check.
SELECT o FROM SomeObjectList o left join o.someObject so WHERE o.someObject is null OR so.someNumber > 0
Not sure I understand?
You don't even need the "not null", just the length > 0 will not return true for null?
Also your code is wrong, length is a function, not a navigation.
It should be,
SELECT o FROM SomeObject o WHERE LENGTH(o.arry) > 0
Or is arry a collection relationship? In this case you would need to use SIZE, and the join will also filter absent relationships.