I have two ResultSets that both of theme has completely separated SELECT and has not any relation to each other. but MyBatis always try to mix the result.
Movie table
ID_
NAME_
YEAR_
1
Movie1
2020
2
Movie2
2008
3
Movie3
1988
Artist table
ID_
NAME_
1
John
2
Jane
Select:
<select id="selectMultiple" resultSets="movies,artists" resultMap="multipleQueriesResult" statementType="CALLABLE">
BEGIN
SELECT M.ID_ AS mId,
M.NAME_ AS mName,
M.YEAR_ AS mYear
FROM TestMyBatis.dbo.Movie AS M
END
BEGIN
SELECT A.ID_ AS aId,
A.NAME_ AS aName
FROM TestMyBatis.dbo.Artist AS A
END
</select>
ResultMap:
<resultMap id="multipleQueriesResult" type="DivideOut" autoMapping="true">
<collection property="movie"
ofType="Movie"
javaType="list"
resultSet="movies">
<id property="id" column="mId"/>
<result property="name" column="mName"/>
<result property="year" column="mYear"/>
</collection>
<collection property="artist"
ofType="Artist"
javaType="list"
resultSet="artists">
<id property="id" column="aId"/>
<result property="name" column="aName"/>
</collection>
</resultMap>
DivideOut:
public class DivideOut {
public List<Object> movie;
public List<Object> artist;
#Override
public String toString() {
return "DivideOut{" +
"movie=" + movie +
", artist=" + artist +
'}';
}
}
Expected Output:
[
DivideOut{movie=[Movie{id=1, name=Movie1, year=2020}], artist=[Artist{id=1, name='john}]},
DivideOut{movie=[Movie{id=1, name=Movie1, year=2020}], artist=[Artist{id=2, name='jane}]},
DivideOut{movie=[Movie{id=2, name=Movie2, year=2008}], artist=[Artist{id=1, name='john}]},
DivideOut{movie=[Movie{id=2, name=Movie2, year=2008}], artist=[Artist{id=2, name='jane}]},
DivideOut{movie=[Movie{id=3, name=Movie3, year=1988}], artist=[Artist{id=1, name='jane}]},
DivideOut{movie=[Movie{id=3, name=Movie3, year=1988}], artist=[Artist{id=2, name='john}]}
]
Actual Output:
[
DivideOut{id=1, name=Movie1, year=2020, artist=[Artist{id=1, name='john}]},
DivideOut{id=1, name=Movie1, year=2020, artist=[Artist{id=2, name='jane}]},
DivideOut{id=2, name=Movie2, year=2008, artist=[Artist{id=1, name='john}]},
DivideOut{id=2, name=Movie2, year=2008, artist=[Artist{id=2, name='jane}]},
DivideOut{id=3, name=Movie3, year=1988, artist=[Artist{id=1, name='john}]},
DivideOut{id=3, name=Movie3, year=1988, artist=[Artist{id=2, name='jane}]}
]
Records of Movie is 3 and records of Artist is 2.
Artist and Movie records should be in separated lists, but they are mix.
Database is MSSQLSERVER 2016 and Driver JDBC.SQLServerDriver 11.2.0
The result map maps the query result to DivideOut object, so the first key question is "How many DivideOut instances do you expect?".
It seems that you just need two independent lists and don't care about DivideOut, so I assumed the answer is 1.
In the following statement, I added an extra query and named the result set dummy.
This extra query returns one row that creates the single DivideOut instance.
<select id="selectMultiple" resultSets="dummy,movies,artists"
resultMap="multipleQueriesResult" statementType="CALLABLE">
BEGIN
SELECT 1
END
BEGIN
SELECT
M.ID_ AS mId,
M.NAME_ AS mName,
M.YEAR_ AS mYear
FROM Movie AS M
END
BEGIN
SELECT A.ID_ AS aId,
A.NAME_ AS aName
FROM Artist AS A
END
</select>
If you just want two independent lists, it probably is better to 1) execute two independent SELECTs or 2) specify two result maps like this test.
Here is an executable demo:
https://github.com/harawata/mybatis-issues/tree/master/so-73685798
Related
I am a beginner in Spring MongoDB Criteria.
Once my database was mysql,if the variable is not none,I can do not none query like this:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.mytest.modules.system.mapper.SysUserMapper">
<select id="getSysUser" resultType="org.mytest.modules.system.entity.SysUser">
select * from sys_user where del_flag = 0
<if test="username!=null and username!=''">
and username = #{username}
</if>
<if test="age!=null and age!=''">
and age = #{age}
</if>
<if test="profession!=null and profession!=''">
and profession = #{profession}
</if>
<if test="education!=null and education!=''">
and education = #{education}
</if>
<if test="dateStart!=null">
<![CDATA[ and DATE_FORMAT(time, '%Y-%m-%d %H:%T:%s') >=
DATE_FORMAT(#{dateStart} , '%Y-%m-%d %H:%T:%s') ]]>
</if>
<if test="dateEnd!=null">
<![CDATA[ and DATE_FORMAT(time, '%Y-%m-%d %H:%T:%s') <=
DATE_FORMAT(#{dateEnd} , '%Y-%m-%d %H:%T:%s') ]]>
</if>
</select>
</mapper>
Now my database change to mongoDB.How to do it with Criteria MongoTemplate with Criteria().andOperator?
SysUser sysUser = new SysUser();
// Create criteria object
Criteria criteria = Criteria.where("del_flag").is(0);
// Judge one by one
if (StringUtils.hasLength(sysUser.getUsername())) {
criteria = criteria.and("username").is(sysUser.getUsername());
}
if (Objects.nonNull(sysUser.getAge())) {
criteria = criteria.and("age").is(sysUser.getAge());
}
if (StringUtils.hasLength(sysUser.getProfession())) {
criteria = criteria.and("profession").is(sysUser.getProfession());
}
if (StringUtils.hasLength(sysUser.getEducation())) {
criteria = criteria.and("education").is(sysUser.getEducation());
}
if (Objects.nonNull(sysUser.getDateStart())) {
criteria = criteria.and("dateStart").gte(sysUser.getDateStart());
}
if (Objects.nonNull(sysUser.getDateEnd())) {
criteria = criteria.and("dateEnd").lte(sysUser.getDateEnd());
}
Query query = new Query(criteria);
List<SysUser> sysUserList = mongoTemplate.find(query, SysUser.class, "sys_user");
I'm trying to copy rows with Oracle and myBatis with this statement:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper
...
<insert id="copy" >
INSERT INTO ${table}
<foreach item="key" collection="keys" index="index" open="(" separator="," close=")">
${key}
</foreach>
VALUES
<foreach item="key" collection="map" index="index" open="(" separator="," close=")">
${key}
</foreach>
</insert>
</mapper>
--
for ( Map.Entry<Object, Object> entry2 : map.entrySet()) {
String key2 = (String) entry2.getKey();
Object value = null;
if (entry2.getValue() == null)
value = "NULL";
else if (entry2.getValue() instanceof java.sql.Timestamp)
{
//...
}
else if (entry2.getValue() instanceof Integer || entry2.getValue() instanceof Long ||
entry2.getValue() instanceof Short || entry2.getValue() instanceof java.math.BigDecimal
){
value = entry2.getValue();
}
else if (entry2.getValue().getClass().getName().equals("oracle.sql.CLOB"))
{
Clob clob=(Clob)entry2.getValue();
value= (String)clob.getSubString((long)1, (int)clob.length());
}
else{
value = "'" + entry2.getValue() + "'";
}
map.put(key2, value);
columans_valores.add("'" + key2 + "'");
}
copyService(map, map.keySet(), "table");
--
It works with most types, but CLOB and BLOB get their address copied instead or gets shrunk to 4000 bytes depending on the code i use to copy them, how do i deal with them?
Finally got it working with some tweaks:
To inform myBatis about the type used, switch $ for #
Avoid quotes
To prevent Oracle errors when inserting nulls, remove them from the map instead of
trying to insert null values
I have the following report model:
class crm_purchased_products(models.Model):
_name = "crm.purchased_products"
_description = "Most purchased products report"
_auto = False
partner_id = fields.Many2one('res.partner', string='Customer')
product_id = fields.Many2one('product.product', string='Product')
invoice_qty = fields.Integer('Number of invoices')
quantity = fields.Integer('Quantity')
total_amount_untaxed = fields.Float(string='Total amount', digits=dp.get_precision('Account'))
def init(self, cr):
tools.drop_view_if_exists(cr, self._table)
_query = """CREATE OR REPLACE VIEW crm_purchased_products AS (
SELECT ail.partner_id AS partner_id, ail.product_id AS product_id,
COUNT(ail.invoice_id) AS invoice_qty, SUM(ail.quantity) AS quantity,
SUM(ail.price_subtotal) AS total_amount_untaxed, MAX(ai.date_invoice) AS last_purchase,
(MAX(ai.date_invoice) - MIN(ai.date_invoice))/COUNT(ail.invoice_id) AS days_between_purchases
FROM account_invoice_line AS ail
JOIN account_invoice AS ai ON ail.invoice_id = ai.id
WHERE ai.type = 'out_invoice' AND ai.state IN ('open','paid')
GROUP BY ail.partner_id, ail.product_id HAVING ail.product_id > 0 ORDER BY ail.partner_id ASC
);
"""
def search(self, cr, uid, args, offset=0, limit=None, order=None,context=None, count=False):
return super(crm_purchased_products, self).search(cr, uid, args=args, offset=offset, limit=limit, order=order,
context=context, count=count)
crm_purchased_products()
I want to insert as a tree field in res.partner form view to show only the products associated to the contact:
<page name="internal_notes" string="Internal Notes">
<field name="purchased_products">
<tree string="Most purchased products">
<field name="partner_id" invisible="1"/>
<field name="product_id"/>
<field name="invoice_qty"/>
<field name="quantity"/>
<field name="total_amount_untaxed"/>
</tree>
</field>
</page>
But, when I try to relate it from res.partner:
purchased_products = fields.One2many('crm.purchased_products', 'partner_id', string='Purchased products', readonly=True, copy=True)
I get this error:
ProgrammingError: relation "crm_purchased_products" does not exist
LINE 1: SELECT "crm_purchased_products".id FROM "crm_purchased_produ...
ID field is auto added to all models from models.Model, so you must get it. Try this:
def init(self, cr):
tools.drop_view_if_exists(cr, self._table)
_query = """CREATE OR REPLACE VIEW crm_purchased_products AS (
SELECT row_number() OVER () AS id, ail.partner_id AS partner_id, ail.product_id AS product_id,
COUNT(ail.invoice_id) AS invoice_qty, SUM(ail.quantity) AS quantity,
SUM(ail.price_subtotal) AS total_amount_untaxed, MAX(ai.date_invoice) AS last_purchase,
(MAX(ai.date_invoice) - MIN(ai.date_invoice))/COUNT(ail.invoice_id) AS days_between_purchases
FROM account_invoice_line AS ail
JOIN account_invoice AS ai ON ail.invoice_id = ai.id
WHERE ai.type = 'out_invoice' AND ai.state IN ('open','paid')
GROUP BY row_number() OVER (), ail.partner_id, ail.product_id HAVING ail.product_id > 0 ORDER BY ail.partner_id ASC
);
"""
This code can only capture the date, I want to capture all the time of day (sol.datahorafim = 00:00:00 to dataInicial = 23:55:55). How do I do this?
EDIT WITH PARAM from xml and function.
XML:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<PARAMETERS>
<PARAM name='dataInicial' type='java.sql.Date' size='10' value='' description='citcorpore.comum.datainicio' fix='false' mandatory='true' reload='true' />
<PARAM name='dataFinal' type='java.sql.Date' size='10' value='' description='citcorpore.comum.datafim' fix='false' mandatory='true' default='{TODAY}'/>
<PARAM name='contrato' type='java.lang.Integer' typeHTML='select' size='0'value='' description='Contrato' fix='false' mandatory='false' reload='true' onload='true'>
<OPTION value='-1' text='Todos'/>
<OPTIONS type='SQL' onload='true'>
SELECT idcontrato, numero+' '+nomefantasia
FROM contratos ct, clientes cl
where cl.idcliente = ct.idcliente
order by 2
</OPTIONS>
</PARAM>
<PARAM name='status' type='java.lang.Integer' typeHTML='select' size='0'value='' description='Status' fix='false' mandatory='false' reload='true' onload='true'>
<OPTION value='-1' text='Todos'/>
<OPTION value='6' text='Fechada'/>
<OPTION value='3' text='Cancelada'/>
</PARAM>
<PARAM name='executor' type='java.lang.String' value='' typeHTML='text' size='10' description='Executor' fix='false' mandatory='false'/>
<PARAM name='quantidade' type='java.lang.Integer' value='' typeHTML='text' size='10' description='Qtd Bases' fix='false' mandatory='false'/>
</PARAMETERS>
Function:
montarSql = function() {
var sql = new importNames.StringBuilder();
var parametroContrato = hashParametros.get("PARAM.contrato");
var parametroExecutor = hashParametros.get("PARAM.executor");
var parametroQuantidade = hashParametros.get("PARAM.quantidade");
var parametroStatus = hashParametros.get("PARAM.status");
sql.append("select idsolicitacaoservico,nomeservico,idstatus,qtd_conhecimentos, nome, grupo ");
sql.append("from ");
sql.append("(select con.numero,ser.nomeservico,sol.idsolicitacaoservico, sol.idstatus,");
sql.append("(select count(idBaseConhecimento) from conhecimentosolicitacaoservico where ");
sql.append("idsolicitacaoservico = sol.idsolicitacaoservico) qtd_conhecimentos, usu.nome, gr.nome grupo ");
sql.append("from solicitacaoservico sol INNER JOIN servicocontrato scn ON sol.idservicocontrato = scn.idservicocontrato ");
sql.append("JOIN servico ser ON scn.idservico = ser.idservico ");
sql.append("JOIN contratos con ON scn.idcontrato = con.idcontrato ");
sql.append("join bpm_itemtrabalhofluxo ite on ite.iditemtrabalho = sol.idtarefaencerramento ");
sql.append("left join usuario usu on usu.idusuario = ite.idresponsavelatual ");
sql.append("left join grupo gr on gr.idgrupo = sol.idgrupoatual ");
sql.append("where ")
if (parametroExecutor != ""){
sql.append(" emp.nome like '%");sql.append(parametroExecutor);sql.append("%' and ");
}
if(parametroContrato!="-1"){
sql.append("(con.idcontrato = {PARAM.contrato}) and ");
}
if(parametroStatus !="-1"){
sql.append(" sol.idstatus = ");sql.append(parametroStatus );sql.append(" and ");
}
sql.append(" sol.datahorafim BETWEEN {PARAM.dataInicial} and {PARAM.dataFinal} ) bs ");
I try see documentation but nothing exactly what I want
Your description is not quite clear, but maybe you want to compare like this:
WHERE date_trunc('day', sol.datahorafim) BETWEEN ... AND ...
I am building a custom module in Magento 2 that has a custom discount. I am trying to copy the discount from quote, quote item to order and order item.
In Magento 1, I declare the config.xml like this:
<fieldsets>
<sales_convert_quote_address>
<custom_discount_amount><to_order>*</to_order></custome_discount_amount>
<base_custom_discount_amount><to_order>*</to_order></base_custome_discount_amount>
</sales_convert_quote_address>
<sales_convert_quote_item>
<custome_discount_amount><to_order_item>*</to_order_item></custome_discount_amount>
<base_custom_discount_amount><to_order_item>*</to_order_item></base_custom_discount_amount>
</sales_convert_quote_item>
</fieldsets>
and my custom discount amount was copied to tables: sales_flat_order and sales_flat_order_item as expected.
In Magento 2, I created a file named fieldset.xml with this code:
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Object/etc/fieldset.xsd">
<scope id="global">
<fieldset id="sales_convert_quote_item">
<field name="custom_discount_amount">
<aspect name="to_order_item" />
</field>
<field name="base_custom_discount_amount">
<aspect name="to_order_item" />
</field>
</fieldset>
<fieldset id="sales_convert_quote_address">
<field name="custom_discount_amount">
<aspect name="to_order" />
</field>
<field name="base_custom_discount_amount">
<aspect name="to_order" />
</field>
</fieldset>
</scope>
</config>
but there is no success.
What else do I need to do in Magento 2 to make it work? Can you guys please help me?
In Magento 2 without using fieldset you can also copy custom data from quote item to order item by using plugin.
create di.xml in your module etc folder.
app/code/Vender/Yourmodule/etc/di.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/ObjectManager/etc/config.xsd">
<type name="Magento\Quote\Model\Quote\Item\ToOrderItem">
<plugin name="cedapi_quote_to_order_item" type="Vender\Yourmodule\Model\Plugin\Quote\QuoteToOrderItem"/>
</type>
</config>
Create a class to your module and define a function. app/code/Vender/Yourmodule/Model/Plugin/Quote
Create QuoteToOrderItem.php file
<?php
namespace Vender\Yourmodule\Model\Plugin\Quote;
use Closure;
class QuoteToOrderItem
{
/**
* #param \Magento\Quote\Model\Quote\Item\ToOrderItem $subject
* #param callable $proceed
* #param \Magento\Quote\Model\Quote\Item\AbstractItem $item
* #param array $additional
* #return \Magento\Sales\Model\Order\Item
* #SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function aroundConvert(
\Magento\Quote\Model\Quote\Item\ToOrderItem $subject,
Closure $proceed,
\Magento\Quote\Model\Quote\Item\AbstractItem $item,
$additional = []
) {
/** #var $orderItem \Magento\Sales\Model\Order\Item */
$orderItem = $proceed($item, $additional);//result of function 'convert' in class 'Magento\Quote\Model\Quote\Item\ToOrderItem'
$orderItem->setCustomDesign($item->getCustomDesign());//set your required
return $orderItem;// return an object '$orderItem' which will replace result of function 'convert' in class 'Magento\Quote\Model\Quote\Item\ToOrderItem'
}
}
After spend some time and research issue, i stucked here:
Magento\Quote\Model\QuoteManagement.php
line 446
public function mergeDataObjects(
$interfaceName,
$firstDataObject,
$secondDataObject
) {
if (!$firstDataObject instanceof $interfaceName || !$secondDataObject instanceof $interfaceName) {
throw new \LogicException('Wrong prototype object given. It can only be of "' . $interfaceName . '" type.');
}
$secondObjectArray = $this->objectProcessor->buildOutputDataArray($secondDataObject, $interfaceName);
$this->_setDataValues($firstDataObject, $secondObjectArray, $interfaceName);
return $this;
}
Which ignores converted attributes because logic of merging based on presence getters and setters of target model\interface. So, if you converting attributes which haven't setters and getters in target model they will be ignored here:
Magento\Framework\Reflection\DataObjectProcessor.php line 75
public function buildOutputDataArray($dataObject, $dataObjectType)
{
$methods = $this->methodsMapProcessor->getMethodsMap($dataObjectType);
$outputData = [];
/** #var MethodReflection $method */
foreach (array_keys($methods) as $methodName) {
if (!$this->methodsMapProcessor->isMethodValidForDataField($dataObjectType, $methodName)) {
continue;
}
$value = $dataObject->{$methodName}();
$isMethodReturnValueRequired = $this->methodsMapProcessor->isMethodReturnValueRequired(
$dataObjectType,
$methodName
);
Maybe you might to use observer or plugin to avoid this problem. (issue encountered in 2.0.6 Magento version)
For anybody looking at this in the future, the fact that the fieldset XML doesn't work has been acknowledged by Magento as a bug. There is a core patch available in the ticket (not reproduced here since it may need to be tweaked with new Magento versions).
https://github.com/magento/magento2/issues/5823