First, briefly explain the use of MyBatis, and then focus on several core principles of MyBatis.
MyBatis easy to use
MyBatis can be divided into the following key points
- Introducing MyBatis dependency
- Configure mybatis config XML configuration file
- Create database entity class and Mapper mapping file
- Load the configuration through SqlSessionFactoryBuilder and use
Write a unit test as follows:
- Introduce dependency
<!-- mybatis Dependent package --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.4</version> </dependency> <!-- The database driver needs to use 5.1.40 The above can be solved mysql json Format garbled problem --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.40</version> </dependency> <!-- lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.6</version> </dependency>
- Write configuration file
mybatis-config.xml
<?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="db.properties"></properties> <settings> <!-- Print query statement --> <setting name="logImpl" value="STDOUT_LOGGING" /> <!-- Control global cache (L2 cache), default true--> <setting name="cacheEnabled" value="false"/> <!-- Global switch for delayed loading. When turned on, all associated objects are loaded late. default false --> <setting name="lazyLoadingEnabled" value="false"/> <!-- When on, any method call will load all the properties of the object. default falseļ¼Can pass select Tagged fetchType To cover--> <setting name="aggressiveLazyLoading" value="true"/> <!-- Mybatis The proxy tool used to create objects with delayed loading capability. The default value is JAVASSIST --> <!--<setting name="proxyFactory" value="CGLIB" />--> <!-- STATEMENT Level cache, so that the level-1 cache is only for the current execution statement Effective, equivalent to closing L1 cache --> <!-- <setting name="localCacheScope" value="STATEMENT"/> --> <setting name="localCacheScope" value="SESSION"/> </settings> <typeAliases> <typeAlias alias="user" type="com.freecloud.plug.mybatis.entity.User" /> <typeAlias alias="myBatisJson" type="com.freecloud.plug.mybatis.entity.MyBatisJson" /> <typeAlias alias="department" type="com.freecloud.plug.mybatis.entity.Department" /> <typeAlias alias="userAndDepartment" type="com.freecloud.plug.mybatis.entity.UserAndDepartment" ></typeAlias> </typeAliases> <!-- Custom type converter --> <typeHandlers> <typeHandler handler="com.freecloud.plug.mybatis.type.JsonTypeHandler"></typeHandler> </typeHandlers> <plugins> <plugin interceptor="com.freecloud.plug.mybatis.plugins.SQLExecuteTimeInterceptor"> <property name="name" value="zhangsan" /> </plugin> <plugin interceptor="com.freecloud.plug.mybatis.plugins.SimpleTableInterceptor"></plugin> </plugins> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/><!-- Configuration when used alone MANAGED No transaction --> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource> </environment> </environments> <mappers> <mapper resource="mapper/UserMapper.xml"/> <mapper resource="mapper/MyBatisJsonMapper.xml"/> <mapper resource="mapper/DepartmentMapper.xml"/> </mappers> </configuration>
db.properties database connection configuration
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://127.0.0.1:3306/data_test?useUnicode=true&characterEncoding=utf-8&rewriteBatchedStatements=true jdbc.username=root jdbc.password=123456
Initialization data, used for unit testing later.
## Department table CREATE TABLE `department` ( `id` int(5) NOT NULL, `name` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL, `parent_id` int(5) NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; ## Employee table CREATE TABLE `user_department` ( `id` int(5) NOT NULL, `name` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL, `age` int(4) DEFAULT NULL, `default_department_id` int(5) NOT NULL, `all_department_id` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; ## Initialize Department insert into department values (1,'Department 1',null); insert into department values (2,'Sector 2',1); insert into department values (3,'Sector 3',null); ## Initialize employee insert into user_department values (1,'Zhang San',18,1,'2,3'); insert into user_department values (2,'Li 4',18,2,'1,2'); insert into user_department values (3,'Wang 5',18,3,'3'); insert into user_department values (4,'Zhao 6',18,1,'1');
- Database entity class and mapper configuration
/** * Business entity object */ @Data @NoArgsConstructor @AllArgsConstructor public class User implements Serializable { /** Primary key ID */ private Long id; /** full name */ private String name; /** Age */ private Integer age; }
UserMapper.xml Mapping File
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.freecloud.plug.mybatis.dao.UserMapper"> <!-- Declare this namespace Use L2 cache --> <!-- <cache/>--> <!-- use Redis As L2 cache --> <!-- <cache type="org.mybatis.caches.redis.RedisCache" eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/> --> <cache type="org.apache.ibatis.cache.impl.PerpetualCache" size="1024" eviction="LRU" flushInterval="120000" readOnly="false"/> <resultMap id="BaseResultMap" type="user"> <id column="id" property="id" jdbcType="INTEGER"/> <result column="name" property="name" jdbcType="VARCHAR"/> <result column="age" property="age" jdbcType="INTEGER"/> </resultMap> <!-- Object association query, one sql Direct query --> <resultMap id="UserAndDepartmentResultMap" type="userAndDepartment"> <id column="id" property="id" jdbcType="INTEGER"/> <result column="name" property="name" jdbcType="VARCHAR"/> <result column="age" property="age" jdbcType="INTEGER"/> <association property="defaultDepartment" javaType="com.freecloud.plug.mybatis.entity.Department" > <id column="department_id" property="id"></id> <result column="department_name" property="name" ></result> </association> </resultMap> <!-- Joint query will generate N+1 Question of --> <resultMap id="UserAndDepartmentResultMap1" type="userAndDepartment"> <id column="id" property="id" jdbcType="INTEGER"/> <result column="name" property="name" jdbcType="VARCHAR"/> <result column="age" property="age" jdbcType="INTEGER"/> <!-- You can boast here namespace Call, but ensure that the current application loads the corresponding mapper --> <association property="defaultDepartment" column="default_department_id" javaType="com.freecloud.plug.mybatis.entity.Department" select="com.freecloud.plug.mybatis.dao.DepartmentMapper.byId" ></association> </resultMap> <!-- Joint query, one to many --> <resultMap id="UserAndDepartmentResultMap2" type="userAndDepartment"> <id column="id" property="id" jdbcType="INTEGER"/> <result column="name" property="name" jdbcType="VARCHAR"/> <result column="age" property="age" jdbcType="INTEGER"/> <!-- You can boast here namespace Call, but ensure that the current application loads the corresponding mapper --> <!-- <association property="defaultDepartment" column="default_department_id"--> <!-- javaType="com.freecloud.plug.mybatis.entity.Department"--> <!-- select="com.freecloud.plug.mybatis.dao.DepartmentMapper.byId" ></association>--> <collection property="departmentList" column="all_department_id" ofType="com.freecloud.plug.mybatis.entity.Department" select="com.freecloud.plug.mybatis.dao.DepartmentMapper.byIds"></collection> </resultMap> <select id="byId" resultMap="BaseResultMap" statementType="PREPARED" > select * from user_department where id = #{id} </select> <insert id="save" parameterType="user" > insert into user_department (id,name,age) values (#{id},#{name},#{age}) </insert> <update id="update" parameterType="user"> update user_department <set> <if test="name != null"> name = #{name} </if> <if test="age != null"> ,age = #{age} </if> </set> where id = #{id} </update> <select id="getUserAndDepartmentById" parameterType="long" resultMap="UserAndDepartmentResultMap" > select a.id,a.name,a.age,b.id as department_id,b.name as department_name from user_department a left join department b on a.default_department_id = b.id where a.id = #{id} </select> <select id="getUserAndDepartmentById1" parameterType="long" resultMap="UserAndDepartmentResultMap1" > select * from user_department a where a.id = #{id} </select> <select id="getUserAndDepartmentById2" parameterType="long" resultMap="UserAndDepartmentResultMap2" > select * from user_department a where a.id = #{id} </select> </mapper>
Mapper calls the interface, and the method name should go to mapper The Id of the configuration in the XML file is the same, otherwise it cannot be mapped successfully.
public interface UserMapper { /** * Query by primary key * @param id * @return */ public User byId(Long id); /** * newly added * @param user * @return */ public void save(User user); /** * modify * @param user */ public void update(User user); /** * Multi table Association query * @param id * @return */ public UserAndDepartment getUserAndDepartmentById(Long id); /** * Association query, N + 1 problem * @param id * @return */ public UserAndDepartment getUserAndDepartmentById1(Long id); /** * Association query, 1 to many * @param id * @return */ public UserAndDepartment getUserAndDepartmentById2(Long id); }
- unit testing
package com.freecloud.plug.mybatis; import com.freecloud.common.LoggerUtil; import com.freecloud.plug.mybatis.dao.UserMapper; import com.freecloud.plug.mybatis.entity.User; import com.freecloud.plug.mybatis.entity.UserAndDepartment; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.Before; import org.junit.Test; import java.io.IOException; import java.io.InputStream; /** * @Author: maomao * @Date: 2021-04-08 11:36 */ public class MyBatisTest { private SqlSessionFactory sqlSessionFactory; @Before public void init() throws IOException { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } /** * Hard coding using mybatis api */ @Test public void testApiStatement(){ SqlSession sqlSession = sqlSessionFactory.openSession(); try { sqlSession.selectOne("com.freecloud.plug.mybatis.dao.UserMapper.byId",1); }finally { sqlSession.close(); } } /** * Tests use mapper wrappers and interface calls directly */ @Test public void testMapperInterface(){ SqlSession sqlSession = sqlSessionFactory.openSession(); try { UserMapper userMapper = sqlSession.getMapper(UserMapper.class); User user = userMapper.byId(1L); LoggerUtil.printThread(user.toString()); }finally { sqlSession.close(); } } /** * Multi table Association query */ @Test public void testUserAndDepartment(){ SqlSession sqlSession = sqlSessionFactory.openSession(); try { UserMapper userMapper = sqlSession.getMapper(UserMapper.class); UserAndDepartment user = userMapper.getUserAndDepartmentById(1L); LoggerUtil.printThread(user.toString()); }finally { sqlSession.close(); } } /** * Association query, N + 1 problem */ @Test public void testUserAndDepartment1(){ SqlSession sqlSession = sqlSessionFactory.openSession(); try { UserMapper userMapper = sqlSession.getMapper(UserMapper.class); UserAndDepartment user = userMapper.getUserAndDepartmentById1(1L); LoggerUtil.printThread(user.toString()); }finally { sqlSession.close(); } } /** * Association query, 1 to many */ @Test public void testUserAndDepartment2(){ SqlSession sqlSession = sqlSessionFactory.openSession(); try { UserMapper userMapper = sqlSession.getMapper(UserMapper.class); UserAndDepartment user = userMapper.getUserAndDepartmentById2(1L); LoggerUtil.printThread(user.toString()); }finally { sqlSession.close(); } } }
Using the above example, you can run MyBatis. Look at the use methods in unit testing, and describe several common methods respectively.
- Using traditional hard coding, it is accessed directly through the ID configured in Mapper mapper Mapper
- Use an empty interface to implement method call (no implementation class), and use sqlsession getMapper(UserMapper.class); Get instance execution
- One to many and one to one forms of object association query
Core object
In the unit test class, we see several core objects in MyBatis:
- SqlSessionFactoryBuiler
- SqlSessionFactory
- SqlSession
- Mpper
These core objects play a role in different links of the whole workflow of MyBatis. If we manage these objects without containers, we must consider a question: when to create and destroy these objects?
In some distributed applications, multithreading and high concurrency scenarios, if you want to write efficient code, you must understand the life cycle of these four objects.
SqlSessionFactoryBuiler
It is used to build SqlSessionFactory and parse mybatis config and other configurations. However, only one SqlSessionFactory is required. Therefore, as long as a SqlSessionFactory is built, its mission will be completed and there is no need to exist. Therefore, its life cycle only exists in the part of the method.
SqlSessionFactory
SqlSessionFactory is used to create sqlsessions, and a reply needs to be created every time you access the database. Because we always need to create a session, SqlSessionFactory should exist in the whole life cycle of the application (the scope is the application scope). Creating a SqlSession requires only one instance to do this, otherwise it will cause confusion and waste of resources. So we should use singleton mode.
In general, the single instance mode should be in the factory.
SqlSession
SqlSession is a session because it is not thread safe and cannot be shared between threads. So we create a SqlSession object at the beginning of the request and close it in time at the end of the request. It can also be understood as the Connection of Jdbc.
Mapper
Mapper is actually a proxy object, which is obtained from SqlSession.
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
Its function is to send Sql to operate the data of the database. It should be within a SqlSession transaction method.
object | Scope |
---|---|
SqlSessionFactoryBuilder | Local method |
SqlSessionFactory | application level |
SqlSession | Request and operation (request, method) |
Mapper | method |
The above is the life cycle of the four core objects.