Mybatis
- 持久层框架
- 动态sql
- ORM
快速开始
- 创建mapper接口
public interface UserDao { List<User> findAll();}
- 创建总配置文件
<?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> <environments default="development"> <environment id="development"> <!--事务类型--> <transactionManager type="JDBC"/> <!--连接池--> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql:///mybatis"/> <property name="username" value="root"/> <property name="password" value="123"/> </dataSource> </environment> </environments> <mappers> <mapper resource="mappers/user.xml"/> </mappers></configuration>
- 创建映射配置文件
<?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="wang.ismy.mybatis.dao.UserDao"> <select id="findAll" resultType="wang.ismy.mybatis.entity.User"> SELECT * FROM user </select></mapper>
使用xml
@Testpublic void findAll() throws IOException { SqlSessionFactory factory = new SqlSessionFactoryBuilder().build( Resources.getResourceAsStream("config.xml")); SqlSession sqlSession = factory.openSession(); List<User> list = sqlSession.getMapper(UserDao.class).findAll(); assertEquals(6,list.size());}
根据条件查询
<select id="findById" resultType="wang.ismy.mybatis.entity.User"> SELECT * FROM user WHERE id = #{id} </select>
细节:
- resultType 属性: 用于指定结果集的类型。
- parameterType 属性: 用于指定传入参数的类型。
- sql 语句中使用的`#{}`: 它代表占位符,相当于jdbc 中的?,都是用于执行语句时替换实际的数据
- `#{}`中的内容:因为参数只有一个,所以此处可以随意写
更新
<insert id="save" parameterType="wang.ismy.mybatis.entity.User"> INSERT INTO user(username,address,sex,birthday) VALUES(#{username},#{address},#{sex},#{birthday})</insert>
int save(User user);
需要注意的是,mybatis的SqlSession关闭了事务的默认提交,当进行完更新操作后,需要手动调用sqlSession.commit();
模糊查询字符串拼接问题
SELECT * FROM user WHERE username LIKE '%' #{name} '%'
插入数据后返回ID
<insert id="save" parameterType="wang.ismy.mybatis.entity.User"> <selectKey keyColumn="id" keyProperty="id" resultType="int"> select last_insert_id(); </selectKey> INSERT INTO user(username,address,sex,birthday) VALUES(#{username},#{address},#{sex},#{birthday})</insert>
使用resultMap
<resultMap id="userMap" type="wang.ismy.mybatis.entity.User"> <!--主键--> <id column="id" property="id"/> <!--非主键--> <result column="username" property="username"/></resultMap><select id="findAll" resultMap="userMap"> select * from user</select>
Properties标签
<properties resource="jdbc.cfg"> </properties>
typeAliases标签
<typeAliases> <!--指定别名,不区分大小写--> <typeAlias type="wang.ismy.mybatis.entity.User" alias="user"/> <!--指定该包下的所有类为别名,不区分大小写--> <package name="wang.ismy.mybatis.entity"/></typeAliases>
mapper
<mappers> <package name="wang.ismy.mybatis.dao"></mappers>
使用注解
public interface UserDao { @Select("SELECT * FROM user") List<User> findAll();}
注解 | 作用 |
---|---|
@Insert | 实现新增 |
@Update | 实现更新 |
@Delete | 实现删除 |
@Select | 实现查询 |
@Result | 实现结果集封装 |
@Results | 可以与@Result 一起使用,封装多个结果集 |
@ResultMap | 实现引用@Results 定义的封装 |
@One | 实现一对一结果集封装 |
@Many | 实现一对多结果集封装 |
@SelectProvider | 实现动态 SQL 映射 |
@CacheNamespace | 实现注解二级缓存的使用 |
Result注解使用
@Select("SELECT * FROM user")@Results({ @Result(id=true,column = "id",property ="id"), @Result(column = "username",property ="username"), @Result(column = "sex",property ="sex"), @Result(column = "address",property ="address"), @Result(column = "birthday",property ="birthday")})List<User> find();
一对一查询
@Select("SELECT * FROM account")@Results({ @Result(id=true,column = "id",property = "id"), @Result(column = "uid",property = "uid"), @Result(column = "money",property = "money"), @Result(column = "uid",property = "user",one = @One(select = "wang.ismy.mybatis.dao.UserDao.findById",fetchType = FetchType.LAZY))})List<Account> findAll();
一对多查询
@Select("SELECT * FROM user")@Results({ @Result(id=true,column = "id",property ="id"), @Result(column = "username",property ="username"), @Result(column = "sex",property ="sex"), @Result(column = "address",property ="address"), @Result(column = "birthday",property ="birthday"), @Result(column = "id",property = "account", many = @Many(select = "wang.ismy.mybatis.dao.AccountDao.findById",fetchType = FetchType.LAZY))})List<User> find();
开启二级缓存
@CacheNamespacepublic interface UserDao {}
<!--如果使用注解的话,则指定class属性--><mappers> <mapper class="wang.ismy.mybatis.dao.UserDao"/></mappers>
原理及源码分析
Mapper 注册中心 -> 执行器 -> StatementHadnler -> ResultsetHandler
核心接口:
- SqlSession
- SqlSessionFactory
- ResultHandler
XML相关:
- XMLConfigBuilder:读取XML配置文件
- XPathParser:根据XPath解析
重点:
- MapperRegistry:负责Mapper接口处理
- MapperAnnotationBuilder
- Configuration
MapperProxy:
- invoke
MapperMethod
ParamNameResolver
MappedStatement
Executor
StatementHandler
MetaObject:拦截器元数据
缓存机制
延迟加载
就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载.
- 开启延迟加载
<settings> <setting name="logImpl" value="STDOUT_LOGGING" /> <!--延迟加载相关--> <setting name="lazyLoadingEnabled" value="true"/> <setting name="aggressiveLazyLoading" value="false"/></settings>
- 修改连接
<resultMap id="userMap" type="user"> <id column="id" property="id"/> <result column="username" property="username"/> <result property="address" column="address"/> <result property="birthday" column="birthday"/> <result property="sex" column="sex"/> <collection property="account" ofType="Account" select="wang.ismy.mybatis.dao.AccountDao.findById" column="id"> <id column="account_id" property="id"/> <result column="uid" property="uid"/> <result column="money" property="money"/> </collection></resultMap>
使用的动态代理实现的延迟加载
缓存
一级缓存
一级缓存是 SqlSession 范围的缓存,当调用 SqlSession 的修改,添加,删除,commit(),close()等方法时,就会清空一级缓存。
命中原则:
- statementId相同
- 查询参数相同(hash的方式)
- 分页参数
- sql语句
- 同一环境(environment属性)
生命周期:
缓存销毁:
- session close
- commit
- clearCache主动清除
设计理念:
- 不过期
- 不更新
- 不限制
二级缓存
二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个 SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的。
二级缓存中存放的是数据而不是对象
开启
- 第一步:在 SqlMapConfig.xml 文件开启二级缓存
<!--默认为true,可以省略--><setting name="cacheEnabled" value="true"/>
- 第二步:配置相关的 Mapper 映射文件
<cache/>
- 第三步:配置 statement 上面的 useCache 属性
<select id="findAll" resultMap="userMap" useCache="true"> SELECT * FROM user</select>
命中原则
- 同一 session factory
- statement id 相同
- 参数相同
- environment 环境相同
- sql session close 或 commit
生命周期
创建:
- sql session close 或 commit
销毁:
- sql session update
缓存清除策略
多表查询
一对一
- 定义结果映射
<resultMap id="accountMap" type="Account"> <id column="account_id" property="id"/> <result column="UID" property="uid"/> <result column="MONEY" property="money"/> <association property="user" javaType="User"> <id column="id" property="id"/> <result column="username" property="username"/> <result property="address" column="address"/> <result property="birthday" column="birthday"/> <result property="sex" column="sex"/> </association></resultMap>
- 多表查询设置结果映射
<select id="findAll" resultMap="accountMap"> SELECT account.ID AS account_id, account.UID, account.MONEY, user.* FROM account,user WHERE account.UID = user.id</select>
- 实体类
@Datapublic class Account { private Integer id; private Integer uid; private Double money; private User user;}
一对多
<resultMap id="userMap" type="user"> <id column="id" property="id"/> <result column="username" property="username"/> <result property="address" column="address"/> <result property="birthday" column="birthday"/> <result property="sex" column="sex"/> <collection property="account" ofType="Account"> <id column="account_id" property="id"/> <result column="uid" property="uid"/> <result column="money" property="money"/> </collection></resultMap><select id="findAll" resultMap="userMap"> SELECT user.*, account.ID as account_id, account.uid, account.money FROM user LEFT OUTER JOIN account ON user.id = account.UID</select>
@Datapublic class User { private Integer id; private String username; private LocalDate birthday; private String sex; private String address; private List<Account> account;}
左外连接的使用
多对多
多对多的映射关系,可以拆分成两个一对多的关系
动态SQL
if
<if test="username != null"> #{username} 'abc'</if>
where 标签
<where> <if test="..."> ... </if></where>
这样就不用写where 1=1
前缀
foreach标签
- collection:代表要遍历的集合元素,注意编写时不要写`#{}`
- open:代表语句的开始部分
- close:代表结束部分
- item:代表遍历集合的每个元素,生成的变量名
- sperator:代表分隔符
<foreach collection="ids" open="id in ( " close=")" item="uid" separator=","> #{uid} </foreach>
SQL重用
<sql id="sql"> SELECT * FROM user WHERE id = #{id}</sql><select id="findById" resultType="wang.ismy.mybatis.entity.User"> <include refid="sql"/></select>
连接池与事务
连接池:
- UNPOOLED:不使用连接池的数据源
- POOLED:使用连接池的数据源
- JNDI:使用 JNDI 实现的数据源
事务:
/*提交事务*/sqlSession.commit();/*回滚事务*/sqlSession.rollback();
分页插件
- 引入依赖
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.1.10</version></dependency>
- 配置
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="plugins"> <array> <bean class="com.github.pagehelper.PageInterceptor"> <property name="properties"> <props> <prop key="helperDialect">oracle</prop> <prop key="reasonable">true</prop> </props> </property> </bean> </array> </property></bean>
- 使用
@Overridepublic List<Order> findAll() { PageHelper.startPage(1,5); return orderDao.findAll();}