Appreciation of common persistence layer frameworks, what makes you choose MyBatis?

In most online application scenarios, data is stored in relational databases. Of course, in scenarios with special requirements, we will also use other persistent storage (such as ElasticSearch, HBase, MongoDB, etc.) as auxiliary storage. But it is undeniable that with decades of development, ecological accumulation, and many successful cases, relational databases are still the core storage of Internet companies.

As a Java developer, you deal with relational databases almost every day. Commonly used relational database products in the production environment include SQL Server, MySQL, Oracle, etc. When using these database products, the basic idea is as follows:

In the process of writing Java code, object-oriented thinking is used to implement business logic;

When designing database tables, consider the first normal form, second normal form and third normal form;

When operating database records, use SQL statements and collective thinking to consider the preparation of table joins, conditional statements, sub-queries, etc.

At this time, a bridge is needed to map Java classes (or other data structures) with tables in relational databases, as well as Java objects and data in tables to realize the interaction between Java programs and the database.

JDBC (Java DataBase Connectivity) is a unified API for interaction between Java programs and relational databases. In fact, JDBC is composed of two parts of API: The first part is the Java API for Java developers, which is a unified and standard Java API, independent of the interface specifications of each database product; the second part is for database driver development The API of the author, which is driven by the database provided by various database manufacturers, is the underlying implementation of the first part of the interface specification, and is used to connect to specific database products.

In the actual development of Java programs, we can connect to the database through JDBC and complete various database operations, such as CRUD data and executing DDL statements. Here we take the execution of a Select query statement in JDBC programming as an example to illustrate the core steps of JDBC operation, as follows:

Register the database driver class, specify the database address, including the user name, password and other connection information of the DB;

Call the DriverManager.getConnection() method to create a Connection to connect to the database;

Call Connection's createStatement() or prepareStatement() method to create a Statement object, at this time SQL (or SQL statement template + SQL parameters) will be specified;

Execute the SQL statement through the Statement object to get the ResultSet object, which is the query result set;

Traverse the ResultSet, read data from the result set, and convert each row of database records into a JavaBean object;

Close the ResultSet result set, Statement object, and database Connection, thereby releasing the underlying resources occupied by these objects.

Whether it is to perform a query operation or perform other DML operations, the steps 1, 2, 3, 4, and 6 will be repeated. In order to simplify the repetitive logic and improve the maintainability of the code, the above repetitive logic can be encapsulated in a tool class similar to DBUtils, and only the methods in the DBUtils tool class need to be called when in use. Of course, we can also use the "reflection + configuration" method to encapsulate the conversion from the relational model to the object model in step 5, but this encapsulation requires a certain degree of programming skills to be generalized and flexible.

In order to deal with the above-mentioned code duplication problem and subsequent maintenance problems, we will conduct a series of evaluations in practice, and choose an ORM (Object Relational Mapping) framework that suits the needs of the project and meets the capabilities of the personnel to encapsulate 1~ The 6-step repetitive code realizes the conversion between the object model and the relational model. This is exactly the core function of the ORM framework: according to the configuration (configuration file or annotation) to realize the non-perceptual mapping between the object model and the relational model (as shown in the figure below).
Insert picture description here
In a production environment, databases are generally scarce, and database connections are also one of the more precious resources in the entire service. Establishing a database connection involves a series of network operations such as authentication and handshake, which is a relatively time-consuming operation. Therefore, we cannot directly release the database connection like the above JDBC basic operation process, otherwise the persistence layer will easily become the performance bottleneck of the entire system.

Java programmers generally use database connection pooling for optimization. At this time, you need to introduce a third-party connection pool implementation. Of course, you can also develop a connection pool by yourself, but you need to deal with the number of active connections and control the status of the connection. The operation is still somewhat difficult. In addition, some of the data returned by the query needs to be cached locally, which can improve the query performance of the entire program, which requires the support of the cache.

If there is no ORM framework, this requires that our Java developers are familiar with the APIs of related connection pools, caches and other components and manually write some "glue" codes to complete the integration, and these codes are highly repetitive, which is obviously not what we want to see The result.

Many ORM frameworks support the integration of common components such as third-party caches and third-party data sources, and provide a unified configuration access method, so that we only need to use simple configurations to complete the integration of third-party components. When we need to replace a third-party component, we only need to introduce related dependencies and update the configuration, which greatly improves the development efficiency and the maintainability of the entire system.

Below we briefly introduce several ORM frameworks commonly used in practice.

Hibernate
Hibernate is one of the well-known ORM frameworks in the Java ecosystem. Hibernate is now also expanding its ecology, beginning to support the persistence of a variety of heterogeneous data, not only provides an ORM framework, but also provides Hibernate Search to support full-text search, validation to perform data verification, and Hibernate OGM to support NoSQL solution.

What we want to focus on here is the related content of Hibernate ORM. As of the end of 2020, the latest version of Hibernate ORM is version 5.4, and version 6.0 is still under development. As a veteran ORM framework, Hibernate has withstood the test of Java EE enterprise-level applications and once became the framework of choice in the Java ORM field.

When using Hibernate, Java development can use mapping files or annotations to define various mapping relationships between classes in the Java language and tables in the database. The mapping file suffix used here is ".hbm.xml". After the hbm.xml mapping file associates a database table with a Java class, each row of the database table can be converted into a corresponding Java object. It is precisely because of the existence of Hibernate mapping that Java development only needs to use object-oriented thinking to complete the design of database tables.

In a pure object-oriented language like Java, there may be one-to-one, one-to-many, or many-to-many complex relationships between two Java objects. The mapping file in Hibernate must also be able to express this complex association relationship to meet our needs. At the same time, it must also be able to carry out this association relationship with the concepts in a series of relationship models such as association tables and foreign keys in the database. Mapping, which is the "association mapping" often mentioned in the ORM framework.

Below we will introduce the "one-to-many" relationship with an example. For example, a customer (Customer) can create multiple orders (Order), and an order (Order) belongs to only one customer (Customer), there is a one-to-many relationship between the two. In a Java program, you can add a List type field to the Customer class to maintain this one-to-many relationship; in the database, you can add a customer_id column in the order table (t_order) as a foreign key to point to the customer table ( t_customer) to maintain this one-to-many relationship, as shown in the following figure: Insert picture description here
one-to-many in the relational model and one-to-many in the object model

In Hibernate, these two relationships can be mapped through the following Customer.hbm.xml configuration file:

<hibernate-mapping>
    <!-- 这里指定了Customer类与t_customer表之间的映射 -->
    <class name="com.mybatis.test.Customer" table="t_customer">
        <!-- Customer类中的id属性与t_customer表中主键id之间的映射 -->
        <id name="id" column="id"/>
        <!-- Customer类中的name属性与t_customer表中name字段之间的映射 -->
        <property name="name" column="name"/>
        <!-- Customer指定了Order与Customer 一对多的映射关系 -->
        <set name="orders" cascade="save,update,delete">
            <key column="customer_id"/>
            <one-to-many class="com.mybatis.test.Order"/>
        </set>
    </class>
</hibernate-mapping>

If it is a two-way association, in the Java code, you can directly add a field of type Customer in the Order class to point to the associated Customer object, and configure the following in the corresponding Order.hbm.xml configuration file:

<hibernate-mapping>
    <!-- 这里指定了Order类与t_order表之间的映射 -->
    <class name="com.mybatis.test.Order" table="t_order">
        <!-- Order类中的id属性与t_order表中主键id之间的映射 -->
        <id name="id" column="id"/>
        <!-- Order类中的address属性与t_order表中address列之间的映射 -->
        <property name="address" column="address"/>
        <!-- Order类中的tele属性与t_order表中tele列之间的映射 -->
        <property name="tele" column="tele"/>
        <!-- Order类中customer属性与t_order表中customer_id之间的映射,
             同时也指定Order与Customer之间的多对一的关系 -->
        <many-to-one name="customer" column="customer_id"></many-to-one>
    </class>
</hibernate-mapping>

One-to-one, many-to-many and other association mappings are defined in the Hibernate mapping file with corresponding XML tags. The principle is basically the same as that of "one-to-many", but the usage and scenarios are slightly different.

In addition to being able to complete the mapping between the object-oriented model and the relational model in the database, Hibernate can also help us shield the differences in SQL statements in different database products.

We know that although there is currently a SQL standard, different relational database products support the SQL standard slightly, which will cause some very embarrassing situations. For example, a SQL statement can be executed normally on MySQL, but on Oracle An error will be reported when executed on the database.

Hibernate encapsulates all operations at the database level. Java programmers no longer need to write SQL statements directly, but only need to use the API provided by Hibernate to complete database operations.

For example, the Criteria provided by Hibernate for users is a set of flexible and extensible data manipulation APIs. The most important thing is that Criteria is a set of object-oriented APIs. When using it to manipulate databases, Java developers only need to pay attention to the Criteria set. API and returned Java objects do not need to consider how the underlying database is implemented, how SQL statements are written, and so on.

Here is a simple example of the Criteria API:

// 创建Criteria对象,用来查询Customer对象

Criteria criteria = session.createCriteria(Customer.class, "u");

//查询出id大于0,且名字中以yang开头的顾客数据

List<Customer> list = criteria.add(Restrictions.like("name","yang%"))

                            .add(Restrictions.gt("id", 0))

                            .list();

In addition to the Criteria API, Hibernate also provides a set of object-oriented query language-HQL (Hibernate Query Language). From the point of view of the structure of the statement, the HQL statement is very similar to the SQL statement, but there are also differences between the two: HQL is an object-oriented query language, and SQL is a relation-oriented query language.

When implementing complex database operations, we can use object-oriented query statements such as HQL. Hibernate's HQL engine will convert HQL statements into legal SQL statements based on the underlying database product used.

Through its concise API and unified HQL statement, Hibernate helps the upper-level program to shield the difference of the underlying database and enhance the portability of the program.

In addition, Hibernate has some other advantages as follows:

The Hibernate API itself is not intrusive, that is, the business logic does not perceive the existence of Hibernate, nor does it need to inherit any interface in the Hibernate package;

Hibernate provides first-level cache and second-level cache by default (the first-level cache is enabled by default, and the second-level cache needs to be configured to be enabled). These two levels of cache can reduce the query pressure of the database and improve the performance of the service;

Hibernate provides the function of lazy loading, which can avoid invalid queries;

Hibernate also provides the reverse operation of automatically generating database tables from the object model.

But it should be noted that Hibernate is not a "silver bullet". We cannot find the mapping of all concepts in the database in the object-oriented model, such as indexes, functions, stored procedures, etc. While enjoying the convenience brought by Hibernate, we also need to endure some of its shortcomings. For example, indexes are very helpful to improve database query performance. We build indexes and appropriately optimize SQL statements, so that the database will use appropriate indexes to increase the speed of the entire query. However, it is difficult for us to modify the SQL statements generated by Hibernate. Why do you say that? Because in some scenarios, the database design is very complicated, and the relationship between the table and the table is intricate, the SQL statement generated by the Hibernate engine will be very difficult to understand, and it is even more difficult to make the generated SQL statement use the correct index. It is easy to generate slow query SQL.

In addition, in some scenarios with large data volume, high concurrency, and low latency, the performance loss of Hibernate will gradually become apparent.

Spring Data JPA
Before we start to introduce Spring Data JPA, we must first introduce the JPA (Java Persistence API) specification.

JPA is the Java Persistence Specification (JSR 338) proposed after JDK 5.0. The JPA specification itself is to integrate the existing ORM frameworks on the market, end the fragmentation of ORM frameworks such as Hibernate, EclipseLink, and JDO, and simplify the development of the Java persistence layer.

The JPA specification borrows many advantages from the existing ORM framework. For example, Gavin King, as the founder of Hibernate, also participated in the writing of the JPA specification, so many concepts and designs similar to Hibernate can be seen in the JPA specification.

Since JPA is a persistence specification and does not provide a specific persistence implementation, who will provide the implementation? The answer is that ORM frameworks on the market, for example, Hibernate, EclipseLink, etc. all provide specific implementations that comply with the JPA specification, as shown in the following figure:
Insert picture description here
JPA has three core parts: ORM mapping metadata, operation entity object API and object-oriented query language (JPQL). This is basically similar to the core function of Hibernate, so I won't repeat it.

Java developers should all know the power of "Spring Family Bucket". Spring has now become the de facto standard. Few companies will completely leave Spring to develop Java programs. Now Spring is not just the earliest IoC container, but the entire Spring ecosystem, such as Spring Cloud, Spring Boot, Spring Security, etc., which includes Spring Data.

Spring Data is a series of extensions and integrations made by Spring in terms of persistence. The following figure shows the sub-projects in
Insert picture description here
Spring Data : Each sub-project in Spring Data corresponds to a persistent storage, which can be accessed through continuous integration. With the ability of persistent storage, Spring's ecology has taken a big step forward, and the most commonly used one should be Spring Data JPA.

Spring Data JPA is an implementation of a Repository layer that conforms to the JPA specification, and its location is shown in the following figure:
Insert picture description here
Although most ORM frameworks on the market implement the JPA specification, they also have their own development and modification on the basis of JPA As a result, when we use JPA, we still cannot seamlessly switch the underlying ORM framework implementation. When using Spring Data JPA, because Spring Data JPA helps us smooth out the differences between ORM frameworks, we can allow our upper-level business to seamlessly switch ORM implementation frameworks.
MyBatis
combines the knowledge points of the above two ORM frameworks, let's introduce the protagonist-MyBatis.
The iBatis project in the Apache Foundation is the predecessor of MyBatis. Due to various reasons, the iBatis project did not develop well in the Apache Foundation, and finally broke away from Apache in 2010 and was renamed MyBatis. Three years later, in 2013, MyBatis migrated the source code to GitHub .

An important function of MyBatis is to help Java develop and encapsulate repetitive JDBC code, which is the same as the Spring Data JPA, Hibernate and other ORM frameworks analyzed in the previous article. The way MyBatis encapsulates repetitive code is to map ResultSet results to Java objects through Mapper mapping configuration files and related solutions. Other mapping rules and necessary sub-queries can be nested in specific mapping rules, so that complex mapping can be easily implemented The logic of, of course, can also realize one-to-one, one-to-many, many-to-many relationship mapping and corresponding two-way relationship mapping.

Many people will compare Hibernate with MyBatis and think that Hibernate is a fully automatic ORM framework, while MyBatis is just a semi-automatic ORM framework or a SQL template engine. In fact, none of these comparisons can fully explain that one framework is more advanced than the other. The key is to look at the application scenarios.

Compared with Hibernate and various JPA implementation frameworks, MyBatis is more flexible, lighter and more controllable.

We can directly write native SQL statements in the Mapper mapping file of MyBatis and apply the dialect of the underlying database product, which gives us the opportunity to directly optimize SQL statements;

We can also follow the database usage rules and let the native SQL statement select the index we expect to ensure the performance of the service. This is particularly suitable for scenarios where SQL needs to be optimized to the extreme, such as large data volume and high concurrency;

When writing native SQL statements, we can also control the columns in the result set more conveniently, instead of querying all columns and mapping objects and returning them. This can also have a certain optimization effect when there are more columns. (Of course, Hibernate can also achieve this effect, you need to add the corresponding construction method in the entity class.)

In actual business, the query conditions for the same data set may change dynamically. If you have experience using JDBC or other similar frameworks, you should be able to realize that splicing SQL statement strings is a very troublesome thing, especially the conditions In complex scenes, special care must be taken during the splicing process. Make sure to add keywords such as "where", "and", "in" and other SQL statements as well as separators such as spaces, commas, and equal signs at the appropriate locations, and the splicing process is very boring , Without technical content, an executable SQL statement may be obtained after repeated debugging.

MyBatis provides powerful dynamic SQL functions to help our developers get rid of this kind of duplication of labor. We only need to write dynamic SQL statements in the mapping configuration file, and MyBatis can piece together a complete, complete set of parameters based on the actual parameter values ​​passed in during execution. Executable SQL statement.

Summary
I focused on three common ORM persistence frameworks. How should we choose the appropriate persistence framework in actual work?

From a performance perspective, Hibernate and Spring Data JPA are not as convenient and efficient as MyBatis in terms of controlling SQL statements, manual SQL tuning, and multi-table join queries.

From the perspective of portability, Hibernate helps us shield the underlying database dialect, Spring Data JPA helps us shield the ORM differences, and MyBatis directly writes native SQL and is fully bound to the specific database (but rarely in practice Some projects will switch back and forth the underlying database product or ORM framework, so this is not particularly important);

From the perspective of development efficiency, Hibernate and Spring Data JPA are slightly more efficient in handling small and medium-sized projects than MyBatis (this mainly depends on the requirements and the developer's technology stack).

In addition to these three aspects, there are many other aspects that need to be considered in the technical selection, such as estimated performance indicators such as QPS and P99, and so on. When selecting technology, we must also consider more aspects in order to choose the most suitable solution.

In addition to the three aspects mentioned above, what other content should be considered in the technology selection? You are welcome to leave a message in the comment area to share and communicate with me.

Guess you like

Origin blog.csdn.net/Rinvay_Cui/article/details/113833016