[Из песочницы] MyBatis как более быстрая альтернатива Hibernate

В Java сообществе Hibernate framework де-факто считается стандартом для удобной работы с базой данных. Разработчику трудно выбрать другой фреймфорк, потому что порой он не знает о существовании альтернатив. В этой статье я проведу курс молодого бойца по работе с MyBatis framework. Полностью охватить весь framework не получится, но информации будет достаточно, что бы увидеть преимущества и слабые стороны данного framework’а и начать работать с MyBatis.MyBatis не реализует JPA спеки, а является альтернативой JPA. Основное отличие MyBatis от Hibernate — это то как производится мапинг объектов. Hibernate мапит таблицы БД на сущности, давая нам доступ к данным. Для получения данных Hibernate генерирует SQL запросы, а генерируемые запросы хороши до поры — до времени, а потом они съедают кучу времени, становятся громоздкими и не управляемыми. MyBatis мапится не на таблицы, а на SQL запросы, за формирование запросов отвечает разработчик и только от него будет зависеть как быстро будет работать приложение.С преамбулой закончили, теперь можно перейти непосредственно к созданию небольшого проекта с использованием MyBatis, что бы познакомиться с ним поближе. Не буду оригинальным, сделаем пару запросов к БД с использованием MyBatis. В примере буду использовать СУБД MySQL, а вы можете использовать любую другую СУБД, которая вам по душе.

Создадим БД mybatis:

CREATE DATABASE `mybatis`; Создадим таблицы subscriber, tariff, payments: subscriber:

CREATE TABLE `mybatis`.`subscriber` ( `id` INT (10) NOT NULL, `name` VARCHAR (255) NOT NULL, `ref_tariff` VARCHAR (10) NOT NULL, PRIMARY KEY (`id`) ) ENGINE = MYISAM tariff: CREATE TABLE `mybatis`.`tariff` ( `id` INT (10) NOT NULL, `descr` VARCHAR (255) NOT NULL, PRIMARY KEY (`id`) ) ENGINE = MYISAM payments: CREATE TABLE `mybatis`.`payments` ( `id` INT (10) NOT NULL, `ref_subscriber` INT (10) NOT NULL, `summa` INT (10) NOT NULL, PRIMARY KEY (`id`) ) ENGINE = MYISAM Самое скучное позади — у нас есть БД, из которой мы будем получать данные, теперь приступим непосредственно к работе с MyBatis. Для начала нам необходима библиотека MyBatis. Для получения библиотеки мы будем использовать maven, необходимо добавить зависимость в настройки проекта (pom.xml): org.mybatis mybatis 3.2.8 На момент написания статьи последняя версия MyBatis 3.2.8После того как библиотека успешно загрузилась, необходимо настроить подключение к БД. Настройки осуществляются в конфигурационном файле mybatis-config.xml.

Ниже приведен листинг конфигурационного файла:

В листинге выше я указал 3 мапера — все взаимодействие с БД будет осуществляться через маперы и чем детальнее вы будете понимать как работать с маперами и формировать запросы, тем более производительней будут ваши приложения.Для корректной работы с MyBatis необходимо создать интерфейс мапера в котором будут предопределены методы, которые будут использоваться и xml файл настроек в котором будут описаны sql запросы, правила их мапинга на объекты и тп.

Создадим интерфейс kz.jazzsoft.mapper.SubscriberMapper:

package kz.jazzsoft.mapper;

import kz.jazzsoft.dal.Subscriber;

public interface SubscriberMapper {

Subscriber getSubscriberById (Integer id);

List getSubscriber ();

} В данном интерфейсе мы определили два метода:1. getSubscriberById — вернет одного пользователя по id;

2. getSubscriber — вернет список пользователей;

Но что бы данные методы заработали необходимо создать xml маппер с sql запросами.

Я упустил еще один момент, который необходимо было сделать — это создать классы beanEntity, на которые мы будем мапить результаты выполнения запросов.Subscriber:

package kz.jazzsoft.dal; import java.util.List; public class Subscriber { private Long id; private String name; private Tariff tariff; private List payments public Long getId () { return id; } public void setId (Long id) { this.id = id; } public String getName () { return name; } public void setName (String name) { this.name = name; } public Tariff getTariff () { return tariff; } public void setTariff (Tariff tariff) { this.tariff = tariff; } public List getPayments () { return paymentList; } public void setPayments (List payments) { this.payments = payments; } public List getConnections () { return connections; } } Tariff: package kz.jazzsoft.dal; public class Tariff { private Long id; private String descr; public Long getId () { return id; } public void setId (Long id) { this.id = id; } public String getDescr () { return descr; } public void setDescr (String descr) { this.descr = descr; } } Payment: package kz.jazzsoft.dal; public class Payment { private Long id; private Integer summa; public Long getId () { return id; } public void setId (Long id) { this.id = id; } public Integer getSumma () { return discount; } public void getSumma (Integer summa) { this.summa = summa; } } Можно сразу посмотреть, как работает код, для этого необходимо подключиться к БД и инициализировать нужный мапер, а пока он у нас один (SubscriberMapper). Создадим класс, в котором будем работать: Work:

package kz.jazzsoft; import kz.jazzsoft.mapper.SubscriberMapper; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; import java.io.Reader; public class Work { public static void main (String[] args) { SqlSessionFactory sqlSessionFactory; SubscriberMapper subscriberMapper; Reader reader = null; try { reader = Resources .getResourceAsReader («mybatis-config.xml»); //Читаем файл с настройками подключения и настройками MyBatis sqlSessionFactory = new SqlSessionFactoryBuilder ().build (reader); subscriberMapper = sqlSessionFactory.openSession ().getMapper (SubscriberMapper.class); //Создаем маппер, из которого и будем вызывать методы getSubscriberById и getSubscribers List subscribers = subscriberMapper.getSubscribers (); Subscriber subscriber = subscriberMapper.getSubscriberById (101); } catch (IOException e) { e.printStackTrace (); } } } Запросы выполнились и у нас есть объекты, с которыми мы можем работать. Вы можете посмотреть, что у объектов есть id и другие поля заполнены, но не все. Тут есть один нюанс, если колонка в БД имеет такое же имя как переменная, то она автоматически смапиться на нее. Что бы расширить возможности мапинга и создавать сложные структуры в арсенале MyBatis есть тег ResultMap, который позволяет настраивать произвольный мапинг. Делать связи one-to-one и one-to-many.ResultMap представляет из себя описание правил связи полей EntityBean с колонками из таблиц. Пример для Subscriber:

В итоге маппер для Subscriber будет выглядеть следующим образом: Связь one-to-one осуществляется не сложнее примера выше. Но нам сначала необходимо будет описать следующий мапер Tariff. Через него мы будем получать данные для связанного поля в Subscriber.Создаем сущность Tariff:

package kz.jazzsoft.dal; public class Tariff { private Long id; private String descr; public Long getId () { return id; } public void setId (Long id) { this.id = id; } public String getDescr () { return descr; } public void setDescr (String descr) { this.descr = descr; } } Создаем интерфейс мапера TariffMapper, нам понадобится только один метод: package kz.jazzsoft.mapper; import kz.jazzsoft.dal.Tariff; public interface TariffMapper { Tariff getTariffById (Integer id); } Создаем мапер: Теперь можно добавить Subscriber c Tariff в SubscriberMaper, в resultMap необходимо добавить правило связи: column=«ref_tariff» javaType=«kz.jazzsoft.dal.Tariff» select=«kz.jazzsoft .mapper.TariffMapper.getTariffById» fetchType=«eager» /> Необходимо заменить данный resultMap и можно будет узнать на каком тарифном (Tariff) плане у нас находится Абонент (Subscriber)Subscriber.gettariff ().getDescr ();

Добавим к Абоненту (Subscriber) список его платежей (Payments)(one-to-many):

Для начала необходимо создать EntityBean Payment:

package kz.jazzsoft.dal; public class Payment { private Long id; private Integer summa; public Long getId () { return id; } public void setId (Long id) { this.id = id; } public Integer getSumma () { return discount; } public void getSumma (Integer summa) { this.summa = summa; } } Теперь необходимо создать интерфейс мапера PaymentMapper, он будет простой. Только один метод получения списка платежей по id пользователя. package kz.jazzsoft.mapper; import kz.jazzsoft.dal.Payment; import java.util.List; public interface PaymentMapper { List getPaymentsByIdSub (Integer id); } Необходимо создать xml мапер: Свяжем список платежей (payment) с абонентом (Subscriber):

column=«id» javaType=«List» ofType=«Payment» select=«kz.jazzsoft.mapper.PaymentMapper.getPaymentsByIdSub» fetchType=«eager» /> Полученный resultMap заменяем в SubscriberMapper и можно посмотреть все платежи пользователя. Но на этом все самое интересное только начинается.MyBatis имеет функционал, который позволяет формировать sql запросы динамически в зависимости от параметров, которые были в него переданы. Например нам нет необходимости создавать кучу sql на каждое действие (выборки из одной таблицы, но по разным параметрам), можно отделаться одним методом, который будет фильтровать тех же абонентов по нескольким колонкам или вообще не будет фильтровать и вернет всех в зависимости от входных данных, но обо всем по порядку.

Для динамического формирования SQL запросов в арсенале MyBatis имеется достаточно компонентов для решения большинства задач. Рассматривать все мы не будем, так как их достаточно много и их можно комбинировать и тп. Для примера расмотрим IF оператор, больше информации можно прочитать в официальном руководстве: mybatis.github.io/mybatis-3/dynamic-sql.html

IF Оператор:

В запросе приведенном выше выполняется проверка, на то что объект в Map по ключу descr не null, тогда в запрос будет добавлена строка в блоке if и таких блоков может быть сколько угодно, они могу быть вложенными. udpate subscriber descr = #{descr}, where id = #{id} MyBatis при разумном использовании может дать ощутимый прирост в скорости работы приложения. Может показаться страшно писать самому запросы и правила мапинга, но это только кажется, Hibirnate тоже не так уж прост.

Не существует одного универсального решения, которое подошло бы всем, в каждом выборе нужен четкий расчет. MyBatis можно использовать совместно с Hibernate там где это действительно нужно, а это сможете определить только вы.

© Habrahabr.ru