How to monitor SQL and ORM frameworks using MyBatis
As we have seen, Hibernate simplifies the persistent layer. However, the developer must understand what’s going on under the hood to optimize the default Framework’s behavior. On the other hand, there is another popular Java Framework that doesn’t do a lot of “black magic” under the hood, namely MyBatis. According to documentation
MyBatis is a first class persistence framework with support for custom SQL, stored procedures and advanced mappings
The developers who use MyBatis have full control over the SQL queries using a user-friendly XML or an annotation-based API (with an introduction of multiline Strings in java 14, the Annotation API should be preferred). Let’s rewrite the previous example using MyBatis. First, we need to eliminate all JPA-specific annotations from Entity classes. Next, we have to write SQL on our own using XML files. For this example, we will rewrite the Repository class that saves the user with corresponding orders (Code examples are available here). Here is the Mapper class:
@Mapper public interface UserMapper void saveUser(User user); void saveOrders(@Param("orders") List<Order> orders, @Param("userId") long userId); default void saveUserWithOrders(User user) this.saveUser(user); this.saveOrders(user.getOrders(), user.getId());
and here is the corresponding XML file:
<mapper namespace="com.fusion.reactor.UserMapper"> <resultMap id="UserResultMap" type="com.fusion.reactor.User"> <id column="id" property="id"/> <result column="name" property="name"/> </resultMap> <insert id="saveUser" parameterType="com.fusion.reactor.User" useGeneratedKeys="true" keyProperty="id" keyColumn="id"> INSERT INTO users (name) VALUES (#name) </insert> <insert id="saveOrders" parameterType="map"> INSERT INTO orders ( price,user_id,name ) VALUES <foreach collection="orders" item="order" index="index" open="(" separator="),(" close=")"> #order.price, #userId, #order.name </foreach> </insert> </mapper>
As you can see, we explicitly wrote two queries, first to save the user and second to save a list of orders. No autogenerated queries and no automapping as in Hibernate, and everything has to be done manually. Let’s rerun load tests and see the JDBC transaction history.
FusionReactor will show you that MyBatis generated queries that you specified with all the parameters displayed, so you can verify that mappers are working as expected Going to the Relations page in transaction detail for one of the requests, we can see that a single request used 1 transaction, and 2 JDBC queries:
The MyBatis plugin for IntelliJ has language server support to write type-safe XML files. However, if you want compile-time support there is another framework to consider.