📜 ⬆️ ⬇️

MyBatis as a faster alternative to Hibernate

In the Java community, the Hibernate framework is de facto considered to be the standard for convenient work with the database. It is difficult for a developer to choose another framework, because sometimes he does not know about the existence of alternatives. In this article, I will conduct a course for a young fighter on working with the MyBatis framework. It will not be possible to fully cover the whole framework, but the information will be enough to see the advantages and weaknesses of this framework and start working with MyBatis.

MyBatis does not implement JPA specs, but is an alternative to JPA. The main difference between MyBatis and Hibernate is how the objects are mapped. Hibernate masps database tables on entities, giving us access to data. To get data, Hibernate generates SQL queries, and the generated queries are good for the time being - for a while, and then they eat up a lot of time, become cumbersome and not manageable. MyBatis is mapped not on tables, but on SQL queries, the developer is responsible for generating queries, and it will only depend on him how quickly the application will run.

With the preamble finished, you can now go directly to creating a small project using MyBatis, to get to know him better. I will not be original, we will make a couple of queries to the database using MyBatis. In the example I will use the MySQL DBMS, and you can use any other DBMS that you like.

Create the mybatis database:
')
CREATE DATABASE `mybatis`; 

Create 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 

The most boring thing behind us is that we have a database from which we will receive data, now we will start working directly with MyBatis. First we need the MyBatis library. To get the library we will use maven, you need to add a dependency to the project settings (pom.xml):

 <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.2.8</version> </dependency> 

At the time of this writing, the latest version of MyBatis 3.2.8

After the library has successfully loaded, you need to configure the connection to the database. Settings are made in the configuration file mybatis-config.xml.

Below is the listing of the configuration file:

 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <properties resource="config.properties"> <!--    (  , ,   .)--> </properties> <settings><!--      .    http://mybatis.imtqy.com/mybatis-3/configuration.html#settings--> <setting name="logImpl" value="LOG4J"/> </settings> <environments default="development"><!--      --> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${db.driver}"/> <property name="url" value="${db.url}"/> <property name="username" value="${db.username}"/> <property name="password" value="${db.password}"/> </dataSource> </environment> </environments> <mappers><!--     ,    --> <mapper class="kz.jazzsoft.mapper.SubscriberMapper"/> <mapper class="kz.jazzsoft.mapper.TariffMapper"/> <mapper class="kz.jazzsoft.mapper.PaymentMapper"/> </mappers> </configuration> 

In the listing above, I indicated 3 mappers - all interaction with the database will be done through mappers, and the more detailed you will understand how to work with mappers and form queries, the more productive your applications will be.

To work correctly with MyBatis, you need to create a mapera interface in which the methods will be predefined, and the xml settings file will be defined in which sql queries will be described, the rules for their mapping to objects and so on.

Create the kz.jazzsoft.mapper.SubscriberMapper interface:

 package kz.jazzsoft.mapper; import kz.jazzsoft.dal.Subscriber; public interface SubscriberMapper { Subscriber getSubscriberById(Integer id); List getSubscriber(); } 

In this interface, we have defined two methods:

1. getSubscriberById - returns one user by id;

2. getSubscriber - returns a list of users;

But in order for these methods to work, you need to create an xml mapper with sql queries.

 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="kz.jazzsoft.mapper.SubscriberMapper"> <select id="getSubscriberById" <!-- --> parameterType="java.lang.Integer" <!--  ,    ,   Map   EntityBean.--> > select * from subscriber where id = #{id} <!--      ,    .   Map —  {}    .     EntityBean  {} —     bean.--> </select> <select id="getSubscriber"> select * from subscriber </select> </mapper> 

I missed another point that needed to be done - this is to create beanEntity classes, to which we will load the results of query execution.

Subscriber:

 package kz.jazzsoft.dal; import java.util.List; public class Subscriber { private Long id; private String name; private Tariff tariff; private List<Payment> 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<Payment> getPayments() { return paymentList; } public void setPayments(List<Payment> payments) { this.payments = payments; } public List<Connection> 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; } } 

You can immediately see how the code works, for this you need to connect to the database and initialize the necessary maper, but for now we have one (SubscriberMapper). Create a class in which we will work:

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<Subscriber> subscribers = subscriberMapper.getSubscribers(); Subscriber subscriber = subscriberMapper.getSubscriberById(101); } catch (IOException e) { e.printStackTrace(); } } } 

Requests were fulfilled and we have objects with which we can work. You can see that objects have id and other fields are filled, but not all. There is one nuance here, if a column in the database has the same name as a variable, then it will automatically be dumped on it. To expand the possibilities of mapping and create complex structures in the arsenal of MyBatis there is a ResultMap tag that allows you to customize a custom mapping. Make one-to-one and one-to-many links.

ResultMap is a description of the rules for connecting EntityBean fields with columns from tables. Example for Subscriber:

 <resultMap id="subscriber" type="kz.jazzsoft.dal.Subscriber"> <id property="id" column="id"/> <result property="name" column="name"/> <!--   name  Subscriber   ,    property--> </resultMap> 

As a result, the mapper for Subscriber will look like this:

 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="kz.jazzsoft.mapper.SubscriberMapper"> <resultMap id="subscriber" type="kz.jazzsoft.dal.Subscriber"> <id property="id" column="id"/> <result property="name" column="name"/> </resultMap> <select id="getSubscriberById" parameterType="java.lang.Integer" resultMap="subscriber"> <!--   ResultMap      --> select * from subscriber where id = #{id} </select> <select id="getSubscribers" resultMap="tariff"> select * from subscriber </select> </mapper> 

The one-to-one connection is as simple as the example above. But we will first need to describe the next mapper Tariff. Through it, we will receive data for the associated field in Subscriber.

Create Tariff entity:

 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; } } 

We create the TariffMapper interface; we need only one method:

 package kz.jazzsoft.mapper; import kz.jazzsoft.dal.Tariff; public interface TariffMapper { Tariff getTariffById(Integer id); } 

Create a maper:

 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="kz.jazzsoft.mapper.TariffMapper"> <resultMap id="tariff" type="kz.jazzsoft.dal.Tariff"> <id property="id" column="id"/> <result property="descr" column="descr"/> </resultMap> <select id="getTariffById" resultMap="tariff" parameterType="java.lang.Integer"> select * from tariff where id = #{id} </select> </mapper> 

Now you can add Subscriber c Tariff to SubscriberMaper, you need to add a connection rule to resultMap:

 <resultMap id="subscriber" type="kz.jazzsoft.dal.Subscriber"> <id property="id" column="id"/> <result property="name" column="name"/> <association property="tariff" <!--  Subscriber --> column="ref_tariff" <!--    Subscriber,          --> javaType="kz.jazzsoft.dal.Tariff" <!-- ,     --> select="kz.jazzsoft .mapper.TariffMapper.getTariffById" <!--       ,   sql      .--> fetchType="eager" <!--  --> /> </resultMap> 

It is necessary to replace this resultMap and it will be possible to find out at which tariff (Tariff) plan we have a Subscriber (Subscriber)

Subscriber.gettariff (). GetDescr ();

Add to Subscriber a list of his payments (Payments) (one-to-many):

First you need to create an 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; } } 

Now you need to create the PaymentMapper interface, it will be simple. Only one method to get the list of payments by user id

 package kz.jazzsoft.mapper; import kz.jazzsoft.dal.Payment; import java.util.List; public interface PaymentMapper { List<Payment> getPaymentsByIdSub(Integer id); } 

You must create an xml maper:

 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="kz.jazzsoft.mapper.PaymentMapper"> <resultMap id="payment" type="kz.jazzsoft.dal.Payment"> <id property="id" column="id"/> <result property="summa" column="date"/> </resultMap> <select id="getPaymentsByIdSub" resultMap="payment" parameterType="java.lang.Integer"> select * from payment where ref_subscriber = #{id} </select> </mapper>    (payment)   (Subscriber): <source lang="xml"> <resultMap id="subscriber" type="kz.jazzsoft.dal.Subscriber"> <id property="id" column="id"/> <result property="name" column="name"/> <association property="tariff" column="ref_tariff" javaType="kz.jazzsoft.dal.Tariff" select="kz.jazzsoft.mapper.TariffMapper.getTariffById" fetchType="eager"/> <collection property="payments" <!--  bean Subscriber--> column="id" <!--id Subscriber--> javaType="List" <!--    ()--> ofType="Payment" <!--     --> select="kz.jazzsoft.mapper.PaymentMapper.getPaymentsByIdSub" <!-- ,   ,       id --> fetchType="eager" <!-- --> /> </resultMap> 

The resulting resultMap is replaced in SubscriberMapper and you can see all user payments. But on this all the fun is just beginning.

MyBatis has a functionality that allows you to generate sql queries dynamically depending on the parameters that were passed to it. For example, we do not need to create a bunch of sql for each action (samples from one table, but by different parameters), we can get rid of one method, which will filter the same subscribers by several columns or will not filter at all and return all depending on the input data, But first things first.

For the dynamic formation of SQL queries in the arsenal of MyBatis there are enough components for most tasks. We will not consider all of them, as there are many of them and they can be combined and so on. For example, consider the IF operator, more information can be found in the official manual: mybatis.imtqy.com/mybatis-3/dynamic-sql.html

IF Operator:

 <select id="getSubscribersWithParam" parameterType="map"> select * from subscriber where (1=1) <if test="descr != null" > and decr = #{descr} </if> </select> 

In the query above, a check is performed that the object in the Map using the descr key is not null, then a line will be added to the query in the if block and there can be as many such blocks as they can be nested.

 <update id="updateSubscriber" parameterType="kz.jazzsoft.dal.Subscriber"> udpate subscriber <set> <if test="descr != null"> descr = #{descr}, </if> </set> where id = #{id} </update> 


MyBatis with reasonable use can give a significant increase in the speed of the application. It may seem scary to write queries and rules for mapping yourself, but it only seems that Hibirnate is not that simple either.

There is no one universal solution that would suit everyone, every choice needs a clear calculation. MyBatis can be used with Hibernate where you really need it, and only you can determine this.

Source: https://habr.com/ru/post/247885/


All Articles