【mybatis】mybatis基础知识总结
1._MyBatis是什么
MyBatis是一个优秀的基于java的持久层框架,它内部封装了JDBC,解决了sql语句不易维护的问题,使开发者只需要关注SQL语句本身,而不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。
MyBatis通过XML或注解的方式将要执行的各种statement配置起来,并通过JAVA对象和statement中SQL的动态参数进行映射生成最终执行的SQL语句。
最后MyBatis框架执行SQL并将结果映射为Java对象并返回。采用ORM思想解决了实体和数据库映射的问题,对JDBC进行了封装,屏蔽了JDBC API底层访问细节,使我们不用与JDBC API打交道,就可以完成对数据库的持久化操作
简单说就是:有一种叫MyBatis的持久层技术,能够代替JDBC,简化JDBC开发。
具体技术:
- Hibernate
- MyBatis
2._ORM
对象关系映射(英语:Object Relational Mapping,简称ORM,或O/RM,或O/R mapping),是一种程序设计技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换。从效果上说,它其实是创建了一个可在编程语言里使用“虚拟对象数据库”。
简单的说:就是把数据库表和实体类及实体类的属性对应起来,让我们可以操作实体类就实现操作数据库表。
数据库 | 实体类 |
---|---|
user表 | User类 |
id列 | id属性 |
username列 | userName属性 |
age列 | age属性 |
3._解决包下的UserMapper.xml不会编译(报错)
出现该问题的原因是因为UserMapper.xml作为一个配置文件应该放在java目录下,如果不做特殊配置Maven不会对其进行编译,Maven只会编译java目录下的.java文件。针对这个问题,有两种解决方案:
- 在resources目录下新建目录com/qfedu/mapper,将UserMapper.xml剪切到该目录下;
- 在pom.xml配置java目录下的xml文件为资源文件,让Maven能够识别,从而进行编译。
我们一般在项目开发中,采用第二种方案进行处理,pom.xml中的新增配置如下:
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<!-- src/main/java下面的 任意文件夹及其子文件夹下的所有xml文件 -->
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
配置完成后,再次运行测试方法,测试通过。
4._MyBatis映射文件概述(增删改查) => UserMapper.xml
使用
<?xml version="1.0" encoding="UTF-8" ?>
<!-- 映射文件DTD约束,用来约束XML中的标签,直接从素材中拷贝就可以,不需要去记。-->
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 映射文件的根标签,其他的内容都要写在该标签的内部
namespace:命名空间,与下面语句的id一起组成查询的标识,唯一的确定要执行的SQL语句 =》 绑定全类名=》就是指这是哪个接口的文件
-->
<mapper namespace="userMapper">
<!-- select标签表示这是一个查询操作,除了这个标签还可以有insert、delete、update
id:语句的id,和上面的namespace一起组成查询的标识,唯一的确定要执行的SQL语句
resultType:查询结果对应的实体类型,目前这里先写全类名(包名+类名)
-->
<select id="findAll" resultType="com.qfedu.bean.User">
<!-- SQL语句 -->
SELECT * FROM user
</select>
<!-- 插入操作使用insert标签
parameterType:指定要插入的数据类型,这里暂时写全类名
-->
<insert id="add" parameterType="com.qfedu.bean.User">
<!-- #{xxx}:使用实体中的xxx属性值 -->
insert into user(username, password, age, gender, addr)
values(#{username}, #{password}, #{age}, #{gender}, #{addr})
</insert>
<!-- 删除操作使用delete标签
#{任意字符串}方式引用传递的单个参数,如果传入的参数只有一个,大括号中的内容可以随意写,我们最好见名知意
-->
<delete id="delete" parameterType="java.lang.Integer">
DELETE FROM user WHERE id=#{id}
</delete>
<update id="chg" parameterType="com.qfedu.bean.User">
UPDATE user SET username=#{username}, password=#{password} WHERE id=#{id}
</update>
</mapper>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
测试
private SqlSession sqlSession;
@Before
public void init() throws IOException {
//加载核心配置文件
InputStream stream = Resources.getResourceAsStream("SqlMapConfig.xml");
//获得sqlSession工厂对象
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(stream);
//获得sqlSession对象
sqlSession = factory.openSession();
}
@After
public void destroy() {
//释放资源
sqlSession.close();
}
@Test
public void testDelete() {
//执行SQL语句
sqlSession.delete("userMapper.delete", 3);
//提交事务
//插入操作涉及数据库的变化,要手动提交事务
sqlSession.commit();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
5._MyBatis核心配置文件 => SqlMapConfig.xml
5.1._MyBatis配置文件层次关系
MyBatis的配置文件包含了会深深影响MyBatis行为的设置和属性信息。配置问及那的顶层结构如下
- configuration (配置)
- properties(属性)
- setting(设置)
- typeAliases(类型别名)
- typeHandlers(类别处理器)
- objectFactory(对象工厂)
- plugin(插件)
- environments(环境配置)
- environment(环境变量)
- transactionManager(事务管理器)
- dataSource (数据源)
- environment(环境变量)
- databaseIdProvider(数据库厂商标识)
- mappers(映射器)
配置相关标签必须按照上图中的顺序,不然程序无法正常运行
5.2._核心配置文件常用配置解析
<?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>
<!-- 配置... -->
</configuration>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
5.2.1._environments标签
该标签用来配置数据库环境,支持多环境配置
<!-- 配置环境
default:指定默认环境,如果下面配置了多个id,通过delfault属性的值指定使用哪个环境
-->
<environments default="dev">
<!-- id:环境的id -->
<environment id="dev">
<!-- type:事务管理器类型,类型有如下两种。
JDBC:这个配置就是直接使用了JDBC的提交和回滚设置,它依赖于从数据源得到的连接来管理事务作用域。
MANAGED:这个配置几乎没做什么。它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期。
默认情况下它会关闭连接,然而一些容器并不希望这样,因此需要将closeConnection属性设置
为false来阻止它默认的关闭行为。
-->
<transactionManager type="JDBC"></transactionManager>
<!-- type:指定数据源类型,类型有如下三种。
UNPOOLED:这个数据源的实现只是每次被请求时打开和关闭连接。
POOLED:这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来。
JNDI:这个数据源的实现是为了能在如EJB或应用服务器这类容器中使用,
容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的引用。
-->
<dataSource type="POOLED">
<!-- 配置数据源的基本参数 -->
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true&characterEncpding=utf8" />
<property name="username" value="root" />
<property name="password" value="root" />
</dataSource>
</environment>
</environments>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
5.2.2._mapper标签
该标签的作用是加载映射文件,加载方式有如下几种:
使用相对于类路径的资源引用,例如:
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
- 1
使用完全限定资源定位符(URL),例如:
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
- 1
使用映射器接口实现类的完全限定类名,例如:
<mapper class="org.mybatis.builder.AuthorMapper"/>
- 1
将包内的映射器接口实现全部注册为映射器,例如:
<package name="org.mybatis.builder"/>
- 1
5.2.3._properties标签
实际开发中,习惯将数据源的配置信息单独抽取成一个properties文件,该标签可以加载额外配置的properties文件
创建配置数据源的配置文件jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=root
jdbc.password=root
- 1
- 2
- 3
- 4
修改核心配置文件
<configuration>
<!-- 引入外部配置文件 -->
<properties resource="jdbc.properties" />
<!-- 配置环境 -->
<environments default="dev">
<environment id="dev">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<!-- 使用${key}引用properties文件中的值 -->
<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>
</configuration>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
5.2.4._typeAliases标签
5.2.4.1._如何设置别名
配置类型别名,为全类名设置一个短名字
写法1
<!-- 配置别名 -->
<typeAliases>
<typeAlias type="com.qfedu.bean.User" alias="user" />
</typeAliases>
- 1
- 2
- 3
- 4
通过上面的配置为com.qfedu.bean包下的User类起了一个别名user。这种写法需要为每个类分别设置别名,如果类很多,这里的配置会很繁琐。
写法2
<!-- 配置别名 -->
<typeAliases>
<package name="com.qfedu.bean"/>
</typeAliases>
- 1
- 2
- 3
- 4
这种写法为com.qfedu.bean下的所有类设置别名,别名为类名的首字母小写。
5.2.4.2._如何使用别名
<select id="findAll" resultType="user">
SELECT * FROM user
</select>
<update id="chg" parameterType="user">
UPDATE user SET username=#{username}, password=#{password} WHERE id=#{id}
</update>
- 1
- 2
- 3
- 4
- 5
- 6
5.2.4.3._MyBatis预先设置好的别名
数据类型 别名 java.lang.String string java.lang.Long long java.lang.Integer int java.lang.Double double …… ……
<delete id="delete" parameterType="int">
DELETE FROM user WHERE id=#{id}
</delete>
- 1
- 2
- 3
6._代理开发方式
代理开发 =》 动态代理 =》 接口的对象由框架生成
MyBatis代理开发方式实现DAO层的开发,这种方式是目前企业的主流。
Mapper接口开发方法只需要程序员编写Mapper接口(相当于Dao 接口),由MyBatis框架根据接口定义创建接口的动态代理对象,代理对象的方法体同上边Dao接口实现类方法。
需要遵循的规范:
- Mapper.xml文件中的namespace与mapper接口的全限定名相同;
- Mapper接口方法名和Mapper.xml中定义的每个statement的id相同;
- Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql的parameterType的类型相同;
- Mapper接口方法的返回值类型和mapper.xml中定义的每个sql的resultType的类型相同。
约定大于配置
6.1._关于参数绑定
6.1.1._序号参数绑定
接口
public interface UserMapper {
User findByNameAndPwd(String username, String password);
}
- 1
- 2
- 3
映射文件
<select id="findByNameAndPwd" resultType="user">
select * from user where username=#{arg0} and password=#{arg1}
</select>
或
<select id="findByNameAndPwd" resultType="user">
select * from user where username=#{param1} and password=#{param2}
</select>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
6.1.2._注解参数绑定(推荐)
接口
public interface UserMapper {
User findByNameAndPwd(@Param("name") String username, @Param("pwd") String password);
}
- 1
- 2
- 3
映射文件
<select id="findByNameAndPwd" resultType="user">
select * from user where username=#{name} and password=#{pwd}
</select>
- 1
- 2
- 3
6.1.3._Map参数绑定
接口
public interface UserMapper {
User findByMap(Map map);
}
- 1
- 2
- 3
映射文件=>map的键名
<select id="findByMap" resultType="user">
select * from user where username=#{name} and password=#{pwd}
</select>
- 1
- 2
- 3
测试方法
@Test
public void testFindByMap2() {
HashMap map = new HashMap();
map.put("name", "admin");
map.put("pwd", "123456");
User admin = userMapper.findByMap(map);
System.out.println(admin);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
6.1.4._对象参数绑定
接口
public interface UserMapper {
User fingByObj(User user);
}
- 1
- 2
- 3
映射文件
<select id="fingByObj" resultType="user">
<!-- 占位符的名字和参数实体类属性的名字相同 -->
select * from user where username=#{username} and password=#{password}
</select>
- 1
- 2
- 3
- 4
测试方法
@Test
public void testFindByObj() {
User user = new User();
user.setUsername("admin");
user.setPassword("123456");
User u = userMapper.fingByObj(user);
System.out.println(u);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
6.2._关于模糊查询
接口
public interface UserMapper {
List<User> findByName(String name);
}
- 1
- 2
- 3
映射文件
<select id="findByName" resultType="user">
select * from user where username like concat('%%',#{username},'%%')
</select>
- 1
- 2
- 3
测试方法
@Test
public void testFindByName() {
List<User> users = userMapper.findByName("zhang");
users.forEach(System.out::println);
}
- 1
- 2
- 3
- 4
- 5
6.3._关于主键回填(返回插入前或者插入后的主键)
6.3.1._通过last_insert_id()查询主键
适用于整数类型自增主键
映射文件
<insert id="add" parameterType="user">
<selectKey keyColumn="id" keyProperty="id" resultType="long" order="AFTER">
<!-- 适用于整数类型自增主键 -->
SELECT LAST_INSERT_ID()
</selectKey>
insert into user(username, password, age, gender, addr) values(#{username}, #{password}, #{age}, #{gender}, #{addr})
</insert>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
测试方法
@Test
public void testAdd() {
User user = new User();
user.setUsername("admin1");
user.setPassword("123456");
userMapper.add(user);
//打印的信息中包含主键
System.out.println(user);
sqlSession.commit();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
6.3.2._通过uuid()查询主键
**适用于字符类型主键 **
接口映射文件
<?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.qfedu.mapper.ProductMapper">
<insert id="add" parameterType="product">
<selectKey keyProperty="id" keyColumn="id" resultType="string" order="BEFORE">
<!-- 适用于字符类型主键 -->
SELECT REPLACE(UUID(),'-','')
</selectKey>
insert into product(id, name) values(#{id}, #{name})
</insert>
</mapper>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
测试同
LAST_INSERT_ID()
,本接口映射 调用添加方法后,创建的product类中会包含id
7._mybatis映射文件深入
7.1._动态SQL语句if
我们根据实体类属性的不同取值,使用不同的SQL语句来进行查询。比如在id如果不为空时可以根据id查询,如果username不同空时还要加入用户名作为条件。这种情况在我们的多条件组合查询中经常会碰到。映射文件如下:
<select id="findByCondition" resultType="user">
<!-- 注意这里的where 1=1,其实1和true也可以,只要返回的是true就行 -->
select * from user where 1=1
<if test="id!=null">
and id=#{id}
</if>
<if test="username!=null">
and username=#{username}
</if>
<if test="password!=null">
and password=#{password}
</if>
</select>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
如果不想写
where 1=1
,还有另外一种写法
<select id="findByCondition" resultType="user">
SELECT * from user
<where>
<if test="id!=null">
and id=#{id}
</if>
<if test="username!=null">
and username=#{username}
</if>
<if test="password!=null">
and password=#{password}
</if>
</where>
</select>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
对应的Mapper接口
List<User> findByCondition(User user);
- 1
测试方法
@Test
public void testFindByCondition() {
User user = new User();
user.setId(1);
//user.setUsername("tom");
//user.setPassword("123");
List<User> users = userMapper.findByCondition(user);
users.forEach(u -> System.out.println(u));
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
运行测试方法,控制台log输出
09:19:48,535 DEBUG findByCondition:159 - ==> Preparing: SELECT * FROM user WHERE id=? and username=? and password=?
09:19:48,592 DEBUG findByCondition:159 - ==> Parameters: 1(Integer), tom(String), 123(String)
09:19:48,623 DEBUG findByCondition:159 - <== Total: 1
User{id=1, username='tom', password='123'}
- 1
- 2
- 3
- 4
7.2._动态SQL语句set
我们根据实体类属性的不同取值,使用不同的SQL语句来进行修改。比如在username不为空时对username进行修改,如果password不为空时还要对password进行修改。这种情况在我们的多条件组合查询中经常会碰到。映射文件如下:
<update id="chgByCondition">
update user
<set>
<if test="username != null">
username=#{username},
</if>
<if test="password != null">
password=#{password},
</if>
</set>
where id=#{id}
</update>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
对应的Mapper接口
void chgByCondition(User user);
- 1
测试方法
@Test
public void testFindByCondition() {
User user = new User();
user.setId(6L);
user.setPassword("111");
userMapper.chgByCondition(user);
sqlSession.commit();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
7.3._动态SQL语句trim
<trim prefix="" suffix="" prefixOverrides="" suffixOverrides="">`代替`<where>、<set>
- 1
<!--
prefix:自动加入前缀
prefixOverrides:自动忽略第一个“and”或者“or”
-->
<select id="findByCondition1" resultType="user">
select * from user
<trim prefix="where" prefixOverrides="and|or">
<if test="username!=null">
and name like concat('%%', #{username}, '%%')
</if>
<if test="password!=null">
and password=#{password}
</if>
</trim>
</select>
<!--
prefix:自动加入前缀
suffixOverrides:自动忽略最后一个”,“
-->
<update id="chgByCondition1" parameterType="user">
update t_users
<trim prefix="set" suffixOverrides=",">
<if test="username!=null">
name=#{username},
</if>
<if test="password!=null">
password=#{password},
</if>
</trim>
where id=#{id}
</update>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
7.4._动态SQL语句foreach
foreach用来循环执行sql的拼接操作,例如:
SELECT * FROM user WHERE id IN (1,2,3)
。
<select id="findByIds" resultType="user">
SELECT * from user
<where>
<!--
collection:代表要遍历的集合元素,注意编写时不要写#{}
open:代表语句的开始部分
close:代表结束部分
item:代表遍历集合的每个元素,生成的变量名
sperator:代表分隔符
-->
<foreach collection="list" open="id in (" close=")" separator="," item="id">
#{id}
</foreach>
</where>
</select>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
对应的Mapper接口
List<User> findByIds(List<Integer> ids);
- 1
测试方法
@Test
public void testFindByIds() {
List<Integer> ids = Arrays.asList(1, 2);
List<User> list = userMapper.findByIds(ids);
list.forEach(u -> System.out.println(u));
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
7.5._SQL片段抽取
目的:将重复的SQL提取出来,使用时用include引用即可,最终达到SQL重用的目的,减少代码冗余。
<?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.qfedu.mapper.UserMapper">
<sql id="selectAll">
SELECT * FROM user
</sql>
<select id="findByIds" resultType="user">
<include refid="selectAll" />
<where>
<foreach collection="list" open="id in (" close=")" separator="," item="id">
#{id}
</foreach>
</where>
</select>
</mapper>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
8._MyBatis多表查询
8.1._一对一查询
用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户
一对一查询的需求:查询一个订单,与此同时查询出该订单所属的用户。
User类 | Order类 | OrderMapper接口 |
---|---|---|
public class User { private Integer id; private String username; private String password; } | public class Order { private Integer id; private Date ordertime; private Double total; private User user; //代表当前订单从属于哪一个客户,注意这个属性 } | public interface OrderMapper { List findAll(); } |
8.1.1._配置对应的映射文件
OrderMapper.xml
<!--
resultMap:完成结果映射,表的字段到对象属性的映射,在表的字段名和对象属性名不相同时通常会被用到
id:设置主键列的对应关系
result:设置普通列的对应关系
column:表的字段名
property:对象的属性名
这种映射关系了解即可,通常不用这种方式
-->
<!--
<resultMap id="orderMap" type="order">
<id column="id" property="id" />
<result column="ordertime" property="ordertime" />
<result column="total" property="total" />
<result column="uid" property="user.id" />
<result column="username" property="user.username" />
<result column="password" property="user.password" />
</resultMap>
-->
<resultMap id="orderMap" type="order">
<id column="id" property="id" />
<result column="ordertime" property="ordertime" />
<result column="total" property="total" />
<!--
association:用于建立一对一的关系
javaType:指定属性的类型
-->
<association property="user" javaType="user">
<id column="uid" property="id" />
<result column="username" property="username" />
<result column="password" property="password" />
</association>
</resultMap>
<!--
resultMap:用于指定要使用的resultMap
-->
<select id="findAll" resultMap="orderMap">
SELECT
o.*, u.id uid, u.username username, u.password password
FROM
`order` o, user u
WHERE
o.uid=u.id;
</select>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
8.1.2._编写测试类
public class Mytest {
private OrderMapper orderMapper;
private SqlSession sqlSession;
@Before
public void before() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(resourceAsStream);
sqlSession = factory.openSession();
userMapper = sqlSession.getMapper(UserMapper.class);
orderMapper = sqlSession.getMapper(OrderMapper.class);
}
@After
public void after() {
sqlSession.close();
}
@Test
public void test1() {
List<Order> orders = orderMapper.findAll();
for (Order order : orders) {
System.out.println(order);
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
8.1.3._另一种方式
OrderMapper.xml
<?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.qfedu.mapper.OrderMapper">
<resultMap id="orderMap" type="order">
<id column="id" property="id" />
<result column="ordertime" property="ordertime" />
<result column="total" property="total" />
<!--
association:用于建立一对一的关系
javaType:指定属性的类型
column: 数据库中的列名,或者是列的别名,被设置为对应嵌套Select语句的参数
select:用于加载复杂类型属性的映射语句的 ID,
它会从 column 属性指定的列中检索数据,作为参数传递给目标 select 语句。
注意这里select中的写法,这里需要UserMapper.xml中相应ID处有对应的SQL语句
-->
<association property="user" column="uid" javaType="user" select="com.qfedu.mapper.UserMapper.findById" />
</resultMap>
<select id="findAll" resultMap="orderMap">
SELECT * FROM `order`
</select>
</mapper>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
UserMapper.xml
<?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.qfedu.mapper.UserMapper">
<select id="findById" resultType="user">
SELECT * FROM user WHERE id=#{id}
</select>
</mapper>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
8.2._一对多查询
用户表和订单表的关系为,一个用户有多个订单,一个订单只从属于一个用户。
一对多查询的需求:查询一个用户,与此同时查询出该用户具有的订单。
修改User类 | 创建UserMapper接口 |
---|---|
public class User { private Integer id; private String username; private String password; //代表当前用户拥有的多个订单 private List orders; //set和get方法 //toString方法 } | public interface UserMapper { List findAll(); } |
8.2.1._配置对应的映射文件
<mapper namespace="com.qfedu.mapper.UserMapper">
<resultMap id="userMap" type="user">
<id column="id" property="id" />
<result column="username" property="username" />
<result column="password" property="password" />
<!--
collection:关联一个集合
-->
<collection property="orders" ofType="order">
<id column="oid" property="id" />
<result column="ordertime" property="ordertime" />
<result column="total" property="total" />
</collection>
</resultMap>
<select id="findAll" resultMap="userMap">
SELECT
u.*, o.id oid, o.ordertime ordertime, o.total total
FROM
user u, `order` o
WHERE
u.id=o.uid;
</select>
</mapper>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
8.2.2._编写测试类
@Test
public void test2() {
List<User> users = userMapper.findAll();
for (User user : users) {
System.out.println(user);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
8.2.3._另一种方式
UserMapper.xml
<?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.qfedu.mapper.UserMapper">
<resultMap id="userMap" type="user">
<id column="id" property="id" />
<result column="username" property="username" />
<result column="password" property="password" />
<!--
collection:关联一个集合
column: 数据库中的列名,或者是列的别名,被设置为对应嵌套Select语句的参数
select:用于加载复杂类型属性的映射语句的 ID,
它会从 column 属性指定的列中检索数据,作为参数传递给目标 select 语句。
注意这里select中的写法,这里需要UserMapper.xml中相应ID处有对应的SQL语句
-->
<collection property="orders" column="id" ofType="order" select="com.qfedu.mapper.OrderMapper.findByUid" />
</resultMap>
<select id="findAll" resultMap="userMap">
SELECT * FROM user
</select>
</mapper>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
OrderMapper.xml
<?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.qfedu.mapper.OrderMapper">
<select id="findByUid" resultType="order">
SELECT * FROM `order` WHERE uid=#{id}
</select>
</mapper>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
8.3._多对多查询
用户表和角色表的关系为,一个用户有多个角色,一个角色被多个用户使用。
多对多关系通常需要有第三张表维护两个表之间的关系。
多对多查询的需求:查询用户同时查询出该用户的所有角色。
整个过程和“一对多”查询类似,我们可以把多对多查询理解为双向一对多查询。
SysUser实体类 | SysRole实体类 |
---|---|
public class SysUser { private Long id; private String username; private String email; private String password; private String phoneNum; private List roles; //set和get方法 //toString方法 } | public class SysRole { private Long id; private String roleName; private String roleDesc; private List users; //set和get方法 //toString方法 } |
SysUserMapper.java | SysRoleMapper.java |
---|---|
public interface SysUserMapper { List findAll(); } | public interface SysRoleMapper { List findAll(); } |
8.3.1._创建接口映射文件
SysUserMapper.xml
<?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.qfedu.mapper.SysUserMapper">
<resultMap id="userMap" type="sysuser">
<id column="id" property="id" />
<result column="username" property="username" />
<result column="email" property="email" />
<result column="password" property="password" />
<result column="phoneNum" property="phoneNum" />
<collection property="roles" ofType="sysrole">
<id column="rid" property="id" />
<result column="roleName" property="roleName" />
<result column="roleDesc" property="roleDesc" />
</collection>
</resultMap>
<select id="findAll" resultType="sysuser" resultMap="userMap">
SELECT
u.*, r.id rid, r.roleDesc roleDesc, r.roleName roleName
FROM
sys_user u, sys_user_role ur, sys_role r
WHERE
u.id=ur.userId AND r.id=ur.roleId
</select>
</mapper>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
SysRoleMapper.xml同SysUserMapper.xml,多对多可以理解为两个一对多
8.3.2._编写测试类
@Test
public void test3() {
List<SysUser> users = sysUserMapper.findAll();
System.out.println(users);
}
@Test
public void test4() {
List<SysRole> roles = sysRoleMapper.findAll();
System.out.println(roles);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
9._分页-pagehelper
MyBatis可以使用第三方的插件来对功能进行扩展,分页助手PageHelper是将分页的复杂操作进行封装,使用简单的方式即可获得分页的相关数据。
开发步骤:
- 在pom.xml中添加相关依赖;
- 在核心配置文件配置PageHelper插件;
- 测试。
9.1._在pom.xml中添加相关依赖
<!-- 分页助手 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>3.7.5</version>
</dependency>
<dependency>
<groupId>com.github.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
<version>0.9.1</version>
</dependency>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
9.2._在核心配置文件配置PageHelper插件
<!-- 配置插件 -->
<plugins>
<!-- 配置分页插件 -->
<plugin interceptor="com.github.pagehelper.PageHelper">
<!-- 配置分页插件方言 -->
<property name="dialect" value="mysql" />
</plugin>
</plugins>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
9.3._测试
@Test
public void test3() {
PageHelper.startPage(2, 2);
List<User> users = userMapper.findAll();
users.forEach(item -> System.out.println(item));
PageInfo<Student> pageInfo = new PageInfo<>(users);
System.out.println("总记录数:" + pageInfo.getTotal());
System.out.println("当前页:" + pageInfo.getPageNum());
System.out.println("总页数:" + pageInfo.getPages());
System.out.println("上一页:" + pageInfo.getPrePage());
System.out.println("下一页:" + pageInfo.getNextPage());
System.out.println("是否是首页:" + pageInfo.isIsFirstPage());
System.out.println("是否是尾页:" + pageInfo.isIsLastPage());
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
10._缓存(Cache)
内存中的一块存储空间,服务于某个应用程序,旨在将频繁读取的数据临时保存在内存中,便于二次快速访问。
10.1._一级缓存
SqlSession级别的缓存,同一个SqlSession的发起多次同构查询,会将数据保存在一级缓存中。
注意:无需任何配置,默认开启一级缓存。
/**
* 1、MyBatis一级缓存,一级缓存在SqlSession中
* 2、如果不关闭Sqlsession,那么SqlSession中的内容会一直存在
*
* 如何验证
* 1.多次查询相同的内容
* 2.每次查询完成之后不关闭SqlSession
* 3.通过日志查看发送了几次SQL
* |---如果是1次---SqlSession中的缓存是存在的
*/
@Test
public void testLevel1() throws IOException {
//加载配置文件
InputStream in = Resources.getResourceAsStream("mybatis_config.xml");
//创建Session工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//获取session
SqlSession session = factory.openSession();
UsersMapper usersMapper = session.getMapper(UserMapper.class);
Users u1 = usersMapper.queryById(1);
System.out.println("---------------------------" + u1);
Users u2 = usersMapper.queryById(1);
System.out.println("+++++++++++++++++++++++++++" + u2);
session.close();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
10.2._二级缓存
SqlSessionFactory级别的缓存,同一个SqlSessionFactory构建的SqlSession发起的多次同构查询,会将数据保存在二级缓存中。
注意:在sqlSession.commit()或者sqlSession.close()之后生效。
10.2.1._开启全局缓存
<settings>
是MyBatis中极为重要的调整设置,他们会改变MyBatis的运行行为,其他详细配置可参考官方文档。
<configuration>
<properties .../>
<!-- 注意书写位置 -->
<settings>
<!-- mybaits-config.xml中开启全局缓存(默认开启) -->
<setting name="cacheEnabled" value="true"/>
</settings>
<typeAliases></typeAliases>
</configuration>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
10.2.2._指定Mapper缓存
<mapper namespace="com.qfedu.mapper.UserMapper">
<cache />
...
...
</mapper>
- 1
- 2
- 3
- 4
- 5
代码验证
/**
* SqlSessionFactory中的缓存是二级缓存,默认不开启
*
* 如果开启了二级缓存,如何将查询出的信息存放在二级缓存中?
* 1、session.commit();
* 2、session.close;
* 如何验证二级缓存是否存在
* 1.多次查询看是否发送了一个SQL语句
* 2.进行一次查询之后就要commit()或者close连接
*
*/
@Test
public void testLevel2() throws IOException {
//加载配置文件
InputStream in = Resources.getResourceAsStream("mybatis_config.xml");
//创建Session工厂
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
//获取session
SqlSession session = factory.openSession();
UsersMapper usersMapper = session.getMapper(UserMapper.class);
Users u1 = usersMapper.queryById(1);
System.out.println("------------------" + u1);
//让二级缓存生效
session.close();
session = factory.openSession();
usersMapper = session.getMapper(UsersMapper.class);
Users u2 = usersMapper.queryById(1);
System.out.println("++++++++++++++++++++" + u2);
session.close();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33