推荐b站up主: 三更草堂
强烈建议新手看看三更讲师的视频, 简单易懂
本笔记仅供个人学习, 笔记大部分存在CV
Mybatis
搭建Mybatis环境
创建maven项目
导入依赖
<dependencies>
<!--mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.13</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<!--lombok依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.26</version>
</dependency>
<!--junit测试依赖-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--log4j依赖-->
<!--<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>-->
<!--分页查询依赖-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>4.0.0</version>
</dependency>
</dependencies>
编写核心配置
-
在resources中创建 mybatis-config.xml 和 jdbc.properties 资源包
-
mybatis-config.xml 只需要改动 "properties"标签 和 映射文件 "mapper"
<?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读取配置文件-->
<properties resource="jdbc.properties"></properties><!--jdbc相对路径-->
<environments default="development"> <!--默认环境id=development-->
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<!--获取配置文件中配置对应的值来设置连接相关参数-->
<property name="driver" value="${jdbc.driver}"/> <!--com.mysql.cj.jdbc.Driver-->
<property name="url" value="${jdbc.url}"/> <!--jdbc:mysql://localhost:3306/mybatis_db-->
<property name="username" value="${jdbc.username}"/> <!--root-->
<property name="password" value="${jdbc.password}"/> <!--123456-->
</dataSource>
</environment>
</environments>
<!-- mapper资源-->
<mappers>
<package name="com.sangeng.dao"/> <!--包下所有类-->
</mappers>
</configuration>
jdbc.url=jdbc:mysql://localhost:3306/mybatis_db
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.username=root
jdbc.password=123456
高效编程
-
添加mybatis-config.xml文件的 代码模板, 下次可以直接新建该xml
-
安装插件 Free Mybatis plugin (该插件可以自动生成 接口的实现类对象UserDao.xml 以及 自动生成 接口中的方法 SQL语句的括号)
-
安装了插件之后 就可以 只需要在resources中创建 com/sangeng/dao 文件夹后, 在UserDao接口中 的名字 [ Alt + 回车 ] 选中 mybatis插件 实现创建 实现类对象的头
-
接口的方法 findAll() [ Alt + 回车 ] 就可以 在实现类对象中 创建该sql语句的括号, 我们只需要 填充写入sql语句就行
项目内容
软件包名和类名:
-
DAO: (Data Access Object)是一个数据访问接口,数据访问:顾名思义就是与数据库打交道,夹在业务逻辑与数据库资源中间。
-
POJO: POJO(Plain Ordinary Java Object)简单的Java对象,实际就是普通JavaBeans, 其中有一些属性及其getter setter方法的类, 基本注释@ToString, @Data, @AllArgsConstructor
数据准备
CREATE DATABASE /*!32312 IF NOT EXISTS*/`mybatis_db` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `mybatis_db`;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`address` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
insert into `user`(`id`,`username`,`age`,`address`) values (1,'UZI',19,'上海'),(2,'PDD',25,'上海');
实体类
@ToString
@Data
@AllArgsConstructor
public class User {
private Integer id;
private String username;
private Integer age;
private String address;
}
定义接口以及对应的xml映射文件
com.sangeng.dao.UserDao:
public interface UserDao {
List<User> findAll();
}
资源目录下:com/sangeng/dao/UserDao.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.sangeng.dao.UserDao">
<select id="findAll" resultType="com.sangeng.pojo.User">
select * from user
</select>
</mapper>
测试类 Demo
public static void main(String[] args) throws IOException {
//定义mybatis配置文件的路径
String resource = "mybatis-config.xml";
//读取配置文件路径mybatis-config.xml文件
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//获取Sqlsession对象
SqlSession sqlSession = sqlSessionFactory.openSession();
//获取UserDao实现类对象
UserDao userDao = sqlSession.getMapper(UserDao.class);
//调用方法测试
List<User> userList = userDao.findAll();
System.out.println(userList);
//释放资源
sqlSession.close();
}
或者 我们也可以在测试包中进行测试
public class MybatisTest {
private SqlSession session;
@Before
public void init() throws IOException {
//定义mybatis配置文件的路径
String resource = "mybatis-config.xml";
// 读取配置文件, 拿到对应的输入流
InputStream inputStream = Resources.getResourceAsStream(resource);
// 传入对应的配置文件的输入流,读取配置文件获取 SqlSessionFactory 对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 通过SqlSessionFactory获取Sqlsession对象(Sqlsession对象理解为是一个数据库的连接)
// 获取Sqlsession对象
session = sqlSessionFactory.openSession();
}
@Test
public void testUser(){
UserDao userDao = session.getMapper(UserDao.class);
// 获取UserDao实现类对象 - 拿到对应的接口的实现类对象
List<User> userList = userDao.findAll();
System.out.println(userList);
}
// 提交事务, 释放资源
@After
public void destory(){
session.commit();
session.close();
}
}
修改操作
日常增删改查操作
当基本的框架和基本的方法成功实现, 我们在日常生活中对项目添加增删改查的操作步骤
-
定义 接口 UserDao 并且在里面定义方法 findUser
-
方法对应的xml映射文件, 在UserDao.xml 文件 添加sql语句
-
mybatis-config.xml文件中 添加 mapper设置
-
demo中运行, 结束
参数
-
一个参数: 用 #{}
-
多个参数: 记得加上@Parm, 声明他的名称
User findByCondition(@Param("id") Integer id, @Param("username") String username); @Parm注解指定给哪个参数命名
获取参数时 #{}和${}的区别
如果使用#{}.他是预编译的sql可以防止SQL注入攻击 如果使用${}他是直接把参数值拿来进行拼接,这样会有SQL注入的危险
如果使用的是#{}来获取参数值日志如下: Preparing: select * from user where id = ? and username = ? and age = ? and address = ? Parameters: 2(Integer), 快乐风男(String), 29(Integer), 北京(String)
如果使用${}来获取参数值日志如下: Preparing: select * from user where id = 2 and username = 快乐风男 and age = 29 and address= 北京
核心类
SqlSessionFactory
SqlSessionFactory是一个SqlSession的工厂类。主要用来获取SqlSession对象。
成员方法如下:
SqlSession openSession(); //获取SqlSession对象,传入的参数代表创建的SqlSession是否自动提交 SqlSession openSession(boolean autoCommit);
SqlSession
SqlSession 提供了在数据库执行 SQL 命令所需的所有方法 。它还提供了事务的相关操作。
成员方法如下:
T getMapper(Class<T> type);//获取mapper对象 void commit();//提交事务 void rollback();//回滚事务 void close();//释放资源
Mybatis实现增删改查
新增
-
接口中增加相关方法
void insertUser(User user);
-
映射文件UserDao.xml增加响应的标签
<insert id="insertUser">
insert into user values(null,#{username},#{age},#{address})
</insert>
-
注意:增删改查除了查询其他都要提交事务
删除
-
接口中增加相关方法
void deleteById(Integer id);
-
映射文件UserDao.xml增加响应的标签
<delete id="deleteById">
delete from user where id = #{id}
</delete>
更改
-
接口中增加相关方法
void updateUser(User user);
-
映射文件UserDao.xml增加响应的标签
<!--更新用户-->
<update id="updateUser">
UPDATE USER SET age = #{age} , username = #{username},address = #{address} WHERE id = #{id}
</update>
根据Id查询
-
接口中增加相关方法
User findById(Integer id);
-
映射文件UserDao.xml增加响应的标签
<select id="findById" resultType="com.sangeng.pojo.User">
select * from user where id = #{id}
</select>
查询所有
-
接口中增加相关方法
List<User> findAll();
-
映射文件UserDao.xml增加响应的标签
<select id="findAll" resultType="com.sangeng.pojo.User">
select * from user
</select>
配置文件详解
properties
可以使用properties读取properties配置文件。使用其中的resource属性来设置配置文件的路径。
然后使用${key}来获取配置文件中的值
例如:
在resources目录下有jdbc.properties文件,内容如下:
jdbc.url=jdbc:mysql://localhost:3306/mybatis_db
jdbc.driver=com.mysql.jdbc.Driver
jdbc.username=root
jdbc.password=root
在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="jdbc.properties"></properties>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<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>
</configuration>
settings
可以使用该标签来设置进行一些设置
例如:
<settings>
<!--开启自动驼峰命名映射-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
具体的设置参考:mybatis – MyBatis 3 | 配置
typeAliases
可以用来设置给全类名设置别名,简化书写。一般设置一个包下的类全部具有默认别名。默认别名是类目首字母小写。例如:com.sangeng.pojo.User别名为user
<typeAliases>
<package name="com.sangeng.dao"></package>
</typeAliases>
environments
配置数据库相关的环境,例如事物管理器,连接池相关参数等。 配置多个数据库环境, 每个数据库对应一个SQLsessionFactory, 有三个数据库就要三个实例
<!--设置默认环境-->
<environments default="development">
<!--设置该环境的唯一标识-->
<environment id="development">
<transactionManager type="JDBC"/>
<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
该标签的作用是加载映射的,加载方式有如下几种(主要使用第四种):
①使用相对于类路径的资源引用,例如:
<!-- 使用相对于类路径的资源引用 -->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
②使用完全限定资源定位符(URL),例如:
<!-- 使用完全限定资源定位符(URL) -->
<mappers>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
<mapper url="file:///var/mappers/BlogMapper.xml"/>
<mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
③使用映射器接口实现类的完全限定类名,例如:
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
④将包内的映射器接口实现全部注册为映射器,例如:
<!-- 定义dao接口所在的包。要求xml文件存放的路径和dao接口的包名要对应 -->
<mappers>
<package name="org.mybatis.builder"/>
</mappers>
打印日志
①log4j配置 在resources目录下创建log4j.properties文件,内容如下:
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### direct messages to file mylog.log ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=c:/mylog.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### set log levels - for more verbose logging change 'info' to 'debug' ###
log4j.rootLogger=debug, stdout
②引入依赖
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
注解开发
可以使用注解的形式来进行开发,用注解来替换掉xml。 使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不从心,还会让你本就复杂的 SQL 语句更加混乱不堪。 所以我们在实际企业开发中一般都是使用XML的形式。
①在核心配置文件中配置mapper接口所在的包名
<mappers>
<package name="com.sangeng.dao"></package>
</mappers>
②在接口对应方法上使用注解来配置需要执行的sql
public interface UserDao {
@Select("select * from user")
List<User> findAll();
@Insert("insert into user values(null,#{username},#{age},#{address})")
void insertUser(User user);
@Update("UPDATE USER SET age = #{age} , username = #{username},address = #{address} WHERE id = #{id}")
void updateUser(User user);
@Delete("delete from user where id = #{id}")
void deleteById(Integer id);
}
③和之前的一样获取Mapper调用方法, 在测试类中运行即可
动态SQL
在实际开发中的SQL语句没有之前的这么简单,很多时候需要根据传入的参数情况动态的生成SQL语句。Mybatis提供了动态SQL相关的标签让我们使用。
2.1 if
可以使用if标签进行条件判断,条件成立才会把if标签中的内容拼接进sql语句中。
例如:
<select id="findByCondition" resultType="com.sangeng.pojo.User">
select * from user
where id = #{id}
<if test="username!=null">
and username = #{username}
</if>
</select>
如果参数username为null则执行的sql为:select * from user where id = ?
如果参数username不为null则执行的sql为:select * from user where id = ? and username = ?
注意:在test属性中表示参数的时候不需要写#{},写了会出问题。
2.2 trim
可以使用该标签动态的添加前缀或后缀,也可以使用该标签动态的消除前缀。
2.2.1 prefixOverrides属性
用来设置需要被清除的前缀,多个值可以用|分隔,注意|前后不要有空格。例如: and|or
例如:
<select id="findByCondition" resultType="com.sangeng.pojo.User">
select * from user
<trim prefixOverrides="and|or" >
and
</trim>
</select>
最终执行的sql为: select * from user
2.2.2 suffixOverrides属性
用来设置需要被清除的后缀,多个值可以用|分隔,注意|前后不要有空格。例如: and|or
例如:
<select id="findByCondition" resultType="com.sangeng.pojo.User">
select * from user
<trim suffixOverrides="like|and" >
where 1=1 like
</trim>
</select>
最终执行的sql为: select * from user 去掉了后缀like
2.2.3 prefix属性
用来设置动态添加的前缀,如果标签中有内容就会添加上设置的前缀
例如:
<select id="findByCondition" resultType="com.sangeng.pojo.User">
select * from user
<trim prefix="where" >
1=1
</trim>
</select>
最终执行的sql为:select * from user where 1=1 动态增加了前缀where
2.2.4 suffix属性
用来设置动态添加的后缀,如果标签中有内容就会添加上设置的后缀
<select id="findByCondition" resultType="com.sangeng.pojo.User">
select * from user
<trim suffix="1=1" >
where
</trim>
</select>
最终执行的sql为:select * from user where 1=1 动态增加了后缀1=1
2.2.5 动态添加前缀where 并且消除前缀and或者or
User findByCondition(@Param("id") Integer id,@Param("username") String username);
<select id="findByCondition" resultType="com.sangeng.pojo.User">
select * from user
<trim prefix="where" prefixOverrides="and|or" >
<if test="id!=null">
id = #{id}
</if>
<if test="username!=null">
and username = #{username}
</if>
</trim>
</select>
调用方法时如果传入的id和username为null则执行的SQL为:select * from user
调用方法时如果传入的id为null,username不为null,则执行的SQL为:select * from user where username = ?
2.2 where
where标签等价于:
<trim prefix="where" prefixOverrides="and|or" ></trim>
可以使用where标签动态的拼接where并且去除前缀的and或者or。
例如:
<select id="findByCondition" resultType="com.sangeng.pojo.User">
select * from user
<where>
<if test="id!=null">
id = #{id}
</if>
<if test="username!=null">
and username = #{username}
</if>
</where>
</select>
如果id和username都为null,则执行的sql为:select * from user
如果id为null,username不为null,则执行的sql为:select * from user where username = ?
2.3 set
set标签等价于
<trim prefix="set" suffixOverrides="," ></trim>
可以使用set标签动态的拼接set并且去除后缀的逗号。
例如:
<update id="updateUser">
UPDATE USER
<set>
<if test="username!=null">
username = #{username},
</if>
<if test="age!=null">
age = #{age},
</if>
<if test="address!=null">
address = #{address},
</if>
</set>
where id = #{id}
</update>
如果调用方法时传入的User对象的id为2,username不为null,其他属性都为null则最终执行的sql为:UPDATE USER SET username = ? where id = ?
2.4 foreach
可以使用foreach标签遍历集合或者数组类型的参数,获取其中的元素拿来动态的拼接SQL语句。
例如:
方法定义如下
List<User> findByIds(@Param("ids") Integer[] ids);
如果期望动态的根据实际传入的数组的长度拼接SQL语句。例如传入长度为4个数组最终执行的SQL为:
select * from User WHERE id in( ? , ? , ? , ?, ? )
则在xml映射文件中可以使用以下写法
<select id="findByIds" resultType="com.sangeng.pojo.User">
select * from User
<where>
<foreach collection="ids" open="id in(" close=")" item="id" separator=",">
#{id}
</foreach>
</where>
</select>
collection:表示要遍历的参数。
open:表示遍历开始时拼接的语句
item:表示给当前遍历到的元素的取的名字
separator:表示每遍历完一次拼接的分隔符
close:表示最后一次遍历完拼接的语句
注意:如果方法参数是数组类型,默认的参数名是array,如果方法参数是list集合默认的参数名是list。建议遇到数组或者集合类型的参数统一使用@Param注解进行命名。
2.5 choose、when、otherwise
当我们不想使用所有的条件,而只是想从多个条件中选择一个使用时。可以使用choose系列标签。类似于java中的switch。
例如:
接口中方法定义如下
List<User> selectChose(User user);
期望:
如果user对象的id不为空时就通过id查询。
如果id为null,username不为null就通过username查询。
如果id和username都会null就查询id为3的用户
xml映射文件如下
<select id="selectChose" resultType="com.sangeng.pojo.User">
select * from user
<where>
<choose>
<when test="id!=null">
id = #{id}
</when>
<when test="username!=null">
username = #{username}
</when>
<otherwise>
id = 3
</otherwise>
</choose>
</where>
</select>
-
choose类似于java中的switch
-
when类似于java中的case
-
otherwise类似于java中的dufault
一个choose标签中最多只会有一个when中的判断成立。从上到下去进行判断。如果成立了就把标签体的内容拼接到sql中,并且不会进行其它when的判断和拼接。如果所有的when都不成立则拼接otherwise中的语句。
总结
1. if 标签进行条件判断,条件成立才会把if标签中的内容拼接进sql语句中 2. trim 之 prefixOverrides="and|or" 设置需要被清除掉的前缀"and|or" 3. trim 之 suffixOverrides="and|or" 设置需要被清除掉的后缀"and|or" 4. trim 之 prefix和suffix"where" 动态添加前缀和后缀, 标签内有内容则自动添加"里面的内容" 5. trim 综合运用 看UserDao代码 6. where trim升级版, 自动拼接where和动态清除前缀and与or 等价于 <trim prefix="where" prefixOverrides="and|or"> 7. set 在更新数据的时候, 我们往往希望的是只更新其中的一条数据, 比如更新这个人的性别 8. foreach 对参数是集合或者数组遍历的效果 id in ( ?, ?, ? ) <foreach collection="ids" open="id in( " close=")" item="id" separator=","> 9. choose标签 选择其中之一的条件去满足
SQL片段抽取
我们在xml映射文件中编写SQL语句的时候可能会遇到重复的SQL片段。这种SQL片段我们可以使用sql标签来进行抽取。然后在需要使用的时候使用include标签进行使用。
例如:
<!-- 可以将重复,频繁的SQL语句进行打包 -->
<sql id="baseSelect" >id,username,age,address</sql>
<select id="findAll" resultType="com.sangeng.pojo.User">
select <include refid="baseSelect"/> from user
</select>
最终执行的sql为: select id,username,age,address from user
案例环境
0.1 案例数据初始化sql
CREATE DATABASE /*!32312 IF NOT EXISTS*/`mybatis_db` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `mybatis_db`;
DROP TABLE IF EXISTS `orders`;
CREATE TABLE `orders` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`createtime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',
`price` int(11) DEFAULT NULL COMMENT '价格',
`remark` varchar(100) DEFAULT NULL COMMENT '备注',
`user_id` int(11) DEFAULT NULL COMMENT '用户id',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
insert into `orders`(`id`,`createtime`,`price`,`remark`,`user_id`) values (1,'2014-06-26 16:55:43',2000,'无',2),(2,'2021-02-23 16:55:57',3000,'无',3),(3,'2021-02-23 16:56:21',4000,'无',2);
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) DEFAULT NULL COMMENT '角色名',
`desc` varchar(100) DEFAULT NULL COMMENT '角色描述',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
/*Data for the table `role` */
insert into `role`(`id`,`name`,`desc`) values (1,'总经理','一人之下'),(2,'CFO',NULL);
/*Table structure for table `user` */
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`address` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=33 DEFAULT CHARSET=utf8;
/*Data for the table `user` */
insert into `user`(`id`,`username`,`age`,`address`) values (2,'pdd',26,NULL),(3,'UZI',19,'上海11'),(4,'RF',19,NULL);
/*Table structure for table `user_role` */
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role` (
`user_id` int(11) DEFAULT NULL,
`role_id` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*Data for the table `user_role` */
insert into `user_role`(`user_id`,`role_id`) values (2,2),(2,1),(3,1);
0.2 实体类
0.2.1 User.java
public class User {
private Integer id;
private String username;
private Integer age;
private String address;
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public User() {
}
public User(Integer id, String username, Integer age, String address) {
this.id = id;
this.username = username;
this.age = age;
this.address = address;
}
}
0.2.2 Order.java
public class Order {
private Integer id;
private Date createtime;
private Integer price;
private String remark;
private Integer userId;
@Override
public String toString() {
return "Order{" +
"id=" + id +
", createtime=" + createtime +
", price=" + price +
", remark='" + remark + '\'' +
", userId=" + userId +
'}';
}
public Order() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Date getCreatetime() {
return createtime;
}
public void setCreatetime(Date createtime) {
this.createtime = createtime;
}
public Integer getPrice() {
return price;
}
public void setPrice(Integer price) {
this.price = price;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public Order(Integer id, Date createtime, Integer price, String remark, Integer userId) {
this.id = id;
this.createtime = createtime;
this.price = price;
this.remark = remark;
this.userId = userId;
}
}
0.2.3 Role.java
public class Role {
private Integer id;
private String name;
private String desc;
@Override
public String toString() {
return "Role{" +
"id=" + id +
", name='" + name + '\'' +
", desc='" + desc + '\'' +
'}';
}
public Role() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public Role(Integer id, String name, String desc) {
this.id = id;
this.name = name;
this.desc = desc;
}
}
1. ResultMap
1.1 基本使用
我们可以使用resultMap标签自定义结果集和实体类属性的映射规则。
<!--
resultMap 用来自定义结果集和实体类的映射
属性:
id 相当于这个resultMap的唯一标识
type 用来指定映射到哪个实体类
id标签 用来指定主键列的映射规则
属性:
property 要映射的属性名
column 对应的列名
result标签 用来指定普通列的映射规则
属性:
property 要映射的属性名
column 对应的列名
-->
<resultMap id="orderMap" type="com.sangeng.pojo.Order" >
<id column="id" property="id"></id>
<result column="createtime" property="createtime"></result>
<result column="price" property="price"></result>
<result column="remark" property="remark"></result>
<result column="user_id" property="userId"></result>
</resultMap>
<!--使用我们自定义的映射规则-->
<select id="findAll" resultMap="orderMap">
SELECT id,createtime,price,remark,user_id FROM ORDERS
</select>
1.2 自动映射
我们定义resultMap时默认情况下自动映射是开启状态的。也就是如果结果集的列名和我们的属性名相同是会自动映射的, 相同的可以删除, 我们只需要写特殊情况的映射关系即可。
例如:
下面这种写法和上面的写法会有相同的效果,因为其他属性的属性名和结果集的列名都是相同的会自动映射。
<resultMap id="orderMap" type="com.sangeng.pojo.Order" >
<result column="user_id" property="userId"></result>
</resultMap>
<!--使用我们自定义的映射规则-->
<select id="findAll" resultMap="orderMap">
SELECT id,createtime,price,remark,user_id FROM ORDERS
</select>
如有需要可以选择关闭自动映射可以把resultMap的autoMapping属性设置为false。
例如:
<resultMap id="orderMap" type="com.sangeng.pojo.Order" autoMapping="false">
<id column="id" property="id"></id>
<result column="createtime" property="createtime"></result>
<result column="price" property="price"></result>
<result column="remark" property="remark"></result>
<result column="user_id" property="userId"></result>
</resultMap>
1.3 继承映射关系
我们可以使用resultMap 的extends属性来指定一个resultMap,从而复用重复的映射关系配置。
例如:
<!--定义个父映射,供其他resultMap继承-->
<resultMap id="baseOrderMap" type="com.sangeng.pojo.Order" >
<id column="id" property="id"></id>
<result column="createtime" property="createtime"></result>
<result column="price" property="price"></result>
<result column="remark" property="remark"></result>
</resultMap>
<!--继承baseOrderMap,然后只需要写自己特有的映射关系即可-->
<resultMap id="orderMap" type="com.sangeng.pojo.Order" autoMapping="false" extends="baseOrderMap">
<result column="user_id" property="userId"></result>
</resultMap>
2. 多表查询
有的时候我们需要查询多张表的数据才可以得到我们要的结果。
我们可以直接写一个多表关联的SQL进行查询。也可以分步进行多次的查询来拿到我们需要的结果。
Mybatis就提供了对应的配置,可以让我们去更方便的进行相应的查询和对应的结果集处理。
2.1 多表关联查询
2.1.1 一对一关系
两个实体之间是一对一的关系。(例如我们需要查询订单,要求还需要下单用户的数据。这里的订单相对于用户是一对一。)
例如:
方法定义如下
//根据订单id查询订单,要求把下单用户的信息也查询出来 Order findById(Integer id);
因为期望Order中还能包含下单用户的数据,所以可以再Order中增加一个属性
private User user;
SQL语句如下
SELECT
o.id,o.`createtime`,o.`price`,o.`remark`,o.`user_id`,u.`id` uid,u.`username`,u.`age`,u.`address`
FROM
orders o,USER u
WHERE
o.`user_id` = u.`id`
AND o.id = 2
2.1.1.1 使用ResultMap对所有字段进行映射
可以使用ResultMap设置user对象的属性的映射规则。
①resultMap定义,主要是对user对象的属性设置映射规则
<resultMap id="baseOrderMap" type="com.sangeng.pojo.Order" >
<id column="id" property="id"></id>
<result column="createtime" property="createtime"></result>
<result column="price" property="price"></result>
<result column="remark" property="remark"></result>
</resultMap>
<resultMap id="orderMap" type="com.sangeng.pojo.Order" autoMapping="false" extends="baseOrderMap">
<result column="user_id" property="userId"></result>
</resultMap>
<!--Order和User关联的映射-->
<resultMap id="orderUserMap" type="com.sangeng.pojo.Order" autoMapping="false" extends="orderMap">
<result property="user.id" column="uid"></result>
<result property="user.username" column="username"></result>
<result property="user.age" column="age"></result>
<result property="user.address" column="address"></result>
</resultMap>
②使用定义好的resultMap
<!--根据订单id查询订单,要求把下单用户的信息也查询出来-->
<select id="findById" resultMap="orderUserMap">
SELECT
o.`id`,o.`createtime`,o.`price`,o.`remark`,o.`user_id`,u.`id` uid,u.`username`,u.`age`,u.`address`
FROM
orders o,`user` u
WHERE
o.id = #{id} AND
o.`user_id`=u.`id`
</select>
2.1.1.2 使用ResultMap中的association
可以使用ResultMap中的子标签association 来设置关联实体类的映射规则.
①定义resultMap
<resultMap id="baseOrderMap" type="com.sangeng.pojo.Order" >
<id column="id" property="id"></id>
<result column="createtime" property="createtime"></result>
<result column="price" property="price"></result>
<result column="remark" property="remark"></result>
</resultMap>
<resultMap id="orderMap" type="com.sangeng.pojo.Order" autoMapping="false" extends="baseOrderMap">
<result column="user_id" property="userId"></result>
</resultMap>
<!--Order和User关联的映射(使用association)-->
<resultMap id="orderUserMapUseAssociation" type="com.sangeng.pojo.Order" autoMapping="false" extends="orderMap">
<association property="user" javaType="com.sangeng.pojo.User">
<id property="id" column="uid"></id>
<result property="username" column="username"></result>
<result property="age" column="age"></result>
<result property="address" column="address"></result>
</association>
</resultMap>
②使用resultMap
<!--根据订单id查询订单,要求把下单用户的信息也查询出来-->
<select id="findById" resultMap="orderUserMapUseAssociation">
SELECT
o.`id`,o.`createtime`,o.`price`,o.`remark`,o.`user_id`,u.`id` uid,u.`username`,u.`age`,u.`address`
FROM
orders o,`user` u
WHERE
o.id = #{id} AND
o.`user_id`=u.`id`
</select>
2.1.2 一对多关系
两个实体之间是一对多的关系。(例如我们需要查询用户,要求还需要该用户所具有的角色信息。这里的用户相对于角色是一对多的。)
例如:
方法定义如下
//根据id查询用户,并且要求把该用户所具有的角色信息也查询出来 User findById(Integer id);
因为期望User中还能包含该用户所具有的角色信息,所以可以在User中增加一个属性
// 该用户所具有的角色 private List<Role> roles;
SQL语句如下
SELECT
u.`id`,u.`username`,u.`age`,u.`address`,r.id rid,r.name,r.desc
FROM
USER u,user_role ur,role r
WHERE
u.id=ur.user_id AND ur.role_id = r.id
AND u.id = 2
2.1.2.1 使用ResultMap中的collection
可以使用ResultMap中的子标签association 来设置关联实体类的映射规则.
①定义ResultMap
<!--定义User基本属性映射规则-->
<resultMap id="userMap" type="com.sangeng.pojo.User">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="age" column="age"></result>
<result property="address" column="address"></result>
</resultMap>
<resultMap id="userRoleMap" type="com.sangeng.pojo.User" extends="userMap">
<collection property="roles" ofType="com.sangeng.pojo.Role" >
<id property="id" column="rid"></id>
<result property="name" column="name"></result>
<result property="desc" column="desc"></result>
</collection>
</resultMap>
②使用ResultMap
<select id="findById" resultMap="userRoleMap" >
SELECT
u.`id`,u.`username`,u.`age`,u.`address`,r.id rid,r.name,r.desc
FROM
USER u,user_role ur,role r
WHERE
u.id=ur.user_id AND ur.role_id = r.id
AND u.id = #{id}
</select>
2.2 分步查询
无论一对多还是一对一, 都可以使用分布查询, 如果有需要多表查询的需求我们也可以选择用多次查询的方式来查询出我们想要的数据。Mybatis也提供了对应的配置。
例如我们需要查询用户,要求还需要查询出该用户所具有的角色信息。我们可以选择先查询User表查询用户信息。然后在去查询关联的角色信息。
2.2.1实现步骤
具体步骤如下:
①定义查询方法
因为我们要分两步查询: 1.查询User 2.根据用户的id查询Role 所以我们需要定义下面两个方法,并且把对应的标签也先写好
public interface RoleDao {
//根据userId查询所具有的角色
List<Role> findRoleByUserId(Integer userId);
}
<!--根据userId查询所具有的角色-->
<select id="findRoleByUserId" resultType="com.sangeng.pojo.Role">
select
r.id,r.name,r.desc
from
role r, user_role ur
where
ur.role_id = r.id
and ur.user_id = #{userId}
</select>
1.查询User
//根据用户名查询用户,并且要求把该用户所具有的角色信息也查询出来
User findByUsername(String username);
<!--根据用户名查询用户-->
<select id="findByUsername" resultType="com.sangeng.pojo.User">
select id,username,age,address from user where username = #{username}
</select>
2.根据user_id查询Role
②配置分步查询
我们期望的效果是调用findByUsername方法查询出来的结果中就包含角色的信息。所以我们可以设置findByUsername方法的RestltMap,指定分步查询
<resultMap id="userMap" type="com.sangeng.pojo.User">
<id property="id" column="id"></id>
<result property="username" column="username"></result>
<result property="age" column="age"></result>
<result property="address" column="address"></result>
</resultMap>
<!--
select属性:指定用哪个查询来查询当前属性的数据 写法:包名.接口名.方法名
column属性:设置当前结果集中哪列的数据作为select属性指定的查询方法需要参数
-->
<resultMap id="userRoleMapBySelect" type="com.sangeng.pojo.User" extends="userMap">
<collection property="roles"
ofType="com.sangeng.pojo.Role"
select="com.sangeng.dao.RoleDao.findRoleByUserId"
column="id">
</collection>
</resultMap>
指定findByUsername使用我们刚刚创建的resultMap
<!--根据用户名查询用户-->
<select id="findByUsername" resultMap="userRoleMapBySelect">
select id,username,age,address from user where username = #{username}
</select>
2.2.2 设置按需加载
我们可以设置按需加载,这样在我们代码中需要用到关联数据的时候才会去查询关联数据。
有两种方式可以配置分别是全局配置和局部配置
-
局部配置
设置fetchType属性为lazy
<resultMap id="userRoleMapBySelect" type="com.sangeng.pojo.User" extends="userMap"> <collection property="roles" ofType="com.sangeng.pojo.Role" select="com.sangeng.dao.RoleDao.findRoleByUserId" column="id" fetchType="lazy"> </collection> </resultMap>
-
全局配置
设置lazyLoadingEnabled为true
<settings> <setting name="lazyLoadingEnabled" value="true"/> </settings>
3.分页查询-PageHelper
我们可以使用PageHelper非常方便的帮我们实现分页查询的需求。不需要自己在SQL中拼接SQL相关参数,并且能非常方便的获取的总页数总条数等分页相关数据。相当于sql语句中的limit 。
3.1 实现步骤
①定义方法查询方法以及生成对应标签
List<User> findAll();
<select id="findAll" resultType="com.sangeng.pojo.User">
select id,username,age,address from user
</select>
② 引入依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>4.0.0</version>
</dependency>
③ 配置Mybatis核心配置文件使用分页插件
<plugins>
<!-- 注意:分页助手的插件 配置在通用馆mapper之前 -->
<plugin interceptor="com.github.pagehelper.PageHelper">
<!-- 指定方言 -->
<property name="dialect" value="mysql"/>
</plugin>
</plugins>
④ 开始分页查询
我们只需要在使用查询方法前设置分页参数即可
//设置分页参数
UserDao userDao = session.getMapper(UserDao.class);
//设置分页查询参数
PageHelper.startPage(1,1);
List<User> users = userDao.findAll();
System.out.println(users.get(0));
如果需要获取总页数总条数等分页相关数据,只需要创建一个PageInfo对象,把刚刚查询出的返回值做为构造方法参数传入。然后使用pageInfo对象获取即可。
PageInfo<User> pageInfo = new PageInfo<User>(users);
System.out.println("总条数:"+pageInfo.getTotal());
System.out.println("总页数:"+pageInfo.getPages());
System.out.println("当前页:"+pageInfo.getPageNum());
System.out.println("每页显示长度:"+pageInfo.getPageSize());
3.2 一对多多表查询分页问题
我们在进行一对多的多表查询时,如果使用了PageHelper进行分页。会出现关联数据不全的情况。我们可以使用分步查询的方式解决该问题。
4.Mybatis缓存
Mybatis的缓存其实就是把之前查到的数据存入内存(map),下次如果还是查相同的东西,就可以直接从缓存中取,从而提高效率。
Mybatis有一级缓存和二级缓存之分,一级缓存(默认开启)是sqlsession级别的缓存。二级缓存相当于mapper级别的缓存。
4.1 一级缓存
几种不会使用一级缓存的情况 1.调用相同方法但是传入的参数不同 2.调用相同方法参数也相同,但是使用的是另外一个SqlSession 3.如果查询完后,对同一个表进行了增,删改的操作,都会清空这sqlSession上的缓存 4.如果手动调用SqlSession的clearCache方法清除缓存了,后面也使用不了缓存
4.2 二级缓存
注意:只在sqlsession调用了close或者commit后的数据才会进入二级缓存。 问题: 第一个连接查询了id为2的数据后关闭连接, 第二个连接修改了id为2的信息, 第一个连接又打开连接查询id为2的信息后数据发生错乱
4.2.1 开启二级缓存
①全局开启
在Mybatis核心配置文件中配置
<settings> <setting name="cacheEnabled" value="true"/> </settings>
②局部开启
在要开启二级缓存的mapper映射文件中设置 cache标签
<?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.sangeng.dao.RoleDao">
<cache></cache>
</mapper>
4.2.2 使用建议
二级缓存在实际开发中基本不会使用。