Что-то странное с mybatis и isnull на BigDecimal с Sybase

Day ago I found something strange behavior in my code after adding new implementation of interface.

I see mybatis update return -1 always, and table not updated.

Log told me about some interesting behavior:

  • Создание нового SqlSession
  • SqlSession [...] не был зарегистрирован для синхронизации, поскольку синхронизация не активна
  • JDBC Connection [...] не будет управляться Spring
  • ==> Подготовка: обновить количество наборов карт = isnull(?, сумма), name=isnull(?, name), balanceTime=isnull(?, balanceTime) где number=? и clientId=?.
  • ==> Параметры: 0.00(BigDecimal), Test Card(String), 2017-02-22 09:05:24.78(Timestamp), 0000000000000000(String), 111000(Long)
  • Закрытие нетранзакционного SqlSession [...]

После отправки параметров на сервер ничего не произошло. Это происходит после рефакторинга моего DAO:

public class DBaseCard{
    private long id;
    private String number;
    private String name;
    private BigDecimal amount; // was Double
    .....
}

В производстве я использую Sybase IQ. Для теста я использую H2 db, и для него все работает нормально.

Если я изменю тип поля «сумма» на «Двойной», все будет работать нормально.

Если я оставлю тип BigDecimal и заменю «количество = isnull (# {card.amount}, сумма)» на «количество = # {card.amount}» или что-то вроде «сумма = isnull (0,00, сумма)», все работает нормально, а в логе вижу:

  • ==> Параметры: 0.0(BigDecimal), ....
  • ‹== Обновлений: 1

Пожалуйста, помогите мне понять, почему это происходит.


person E.Yusipiv    schedule 23.02.2017    source источник


Ответы (2)


Это может быть связано с типом столбца в Sybase, H2 может иметь более разрешающее поведение.

Ознакомьтесь с документацией по обработчикам типов Mybatis по умолчанию:

С BigDecimal:

BigDecimalTypeHandler java.math.BigDecimal Любой совместимый NUMERIC или DECIMAL

С двойным:

DoubleTypeHandler java.lang.Double, double Любой совместимый NUMERIC или DOUBLE

Это может быть функция isnull. Вы можете попробовать заменить на coalesce.

Несмотря на то, что в журнале вы видите, что 0.00(BigDecimal), 0.00 — это только строковое представление Java BigDecimal, и значение, фактически привязанное драйвером, может быть однородным до нуля, например, 0, а иногда и 0==null, тогда как 0!==null, это объясняет, почему жестко запрограммировано 0.00 работает как положено.

Написание хранимой процедуры является опцией.

Вы также можете рассмотреть возможность использования динамического SQL Mybatis (обрезать, где set) для создания SET, чтобы вам больше не приходилось полагаться на isnull:

UPDATE
<set>
      <if test="amount!= null">amount=#{amount},</if>
      <if test="name!= null">name=#{name},</if>
      <if test="balanceTime!= null">balanceTime=#{balanceTime} </if>
</set>
WHERE number=#{number} AND clientId=#{clientId}

Если вы не можете гарантировать, что в SET будет что-то (по крайней мере, 1 ненулевой параметр), <set> должен содержать (в последней строке) что-то нейтральное (без фактического обновления), например: id=id.

person blackwizard    schedule 23.02.2017
comment
Спасибо за помощь! Вы хорошо подумали над моей проблемой. Поэтому я создаю ответ на свой вопрос. - person E.Yusipiv; 27.02.2017

Я нашел основной случай моей проблемы. После некоторого времени просмотра исходников mybatis я понимаю, что в моем случае там нет ни Mybatis, ни самого SQL. SQL-запрос работает с клиента пользовательского интерфейса и не будет работать с кодом. И я создаю простой тест:

    Connection conn = DriverManager.getConnection("conn_string", "login", "pass");

    PreparedStatement ps = conn.prepareStatement("update Card set amount=isnull(?, amount), name=isnull(?, name), balanceTime=isnull(?, balanceTime) where number=? and clientId=?");

    //ps.setDouble(1, 12.23);
    ps.setBigDecimal(1, new BigDecimal("155.0"));
    ps.setString(2, "Test test");
    ps.setTimestamp(3, new Timestamp(System.currentTimeMillis()));
    ps.setString(4, "00000000000000");
    ps.setLong(5, 111010111L);

    LOG.info("execute update result: {}", ps.executeUpdate());

    conn.close();

Я пробую несколько вариантов для 'conn_string': "jdbc:jtds:sybase://..." и "jdbc:sybase:Tds://..." . И драйвер jConnect возвращает 1 при обновлении, вместо того, чтобы драйвер jTDS возвращал 0.

Похоже на ошибку в драйвере jTDS?

person E.Yusipiv    schedule 27.02.2017