先简单对MyBatis的使用作一个简要说明,后边会针对MyBatis几个核心原理作重点说明。java
使用MyBatis能够分如下几个关键点mysql
如下按步骤写一个单元测试:redis
<!-- mybatis依赖包 --><dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.4</version></dependency><!-- 数据库驱动 须要使用 5.1.40以上才可解决mysql json格式乱码问题 --><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>
mybatis-config.xmlsql
<?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> <!-- 打印查询语句 --> <setting name="logImpl" value="STDOUT_LOGGING" /> <!-- 控制全局缓存(二级缓存),默认 true--> <setting name="cacheEnabled" value="false"/> <!-- 延迟加载的全局开关。当开启时,全部关联对象都会延迟加载。默认 false --> <setting name="lazyLoadingEnabled" value="false"/> <!-- 当开启时,任何方法的调用都会加载该对象的全部属性。默认 false,可经过select标签的 fetchType来覆盖--> <setting name="aggressiveLazyLoading" value="true"/> <!-- Mybatis 建立具备延迟加载能力的对象所用到的代理工具,默认JAVASSIST --> <!--<setting name="proxyFactory" value="CGLIB" />--> <!-- STATEMENT级别的缓存,使一级缓存,只针对当前执行的这一statement有效,至关于关闭一级缓存 --> <!-- <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> <!-- 自定义类型转换器 --> <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"/><!-- 单独使用时配置成MANAGED没有事务 --> <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数据库链接配置数据库
jdbc.driver=com.mysql.jdbc.Driverjdbc.url=jdbc:mysql://127.0.0.1:3306/data_test?useUnicode=true&characterEncoding=utf-8&rewriteBatchedStatements=truejdbc.username=rootjdbc.password=123456
初始化数据,用于后边的单元测试。apache
## 部门表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;## 员工表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;## 初始化部门insert into department values (1,'部门1',null);insert into department values (2,'部门2',1);insert into department values (3,'部门3',null);## 初始化员工insert into user_department values (1,'张三',18,1,'2,3');insert into user_department values (2,'李4',18,2,'1,2');insert into user_department values (3,'王5',18,3,'3');insert into user_department values (4,'赵6',18,1,'1');
/** * 业务实体对象 */@Data@NoArgsConstructor@AllArgsConstructorpublic class User implements Serializable { /** 主键ID */ private Long id; /** 姓名 */ private String name; /** 年龄 */ private Integer age; }
UserMapper.xml 映射文件json
<?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"> <!-- 声明这个namespace使用二级缓存 --> <!-- <cache/>--> <!-- 使用Redis做为二级缓存 --> <!-- <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> <!-- 对象关联查询,一条sql直接查询 --> <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> <!-- 联合查询,会产生 N+1的问题 --> <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"/> <!-- 此处能够夸namespace调用,但要保证当前应用加载对应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> <!-- 联合查询,一对多 --> <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"/> <!-- 此处能够夸namespace调用,但要保证当前应用加载对应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调用接口,方法名要到Mapper.xml文件中的配置的Id相同,不然没法映射成功。api
public interface UserMapper { /** * 根据主键查询 * @param id * @return */ public User byId(Long id); /** * 新增 * @param user * @return */ public void save(User user); /** * 修改 * @param user */ public void update(User user); /** * 多表关联查询 * @param id * @return */ public UserAndDepartment getUserAndDepartmentById(Long id); /** * 关联查询,有N + 1问题 * @param id * @return */ public UserAndDepartment getUserAndDepartmentById1(Long id); /** * 关联查询,1对多 * @param id * @return */ public UserAndDepartment getUserAndDepartmentById2(Long id); }
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); } /** * 使用mybatis api 方式硬编码方式 */ @Test public void testApiStatement(){ SqlSession sqlSession = sqlSessionFactory.openSession(); try { sqlSession.selectOne("com.freecloud.plug.mybatis.dao.UserMapper.byId",1); }finally { sqlSession.close(); } } /** * 测试使用mapper包装直接使用接口调用 */ @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(); } } /** * 多表关联查询 */ @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(); } } /** * 关联查询,有N + 1问题 */ @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(); } } /** * 关联查询,1对多 */ @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(); } } }
使用以上例子,就能够运行起来MyBatis。看单元测试中的使用方法,分别描述了几种常见方式。缓存
在单元测试类中咱们看到了MyBatis里面的几个核心对象:安全
这几个核心对象在MyBatis的整个工做流程里面的不一样环节发挥做用。若是咱们不用容器,本身去管理这些对象的话,咱们必须考虑一个问题:何时建立和销毁这些对象?
在一些分布式应用里,多线程高并发场景中,若是要写出高效的代码,就必须了解这四个对象的生命周期。
它是用来构建SqlSessionFactory与解析mybatis-config等配置的,而SqlSessionFactory只须要一个,因此只要构建了一个SqlSessionFactory以后它的使命就完成了,也就没有存在的必要了。因此它的生命周期只存在于方法的局部。
SqlSessionFactory是用来建立SqlSession的,每次访问数据库都须要建立一个回话。由于咱们一直有建立会话的须要,因此SqlSessionFactory应该存在于应用的整个生命周期中(做用域是应用做用域)。建立SqlSession只须要一个实例来作这件事就能够了,不然会形成混乱和资源浪费。因此咱们应该采用单例模式。
通常工厂类在都应该是单例模式。
SqlSession是一个会话,由于它不是线程安全的,不能在线程间共享。因此咱们在请求开始的时候建立一个SqlSession对象,在请求结束时要及时关闭它。也能够把它理解成Jdbc 的Connection链接。
实际是Mapper是一个代理对象,是从SqlSession中获取的。
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
它的做用是发送Sql来操做数据库的数据。它应该在一个SqlSession事务方法以内。
对象 | 做用域 |
---|---|
SqlSessionFactoryBuilder | 方法局部(method) |
SqlSessionFactory | 应用级别(application) |
SqlSession | 请求和操做(request、method) |
Mapper | 方法(method) |
以上就是四个核心对象的生命周期。