Mybaties介绍
Mybatis是一款优秀的持久层框架。Mybatis免除了几乎所有的JDBC代码以及设置参数和获取结果集的工作。
快速入门
①数据准备
MySQL表的创建,记录的添加。
②导入依赖
1 | <dependencies> |
③编写核心配置
在资源目录下创建:mybatis-config.xml内容如下:
1 |
|
修改其中的一些参数。
④定义接口及对应的xml映射文件
在资源目录下创建对应的文件xml映射文件
⑤编写测试类
获取SqiSession,通过SqiSession.getMapper获取UserDao调用对应的方法
入门代码初步理解
测试类中:
1 | public static void main(String[] args)throws IOException{ |
光速开发
IDEA配置代码模板
可以在IDEA中设置模板
Mybatis插件
下载Free Mybatis plugin。在接口中可以自动生成xml文件。在接口中的方法中自动生成写入xml中。
参数获取
一个参数
基本参数
我们可以使用#{}直接来取值,写任意名字都可以获取到参数,但是一般用方法的参数名来取。
接口中的方法定义如下:
1 | User findById(Integer id); |
xml中内容如下:
1 | <SELECT id="findById" resultType="com.xiaohupao.pojo.User"> |
POJO
接口中的方法定义如下:
1 | User findByUser(user user); |
xml中内容如下:
1 | <SELECT id="findByUser" resultType="com.xiaohupao.pojo.User"> |
Map
接口中的方法定义如下:
1 | User findByMap(Map map); |
xml中内容如下:
1 | <Select id="findByMap" resultType="com.xiaohupao.pojo.User"> |
1 | Map map = new HashMap(); |
多个参数
Mybatis会把多个参数放入一个Map集合中,默认的key是argx和paramx这种格式。
1 | User findByCondition(Integer id, String username); |
最终map中的键值对如下:
1 | {arg1=PDD, arg0=2, param1=2, param2=PDD} |
虽然可以使用对应的默认Key来获取,但是这样的可读性不好。我们一般在参数前使用@Param注解来设置参数名.
接口中的方法的定义:
1 | User findByCondition( Integer id, String username); |
这样在Mapper中可以这样获取参数:
1 | <select id="findByCondition" resultType="com.xiaohupao.pojo.User"> |
总结
如果只有一个参数的时候不用做什么处理;如果有多个参数情况下一定要加上@Param来设置参数名。
核心类
SqlSessionFactory
SqlSessionFactory是一个SqlSession的工厂类,主要用于获取SqlSession对象。其成员方法方法如下:
1 | SqlSession openSession();//默认不传,则为false |
SqlSession
SqlSession提供了在数据库执行SQL命令所需要的方法。它还提供了事务相关操作。其成员方法如下:
1 | T getMapper(class<T> type);//获取mapper对象 |
Mybatis实现增删改查
新增
①接口中增加相关方法
1 | void insertUser(User user); |
②映射文件UserDao.xml增加相应的标签
1 | <!-- 新增数据 --> |
注意:要记得提交事务:手动提交;或在openSession构造传入true
删除
①接口中增加相关方法
1 | void deleteById(Integer id); |
②映射文件UserDao.xml增加相应的标签
1 | <!-- 删除数据--> |
注意:要记得提交事务:手动提交;或在openSession构造传入true
修改
①接口中增加相关方法
1 | void updataUser(User user); |
②映射文件UserDao.xml增加相应的标签
1 | <!-- 更新数据--> |
查询
根据ID查询
①接口中增加相关方法
1 | User findById(Integer id); |
②映射文件UserDao.xml增加相应的标签
1 | <select id="findById" parameterType="integer" resultType="com.xiaohupao.pojo.User"> |
查询所有
①接口中增加相关方法
1 | List<User> findAll(); |
②映射文件UserDao.xml增加相应的标签
1 | <select id="findAll" resultType="com.xiaohupao.pojo.User"> |
配置文件详解
properties
在resources目录下有jdbc.properties文件,内容如下:
1 | jdbc.url=jdbc:mysql://localhost:3306/mybatis_db |
在mybatis-config.xml中:
1 |
|
settings
可以使用该标签来进行一些设置
例如:
1 | <settings> |
typeAliases
可以用来设置全类名设置别名,简化书写。一般设置一个包下的类全部具有默认别名,默认别名是类目首字母小写。例如:cn.xiaohupao.pojo.User别名为user。
1 | <typeAliases> |
使用\
environments
可以配置成适应多种环境,这种机制有助于SQL映射应用于多种数据库之中。
尽管可以配置多个环境,但每个SqlSessionFactory实例只能选择一种环境。所以如果想连接两个数据库,就需要创建两个SqlSessionFactory实例,每个数据库对应一个。而如果是三个数据库,就需要三个实例,依此类推。
mappers
该标签的作用是加载映射,加载方式如下几种(主要使用四种):
①使用相对于类路径的资源引用,例如:
1 | <mappers> |
②使用完全限定资源定位符(URL)
③使用映射器接口实现类的完全限定类名
④将包内的映射器接口实现全部注册为映射器
1 | <mappers> |
打印日志
添加Log4J的jar包;配置Log4J
1 | <dependency> |
1 | # 全局日志配置 |
获取参数时#{}和${}的区别
如果使用#{}它是预编译的sql可以防止SQL注入攻击;如果使用${}它是直接把参数拿过来直接进行拼接,这样会有SQL注入的危险。
Mybatis注解开发
我们也可以使用注解来进行开发,用注解替换掉XML。使用注解来映射简单语句会使代码显得更加简洁。对于稍微复杂的语句,Java注解不仅力不从心,还会让你本就复杂的SQL语句更加混乱不堪。所以在实际开发中一般都是使用XML的形式。
①在核心配置文件中配置mapper接口所在包名
1 | <mappers> |
②在接口对应方法上使用注解来配置需要执行的sql
1 | public interface UserDao { |
动态SQL
if
使用if标签进行条件判断,条件成立才会把if标签中的内容进行拼接。
例如:
1 | <select id="findByCondition" resultType="com.xiaohupao.pojo.User"> |
如果参数username为null则执行的sql为:SELECT * FORM user WHERE id=?
如果参数username不为null则执行的sql为:SELECT * FORM user WHERE id=? AND username=?
注意:在test属性中表示参数不需要写#{}。
trim
可以使用该标签动态的添加前缀或后缀,也可以使用该标签动态的消除前缀。
prefixOverrides属性
用来设置需要被清除的前缀,多个值可以用|分隔,注意|前后不要有空格。例如and|or
例如:
1 | <select id="findByCondition" resultType="com.xiaohupao.pojo.User"> |
最终执行的sql为:SELECT * FROM user
suffixOverrides属性
用来设置需要被清除的后缀可以用|分隔,注意|后不要有空格。例如and|or
例如:
1 | <select id="findByCondition" resultType="com.xiaohupao.pojo.User"> |
最终执行的sql为:SELECT * FROM user WHERE id = ?
prefix属性
用来设置动态添加的前缀,如果标签中有内容就会添加上设置的前缀。
例如:
1 | <select id="findByCondition" resultType="com.xiaohupao.pojo.User"> |
最终执行的sql为:SELECT * FROM user WHERE id = ?
suffix属性
用来设置动态添加的后缀,如果标签中有内容就会添加上设置的后缀。
1 | <select id="findByCondition" resultType="com.xiaohupao.pojo.User"> |
最终执行的sql为:SELECT * FROM user WHERE id = ?
动态添加前缀WHERE并且消除前缀AND或者OR
1 | <select id="findByCondition" resultType="com.xiaohupao.pojo.User"> |
where
where标签等价于:
1 | <trim prefix="WHERE" prefixOverrides="and|or"></trim> |
动态的拼接where并且去除前缀and或者or。
set
set标签等价于
1 | <trim prefix="SET" prefixOverrides=","></trim> |
可以使用set标签动态拼接set并且去除后缀的逗号。
1 | <update id="updateUser"> |
如果调用方法传入User对象的id为2,username不为null,其他属性都为null则最终执行sql为:UPDATE USER SET username = ? WHERE id = ?
foreach
可以使用foreach标签遍历集合或者数组类型的参数,获取其中的元素拿来动态的拼接SQL语句。
1 | List<User> findByIds( Integer[] ids); |
1 | <select id="findByIds" resultType="com.xiaohupao.pojo.User"> |
collection:表示要遍历的参数;open:表示遍历开始时拼接的语句;item:表示给当前遍历到的元素的取得名字;separator:表示遍历完一次拼接得分隔符;close:表示最后一次遍历拼接得语句。
注意:如果方法参数是数组类型,默认的参数名是array,如果方法参数是list集合默认的参数名是list。建议遇到数组或者集合类型的参数统一使用注解@Param进行命名。
choose、when、otherwise
1 | <select id="findByIds" resultType="com.xiaohupao.pojo.User"> |
choose类似于java中的switch;when类似于java中的case;otherwise类似于java中的default。
一个choose标签中最多只会有一个when中的判断成立。从上到下进行判断。如果成立了就把标签体的内容拼接到sql中,并且不会进行其他when的判断和拼接。如果所有的when都不成立则拼接otherwise中的语句。
SQL片段抽取
在xml映射文件中编写SQL语句的时候可能会遇到重复的SQL片段。这种SQL片段我们可以使用sql标签来进行抽取。然后在需要的时候include标签进行使用。
1 | <sql id="base_sql">id, username, age, address</sql> |
最终执行的sql为:SELECT id, username, age, address FROM user
环境案例
ResultMap
基本使用
我们可以使用resultMap标签来自定义结果集和实体类属性的映射规则。
1 | resultMap 用来自定义结果和实体类的映射 |
1 | <mapper namespace="com.xiaohupao.dao.OrderDao"> |
自动映射
我们定义resultMap时默认情况下自动映射是开启状态的。也就是如果结果集的列明和我们属性名相同是会自动映射的,我们只需要写特殊情况的映射关系即可。
自动映射的属性为autoMapping=false 则将设置自动映射关闭。
1 | <resultMap id="orderMap" type="com.xiaohupao.pojo.Order" autoMapping="false"> |
继承映射关系
啊、我们可以使用resultMap的extends属性来指定一个resultMap,从而复用重复的映射关系配置。
1 | <resultMap id="baseOrderMap" type="com.xiaohupao.pojo.Order"> |
多表查询
多表关联查询
一对一关系
两个实体之间是一对一关系。例如:我们需要查询订单,需求还需要下单用户的数据,这里的订单相当于用户是一对一。
接口中的定义:
1 | /** |
因为期望Order中还能包含下单用户的数据,所以可以在Order中增加一个属性
1 | private User user; |
1 | SELECT o.`id`, o.`createtime`, o.`price`, o.`remark`, u.`id` AS uid, u.`username`, u.`age`, u.`address` |
我们可以使用如下两种方式封装结果集。
使用ResultMap对所有字段进行映射
1 | <resultMap id="baseOrderMap" type="com.xiaohupao.pojo.Order"> |
使用ResultMap中的association
1 | <resultMap id="orderUserMapAssociation" type="com.xiaohupao.pojo.Order" extends="baseOrderMap"> |
一对多关系
两个实体类之间是一对多的关系。例如:我们需要查询用户,要求还是该用户所具有的角色信息,这里的用户相对于角色是一对多的。
接口中的定义
1 | User findById(Integer id); |
由于一个用户可以对应多个角色,所以在实体类中增加集合用于存储角色
1 | private List<Role> roles; |
1 | SELECT u.`id`, u.`username`, u.`age`, u.`address`, r.`id` AS rid, r.`name`, r.`desc` |
封装结果的方式:
1 | <resultMap id="userBase" type="com.xiaohupao.pojo.User"> |
分部查询
如果有需要多表查询的需求我们也可以选择多次查询的方式来查询出我们想要的数据。Mybatis也提供了对应的配置。
例如我们需要查询用户。需求还需要查询出该用户所具有的角色信息。我们可以选择先查询User表查询用户信息。然后在去查询相关的角色信息。
实现步骤
①定义查询方法
因为我们要分两步查询:1.查询User;2.根据用户id查询role信息。所以我们需要定义两个方法,并且把对应的标签也写好。
查询User
1 | User findByUsername(String name); |
1 | <select id="findByUsername" resultType="com.xiaohupao.pojo.User"> |
根据user_id查询Role
1 | public interface RoleDao { |
1 | <select id="findRoleByUserId" resultType="com.xiaohupao.pojo.Role"> |
②定义分部查询
1 | <resultMap id="userBase" type="com.xiaohupao.pojo.User"> |
设置按需加载
我们可以设置按需加载,这样在我们代码中需要用到关联数据的时候才回去查询关联数据。有两种可以配置分别是全局配置和局部配置。
局部配置:设置fetchType属性为lazy
1 | <resultMap id="userBase" type="com.xiaohupao.pojo.User"> |
全局配置:设置lazyLoadingEnabled为true
1 | <!--mybatis-config.xml --> |
分页查询-Pagehelper
我们可以使用PageHelper非常方便的帮我们实现分页查询,不需要自己在SQL中拼接相关参数,并且能非常方便的获取的总页数总条数等分页相关数据。
实现步骤
①定义方法查询方法以及生成对应标签
1 | List<User> findAll(); |
1 | <select id="findAll" resultType="com.xiaohupao.pojo.User"> |
②导入依赖
1 | <!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper --> |
③配置Mybatis核心配置文件使用分页插件
1 | <!--mybatis-config.xml --> |
④开始分页查询
我们只需要在使用查询方法前设置分页参数即可
1 | UserDao userDao = this.sqlSession.getMapper(UserDao.class); |
如果需要获取总页数总条数等分页相关数据,只需要创建一个PageInfo对象,把刚刚查询出的返回值作为构造方法参数传入。然后使用pageInfo对象获取即可。
1 | PageInfo<User> userPageInfo = new PageInfo<User>(all); |
一对多多表查询分页问题
我们在进行一对多表查询时,如果使用了PageHelper进行分页,会出现关联数据不全的情况,我们可以使用分布查询的方式解决该问题。
Mybatis缓存
Mybatis的缓存其实就是把之前查到的数据存入内存(map),下次如果还是查询相同的东西,就可以直接从缓存中取,从而提高效率。
Mybatis有一级缓存和二级缓存之分,一级缓存(默认开启)是sqlsession级别的缓存。二级缓存相当于mapper级别的缓存。
一级缓存
几种不会使用一级缓存的情况:
- 调用相同方法但是传入的参数不同
- 调用相同的方法参数也相同,但是使用的是另外的SqlSession
- 如果查询完后,对同一个表进行了增,删改的操作,都会清空这个SqlSession上的缓存
- 如果手动调用SqlSession的clearCache方法清除了缓存,后面也使用不了缓存
二级缓存
注意:只有SqlSession调用了close或者commit后的数据才会进入二级缓存。
开启二级缓存
①全局开启
在Mybatis核心配置文件中配置
1 | <!--mybatis-config.xml--> |
②局部开启
在要开启二级缓存的mapper映射文件设置cache标签
1 |
|
使用建议
二级缓存在实际开发中基本不会使用。