2017-02-20 27 views
2

Я изучаю, как использовать MyBatis. Честно говоря, мне очень нравятся эти рамки. Он прост в использовании, и я доволен его, потому что я могу использовать свои команды sql с ним :) Я использую MyBatis 3.4.2 и базу данных PostgreSQL.отображение входных и выходных параметров с помощью MyBatis

Например, мне нравится, как легко выполнить запрос перед вставкой с аннотацией @SelectKey. И отображение данных работает как шарм, если я добавлю некоторую аннотацию перед интерфейсом, примерно так: @Results({ @Result(property = "javaField", column = "database_field", javaType = TypeHandler.class).

Что мне не нравится (и я надеюсь, что вы можете поместить меня в правильном направлении), являются следующие:


(Выпуск 1) У меня есть вопросы, которые позволяют мне используйте нулевое и нормальное значение без каких-либо дополнительных «if» java-операторов, чтобы проверить, содержит ли переменная значение null или нет. Они выглядят следующим образом:

SELECT * FROM table 
WHERE key_name = ? AND ((? IS NULL AND user_id IS NULL) OR User_id = ?) 

С JDBC мне нужно на следующее:

stmt = connection.prepareStatement(query); 
stmt.setString(1, "key"); 
stmt.setString(2, userId); 
stmt.setString(3, userId); 

Как вы можете видеть, что нужно пройти два раза USERID потому, что это так, как работает JDBC. Честно говоря, я ожидал, что следующий код ниже будет работать с MyBatis, но, к сожалению, он не работает. Необходимо определить третий параметр.

Интересно, можно ли добавить эту функцию в MyBatis. Это должно быть хорошо, если MyBatis может связать USERID дважды автоматически, что-то вроде этого:

@Select("SELECT * FROM table key_name = #{key} and ((#{userId} is null and user_id is null) OR user_id = #{userId}) 
SomeClass findByKeyAndUserId(String key, Long userId); 

Что обходной путь на самом деле я сделал следующий. Я ненавижу это, потому что это сложно и дополнительные Java «если» заявление необходимо:

@Select("SELECT * FROM table WHERE key_name = #{key} AND COALESCE(user_id, -1) = #{userId}") 
SomeClass findByKeyAndUserId(String key, Long userId); 

userId = (userId == null) ? -1 : userId; 
SomeClass abc = mapper.findByKeyAndUserId(key, userId); 

Я не знаю, что это лучшая практика, чтобы справиться с этой ситуацией с MyBatis. Пожалуйста, направляйте меня.


(Выпуск 2) Картирование в случае @Select. Есть ли способ избежать повторного кода при отображении результата запросов с тем же типом результата?

первый запрос:

@Select("SELECT * FROM table WHERE ...") 
@Results({ 
     @Result(property = "key", column = "key_name", javaType = String.class), 
     @Result(property = "value", column = "key_value", javaType = String.class), 
     @Result(property = "userId", column = "user_id", javaType = Long.class), 
     @Result(property = "interval", column = "interval", javaType = Long.class), 
     @Result(property = "description", column = "description", javaType = String.class), 
     @Result(property = "status", column = "status", typeHandler = StatusTypeHandler.class) 
}) 
SomeClass findByKeyAndUserId(@Param("key") String key, @Param("userId") Long userId); 

второй запрос:

@Select("SELECT * FROM table WHERE <different conditions then before>") 
@Results({ 
     <I need to add here the exact same code then before in query 1> 
}) 
SomeClass findByKeyAndUserId(@Param("key") String key, @Param("userId") Long userId); 

Можно ли использовать как-то код, связанные отображение? Мне нужно добавить сопоставление, потому что я использую специальный обработчик типа для поля состояния. Я использую конфигурацию на основе аннотации.


(Выпуск 3)@Param аннотацию я ничего не мог видеть в документации о @Param аннотаций. Трудно было понять, почему мои параметры java не были ограничены должным образом.Наконец, я понял, что в моем коде отсутствовала аннотация @Param. Почему это не упоминается в официальной документации? Я сделал что-то не так, и @Param не нужно использовать?

Этот код работает отлично:

SomeClass findByKeyAndUserId(@Param("key") String key, @Param("userId") Long userId); 

Это не работает:

SomeClass findByKeyAndUserId(String key, Long userId); 

ОБНОВЛЕНИЕ ДЛЯ (Выпуск 1)

Мой первый идея была аналогична с этим @blackwizard menti oned: «Mybatis связывает параметры по имени, затем один раз, дважды, N раз, как может быть время, на которое он ссылается, он работает».

Но это на самом деле не работает должным образом. Он работает, если userId не равен null. Если это null, я получаю прекрасное исключение, которое возвращается из базы данных. Я предполагаю, что MyBatis связывает нулевое значение неправильно. Возможно, это ошибка. Я не знаю :(

Exception:

org.apache.ibatis.exceptions.PersistenceException: 
### Error querying database. Cause: org.postgresql.util.PSQLException: ERROR: could not determine data type of parameter $2 
### The error may exist in com/.../dao/TableDao.java (best guess) 
### The error may involve ....dao.Table.findByKeyAndUserId-Inline 
### The error occurred while setting parameters 
### SQL: SELECT * FROM table WHERE key_name = ? AND (? IS NULL AND user_id IS NULL) OR user_id = ? AND status = 1 
### Cause: org.postgresql.util.PSQLException: ERROR: could not determine data type of parameter $2 

Наконец я нашел три различных решения

(Выпуск 1: раствор 1).

Я последовал за то, что @blackwizard, и я смог переместить условие userId = (userId == null) ? -1 : userId с уровня Java на уровень MyBatis. И это неплохо! Правильный синтаксис: <if test='userId==null'><bind name='userId' value='-1'/></if>.

(Выпуск 1: Раствор 2) Причина, почему я возвращаюсь could not determine data type of parameter $2 ошибку от Postgres, потому что в случае, если на нулевое значение драйвер JDBC не может определить тип параметра. Итак, давайте определим его вручную.

@Select("SELECT * FROM table " 
     + "WHERE key_name = #{key} AND ((#{userId}::BIGINT IS NULL AND user_id IS NULL) OR user_id = #{userId})") 
}) 
SomeClass findByKeyAndUserId(@Param("key") String key, @Param("userId") Long userId); 

(Выпуск 1: Раствор 3) 2-е решение зависит от PorstgreSQL. Следующее решение полностью независимо от базы данных. Спасибо за @blackwizard за хороший комментарий.

@Select("SELECT * FROM table " 
     + "WHERE key_name = #{key} AND ((#{userId, jdbcType=BIGINT} IS NULL AND user_id IS NULL) OR user_id = #{userId})") 
}) 
SomeClass findByKeyAndUserId(@Param("key") String key, @Param("userId") Long userId); 

Лично я предпочитаю решение 3. Он содержит менее дополнительный код.

ответ

4

Выпуск 1:

Назовите параметры:

@Select("SELECT * FROM table key_name = #{key} and ((#{userId} is null and user_id is null) OR user_id = #{userId}") 
SomeClass findByKeyAndUserId(@Param("key") String key, @Param("userId") Long userId); 

Mybatis действительно связывают параметры по имени, а затем один раз, два раза, N раз, а может времени он ссылается, он работает.

Вы можете сделать , если в Mybatis: с XML-тегами, хотя на самом деле это не лучше ... в любом случае это хороший трюк, который вы можете повторно использовать когда-нибудь для другой цели.Чтобы использовать теги XML в значении аннотации, значение должно быть встроено в тег <script> точно в начале и в конце строки.

@Select({"<script>", 
     "<if 'userId==null'><bind name='userId' value='1'/></if>", 
     "SELECT * FROM table WHERE key_name = #{key} ", 
     "AND COALESCE(user_id, -1) = #{userId}", 
     "</script>"}) 
SomeClass findByKeyAndUserId(@Param("key") String key, @Param("userId") Long userId); 

Вы также можете использовать typeHandler для установки значения по умолчанию, просто использовать его с параметром: #{userId, typeHandler=CustomDefaultValueTypeHandler}

Edit: ответ на дополнительный вопрос: Если вы хотите разрешить прохождение нулевых значений вместо обработки замените значение по умолчанию, то вы должны дать Mybatis некоторый намек на предполагаемый тип связанного параметра, потому что он не может разрешить фактический тип null, так как он не знает/не видит объявление переменной в интерфейсе Mapper. Итак: #{userId, javaType=int,jdbcType=NUMERIC}. Достаточно одного из двух атрибутов.

Documentation состояния:

Как и остальные MyBatis, то javaType почти всегда может быть определена от объекта параметра, если этот объект не является HashMap. Затем необходимо указать javaType, чтобы обеспечить правильность TypeHandler is б/у.

ПРИМЕЧАНИЕ Тип JDBC требуется JDBC для всех столбцов с нулевым значением, если null передается как значение. Вы можете сами изучить это по , прочитав JavaDocs для метода PreparedStatement.setNull().

Выпуск 2: вы определенно не может повторно использовать/mutualize, что вы определяете в аннотации. Это не из-за Мибатиса, а из аннотаций. Вам нужно будет определить карты результатов в XML, ссылающиеся на них с помощью @ResultMap. Documentation состояние:

ResultMap Method N/A  This annotation is used to provide the id of a <resultMap> element in an XML mapper to a @Select or @SelectProvider annotation. This allows annotated selects to reuse resultmaps that are defined in XML. This annotation will override any @Results or @ConstructorArgs annotation if both are specified on an annotated select. 

Выпуск 3: как я уже answered in your other question, @param аннотации имеет эффект для преобразования удобного списка параметров на карту, такие как:

Map<String, Object> params = new HashMap<String, Object>(); 
params.put("key", key); 
params.put("userId", userId); 

Я согласен, что Mybatis документация может быть лучше, и вы найдете больше ресурсов на месте, таких как here.

Однако Mybatis documentation состояния следующее о @param аннотацию

@Param Parameter N/A  If your mapper method takes multiple parameters, this annotation can be applied to a mapper method parameter to give each of them a name. Otherwise, multiple parameters will be named by their position prefixed with "param" (not including any RowBounds parameters). For example #{param1}, #{param2} etc. is the default. With @Param("person"), the parameter would be named 
#{person}. 
+0

Большое спасибо за ваш ответ. Я обновил свое оригинальное сообщение для (выпуск 1).Не могли бы вы проверить его? Завтра буду проверять ваши два ответа. – zappee

+0

3-я проблема теперь понятна. Большое спасибо! Так вот почему я иногда вижу в журнале исключений, что привязка paramX неверна;) – zappee

+0

Я обновил сообщение. Первая проблема полностью решена. Спасибо за вашу помощь! – zappee

0

Выпуск 3:

Пожалуйста, попробуйте скомпилировать опцию-параметров при условии, так как JDK 8. Вы можете опустить @param аннотацию.

См https://github.com/mybatis/mybatis-3/issues/549

Спасибо.