MyBatis的基本概念及使用方法
SSM-MyBatis
重点:增删改查;查中包含:多对一:一对多;自定义映射;动态SQL等
第一章 MyBatis简介
1.SSM框架及整合
* MyBatis:封装JDBC,负责访问数据库,完成持久化操作。
* Spring:使用其核心思想IOC管理组件,使用AOP思想实现功能增强
* SpringMVC:接收浏览器发送的请求,并响应浏览器数据
* SSM整合:结合案例整合SSM,进一步了解各个框架的功能
2.MyBatis:
* 原称为iBatis,是一个基于Java的持久层框架;ibatis提供的持久层框架包括SQL Maps和Data Access Object(DAO)
* 优势:性能出色;java和SQL语句编码分开。java代码专注业务,SQL语句专注数据
3.MyBatis特性
1. 是支持定制化SQL、存储过程以及高级映射的优秀的持久层框架
* mybatis中的SQL语句都需要自己去写,因此在定制化SQL中,mybatis利于维护SQL语句;
2. MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集
* mybatis将大部分jdbc代码进行封装,不需要再手动写代码;SQL语句中的许多东西不能写死,jdbc中使用SQL拼接/占位符赋值进行,mybatis使用xml文件即可;结果集在mybatis中不需要再解析,能直接拿到
3. MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录
4. MyBatis是一个半自动的ORM(Object Relation Mapping-对象关系映射)框架
* mybatis实现数据库和java映射比较方便
第二章 搭建Mybatis
1.注意事项
2.创建Mybatis的核心配置文件
* 核心配置文件主要用于配制连接数据库的环境以及Mybatis的全局配置信息
* 映射文件的作用:设置如何操作数据库;
* 核心配置文件的作用:设置如何连接数据库
* 将映射文件配置到核心配置文件中,然后通过加载核心配置文件找到相对应的映射文件中的SQL语句,之后执行SQL语句达到操作数据库的目的
3.mapper接口
* mybatis中的mapper接口相当于以前的DAO,区别在于mapper仅仅是接口,不需要实现类
* mapper接口和映射文件要保证两个一致
1. mapper接口的全类名和映射文件的namespace一致
2. mapper接口中的方法的方法名要和映射文件中的SQL的ID保持一致
public interface UserMapper {
//添加用户信息
int insertUser();
//删除用户信息
void deleteUser(@Param("username") String username);
//更新用户信息
void updateUser(@Param("username") String username);
//查询用户信息
User getUserByName(@Param("username") String username);
}
<?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.gz.mapper.UserMapper">
<!-- int insertUser();-->
<insert id="insertUser">
insert into t_user values(null,'wanghao','123456',78,'男','121@qq.com')
</insert>
<!-- void deleteUser(@Param("username") String username);-->
<delete id="deleteUser">
delete from t_user where username = #{username}
</delete>
<!-- void updateUser(@Param("username") String username);-->
<update id="updateUser">
update t_user set age = 10 where username = #{username}
</update>
<!-- User getUserByName(@Param("username") String username);-->
<select id="getUserByName" resultType="User">
select * from t_user where username = #{username}
</select>
</mapper>
4.创建Maven工程的步骤
1. 在module中的pom.xml中配置好依赖:Mybatis核心、junit测试、mysql驱动
<dependencies>
<!-- Mybatis核心 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!-- junit测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.16</version>
</dependency>
</dependencies>
2. 创建Mybatis的核心配置文件:配置连接数据库的环境、Mybatis的全局配置信息
3. 创建表,及表对应映射的实体类
4. 创建mapper接口,相当于dao,用来操作数据库,但在mybatis中不需要实现类
5. 创建mybatis的映射文件;注意mybatis的映射文件包名和mapper接口中对应的mapper方法相对应(在本章第8段中详细讲解)
* 一个映射文件对应一个实体类,对应一张表的操作;
* mybatis映射文件用来编写SQL,操作数据库中的数据;
6. 通过Junit测试功能,步骤固定,因此可以作为一个utils工具类封装起来
@Test
public void testSelectById() throws IOException {
//1.获取核心配置文件输入流
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
//2.new 一个 SqlSessionFactoryBuilder 对象
SqlSessionFactoryBuilder ssfb = new SqlSessionFactoryBuilder();
//3.build一个SqlSessionFactory
SqlSessionFactory sqlSessionFactory = ssfb.build(is);
//4.利用sqlSessionFactory打开一个sqlSession会话
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//5.获取UserMapper的代理实现类对象
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//6.调用添加用户功能
User result = mapper.getUserByName("mary");
System.out.println(result);
sqlSession.close();
}
7. 加入log4j日志
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
<param name="Encoding" value="UTF-8" />
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS}
%m (%F:%L) \n" />
</layout>
</appender>
<logger name="java.sql">
<level value="debug" />
</logger>
<logger name="org.apache.ibatis">
<level value="info" />
</logger>
<root>
<level value="debug" />
<appender-ref ref="STDOUT" />
</root>
</log4j:configuration>
5..日志的级别
fatal(致命)- error()- warn()-info(信息)-debug(调试);从左到右打印的内容越来越清晰
6.在utils工具类
工具类中的异常都需要try- catch一下;若是throws抛出的话就会抛给调用它的方法,在调用处还需要在处理异常;工具类封装如下:
public static SqlSession getSqlSession(){
//1.获取核心配置文件输入流
SqlSession sqlSession = null;
try {
InputStream is = Resources.getResourceAsStream("mybatis-config.xml");
//2.new 一个 SqlSessionFactoryBuilder 对象
SqlSessionFactoryBuilder ssfb = new SqlSessionFactoryBuilder();
//3.build一个SqlSessionFactory
SqlSessionFactory sqlSessionFactory = ssfb.build(is);
//4.利用sqlSessionFactory打开一个sqlSession会话
sqlSession = sqlSessionFactory.openSession(true);
} catch (IOException e) {
e.printStackTrace();
}
return sqlSession;
}
7.使用Mybatis进行增删改查
* 步骤都相似
1. 在DAO接口(mapper接口)中创建方法
2. 在对应的映射文件中写SQL语句(注意:此时映射文件和mapper接口应该保证两个一致)
3. 在test中测试
* 查询操作时,要设置查询的数据对应的实体类的类型;使用两种方式
1. resultType:设置结果类型,即查询的数据要转换为的java类型
2. resultMap:自定义映射,处理多对一或一对多的映射关系
3. 注意:两者不能同时存在,但也不能没有
* 增加
* 删除
* 修改
简单的代码示例:
<mapper namespace="com.gz.mapper.UserMapper">
<!-- int insertUser();-->
<insert id="insertUser">
insert into t_user values(null,'wanghao','123456',78,'男','121@qq.com')
</insert>
<!-- void deleteUser(@Param("username") String username);-->
<delete id="deleteUser">
delete from t_user where username = #{username}
</delete>
<!-- void updateUser(@Param("username") String username);-->
<update id="updateUser">
update t_user set age = 10 where username = #{username}
</update>
<!-- User getUserByName(@Param("username") String username);-->
<select id="getUserByName" resultType="User">
select * from t_user where username = #{username}
</select>
</mapper>
8.核心配置文件讲解:四个标签讲解
* environments:配置连接数据库的环境
1. environments的属性:default:设置默认使用的环境的ID
2. environment:设置一个具体的连接数据库的环境;属性为:ID:设置环境的唯一标识,不能重复
3. 每一个环境-environment有两个子标签组成
1. 第一个标签:transactionManager设置事务管理器;
属性为type:设置事务管理的方式;type只有两个值:JDBC/MANAGED
2. 第二个标签:dataSource:设置数据源;
属性:type:设置数据源的类型;三个值:POOLED/UNPOOLED/JNDI
在datasource中,各种参数可以用配置文件来写;首先在核心配置文件中引入配置文件,此后可以在当前文件中使用${key}方式访问value
* 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>
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql:///ssm
jdbc.username=root
jdbc.password=admin//这个是自己登陆数据库的密码
* typeAliases:为文件起别名,在处理结果数据时能够使用别名标识一个具体的类型
<typeAliases>
<package name="com.gz.mybatis.pojo"></package>
</typeAliases>
如:查询语句中使用resultType时可以使用
<select id="getUserByUsername" resultType="User">
select * from t_user where username = '${username}'
</select>
* mappers:引入mybatis的映射文件;在引入映射文件时比较麻烦,因此可以通过包的方式引入映射文件,必须满足两个条件
1. mapper接口和映射文件所在的包必须一致
2. mapper接口的名字和映射文件的名字必须一致
3. 此时如果报错是因为没有clean掉缓存中的xml文件,clean即可
4. 在maven工程下,main目录中的java类用来写主程序,resources类用来放配置文件;虽然在自己设置时目录不一样,但当加载之后,这两者的目录在同一个target目录中,因此保持了一致
* 对于一个maven工程,创建后就会产生一个main目录一个test目录;main中有java和resources;test中写java文件
9.在idea中创建mybatis核心配置文件和映射文件的模版
* 将一个自己写好的模版粘贴进去,删掉注释即可
* 注意:typeAliases和mappers都需要删掉内容,其他两个不用,以后复制就可以
第三章 mybatis的操作方法
1.为什么要获取方法参数
mapper接口中的方法对应映射文件中的一个SQL语句;若SQL语句需要传入的参数,因此在映射文件中获取方法参数,然后拼接到SQL语句中
2.mybatis获取参数值的两种方式
* ${}:字符串拼接;在为字符串类型/日期类型赋值时需要手动加单引号
* 在底层解析时被视为一个字段
* #{}:占位符赋值;在底层被解析时自动添加单引号
* 在底层解析时被视为一个问号❓,对应的量赋值后自动加单引号
3.字面量类型
看上去的东西就是字面量:字符串、基本数据类型、包装类
4.获取mapper接口方法中的参数值
1. 单个参数时
* 此时${}和#{}中可以写任意字符获取参数值,只是推荐写的有意义一些
2. 多个参数时
* 此时在${}和#{}中使用arg0,arg1…或param1,param2…作为键,以参数为值
* 因为在底层多个参数存放在一个map集合中,通过调用键值对获取参数,参数作为键值对的值存在
3. 参数为map集合类型时
* 通过${}和#{}来获取map的键,此时的键是自己put到map集合中的键
4. mapper接口方法的参数为实体类类型的参数:mybatis中只看get、set方法中的参数名(即将get、set去掉后的属性名,而不是成员变量)
5. 可以在mapper接口方法的参数上设置@param注解(除了实体类,都用这个)
* 此时mybatis会将这些参数放在map中,以两种方式进行存储
a>以@param注解的value属性值为键,以参数为值
b>以param1…为键,以参数为值
方法中加入@Param注解,注解的value值为键,在配置文件中可以使用#{}获取注解标识的value值
public interface UserMapper {
/**
* 根据用户名查询用户信息
* @param username
* @return
*/
User getUserByUsername(@Param("username") String username);
/**
* 验证登录
* @param username
* @param password
* @return
*/
User checkLogin(@Param("username")String username,@Param("password")String password);
/**
* 验证登录,以Map集合作为参数
* @param map
* @return
*/
// List<Map<String,Object>> checkLoginByMap(Map<String,Object> map);
User checkLoginByMap(Map<String,Object> map);
/**
* 添加用户信息
*/
void insertUser(User user);
}
<mapper namespace="com.gz.mybatis.mapper.UserMapper">
<!-- User getUserByUsername(String username); -->
<select id="getUserByUsername" resultType="User">
select * from t_user where username = '${username}'
</select>
<!--User checkLogin(@Param("username")String username,@Param("password")String password);-->
<select id="checkLogin" resultType="User">
select * from t_user where username = #{username} and password = #{password}
</select>
<!-- User checkLoginByMap(Map<String,Object> map); -->
<select id="checkLoginByMap" resultType="User">
select * from t_user where username = #{username} and password = #{password};
</select>
<!-- void insertUser(User user); -->
<insert id="insertUser">
insert into t_user values (null,#{username},#{password},#{age},#{gender},#{email})
</insert>
</mapper>
5.mybatis的各种查询功能
1. 查询一个实体类对象
* 此时若返回多条记录,会报错:TooManyResultException
2. 查询一个list集合
* 若返回一条结果,使用实体类类型或list集合类型都可以
3. 查询单个数据
* 比如查询表中某一字段的个数,返回单行单列数据
* mybatis为java中常用的类型设置了类型别名;比如:
* Integer:int,Integer
* int:_int,_integer
* Map:map
* String:string
* 除了基本数据类型外,其他类型大部分都是相对应驼峰形式的
4. 查询一条数据为map集合
* 与实体类类型查询出来的结果相比较而言:
map集合查询出的结果若有null则不会放入map中;
实体类类型查询会将所有值都查询出来,即使有null值也会赋给键值对
5. 查询多条数据为map集合
* 第一种方法:使用一个list存储所有查出来的map结果(用得多)
* 第二种方法:使用@MapKey()注解的方法,注解中必须将查询到的某个字段的值作为大的Map的键,查询的一个个map集合作为值存储到一个大的Map集合中
代码示例:
public interface SelectMapper {
/**
* 根据id获取一个实体类对象
* @param id
* @return
*/
User getUserById(@Param("id")Integer id);
/**
* 使用List获取所有的用户信息
* @return
*/
List<User> getAllUser();
/**
* 获取当前职员的数量
* @return
*/
Integer getUserCount();
/**
* 根据用户名获取一条数据为Map集合
* @return
*/
Map<String,Object> getUserByMapUsername(@Param("username")String username);
/**
* 使用list获取所有用户信息
* @return
*/
List<Map<String,Object>> getAllUserByList();
/**
* 使用Map获取所有用户信息
* @return
*/
@MapKey("id")
Map<String,Object> getAllUserByMap();
}
注意:resultType的类型随查询的Java类型而变
<mapper namespace="com.gz.mybatis.mapper.SelectMapper">
<!-- User getUserById(@Param("id")Integer id); -->
<select id="getUserById" resultType="User">
select * from t_user where id = #{id}
</select>
<!-- List<User> getAllUser(); -->
<select id="getAllUser" resultType="User">
select * from t_user;
</select>
<!-- Integer getUserCount(); -->
<select id="getUserCount" resultType="Integer">
select count(*) from t_user
</select>
<!-- Map<String,Object> getUserByMapUsername(@Param("username")String username); -->
<select id="getUserByMapUsername" resultType="map">
select * from t_user where username = #{username}
</select>
<!-- List<Map<String,Object>> getAllUserByList(); -->
<select id="getAllUserByList" resultType="User">
select * from t_user
</select>
<!-- Map<String,Object> getAllUserByMap(); -->
<select id="getAllUserByMap" resultType="User">
select * from t_user
</select>
</mapper>
6.Mybatis中的几个特殊SQL的执行
* 模糊查询(#{}和${}都可以)
1. 第一种方法:在查询时若直接使用#{},会被当作字符串的一部分看待,而不是占位符,使用${}方式进行模糊查询就不会出问题
2. 第二种方法:若非要使用#{},则需要使用concat拼接的方式
3. 第三种方法:将模糊查询中的%用双引号包起来,中间使用#{}来获取map的键 即可
<!-- User getLike(@Param("part")String part); -->
<!-- 模糊查询的实现方法:三种-->
<select id="getLike" resultType="User">
select * from t_user where username like '%${part}%'
select * from t_user where username like concat('%',#{part},'%')
select * from t_user where username like "%"#{part}"%"
</select>
* 批量删除(只能${})
只能使用${}方式来拼接,因为使用#{}时,in中的数据会被加上单引号,这会导致删除错误
<!-- void deleteSeveralRecords(@Param("ids")String ids); -->
<!-- 批量删除在范围内的id记录 -->
<delete id="deleteSeveralRecords">
delete from t_user where id in (${ids})
</delete>
* 动态设置表名(只能${})
* 查询不同的表名
* 表名不能加单引号,因此也只能使用${}
<!-- List<User> setTableName(@Param("tableName") String tableName); -->
<select id="setTableName" resultType="User">
select * from ${tableName}
</select>
* 添加功能获取自增的主键(用得多)
* 表关系无论是多对一还是一对多设置都一样,都需要在’多’的这一方设置’一’的主键
* 在insert语句中要写上useGeneratedkeys和keyproperty,后者将得到的自增主键写到User的id属性存入到user中输出
<!-- void insertUser(User user); -->
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
insert into t_user values (null,#{username},#{password},#{age},#{gender},#{email})
</insert>
7.字段名和属性名不一致时,如何处理映射关系
1.将字段名设置别名
如果别名和属性名相等就可以成功输出值,否则不一致的名字输出将为null
<!--Emp getEmpByEmpId(@Param("empId”) Integer empId);-->
<select id="getEmpByEmpId” resultType="Emp">
select emp_id empId,emp_name empName,age,gender from t_emp where emp_id = #{empId}
</select>
2.将下划线映射为驼峰
当字段符合Mysql的要求使用_,而属性符合java的要求使用驼峰;此时可以在MyBatis的核心配置文件设置一个全局配置,可以自动将下划线映射为驼峰
<settings>
<!-- 将下划线映射为驼峰 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
3. 使用resultMap自定义映射处理
* id:唯一标识
* type:处理映射关系的实体类的类型
* 其他标签:
* id:处理主键和实体类中属性的映射关系
* result:处理普通字段和实体类中属性的映射关系
* column:设置映射关系的字段名,必须是SQL查询出的某个字段
* property:设置映射关系中的属性的属性名,必须是处理的实体类类型中的属性名
<!-- Dept getDeptAndEmpByStepOne(@Param("deptId") Integer deptId); -->
<resultMap id="getDeptAndEmpByStepOne" type="Dept">
<id column="dept_id" property="deptId"></id>
<result column="dept_name" property="deptName"></result>
<collection property="emps"
select="com.gz.mybatis.mapper.EmpMapper.getDeptAndEmpByStepTwo"
column="dept_id"></collection>
</resultMap>
<select id="getDeptAndEmpByStepOne" resultMap="getDeptAndEmpByStepOne">
select * from t_dept where dept_id = #{deptId}
</select>
8.处理多对一的映射关系
1.Java类型和数据库数据的对应关系
表-实体类,字段-属性,一行-一个对象
2.多对一与一对多的处理:对一:一个对象;对多:一个集合
* 三种处理方式(方法极其固定,记不住就做成模版,到时候复制粘贴即可)
/**
* 获取员工以及所对应的部门信息:多 对 一
* @param empId
* @return
*/
Emp getEmpAndDeptByEmpId(@Param("empId")Integer empId);
* 第一种方法:级联方式处理
<!-- 处理多对一映射关系的三种方法 -->
<!-- Emp getEmpAndDeptByEmpId(@Param("id")Integer id); -->
<!-- 第一种方法:级联方式处理 -->
<resultMap id="getEmpAndDeptResultMap" type="Emp">
<id column="emp_id" property="empId"></id>
<result column="emp_name" property="empName"></result>
<result column="age" property="age"></result>
<result column="gender" property="gender"></result>
<result column="dept_id" property="dept.deptId"></result>
<result column="dept_name" property="dept.deptName"></result>
</resultMap>
<select id="getEmpAndDeptByEmpId" resultMap="getEmpAndDeptResultMap">
SELECT *
FROM t_emp
LEFT JOIN t_dept
ON t_emp.dept_id = t_dept.dept_id
WHERE t_emp.emp_id = #{empId};
</select>
* 第二种方法:association;专门用来处理多对一的映射关系(专门处理实体类类型的属性)
<!-- 第二种方法:association处理 -->
<resultMap id="getEmpAndDeptResultMap" type="Emp">
<id column="emp_id" property="empId"></id>
<result column="emp_name" property="empName"></result>
<result column="age" property="age"></result>
<result column="gender" property="gender"></result>
<association property="dept" javaType="Dept">
<id column="dept_id" property="deptId"></id>
<result column="dept_name" property="deptName"></result>
</association>
</resultMap>
<select id="getEmpAndDeptByEmpId" resultMap="getEmpAndDeptResultMap">
SELECT *
FROM t_emp
LEFT JOIN t_dept
ON t_emp.dept_id = t_dept.dept_id
WHERE t_emp.emp_id = #{empId};
</select>
* 第三种方法:分步查询
* 注意:此时应将分步查询的不同步骤写在对应的映射文件中
* select的唯一标识:namespace.SQL Id
/**
* 通过分步查询员工以及对应的部门信息的第一步
* @param empId
* @return
*/
Emp getEmpAndDeptByStepOne(@Param("empId") Integer empId);
/**
* 通过分步查询员工以及对应的部门信息的第二步
* @param deptId
* @return
*/
Dept getEmpAndDeptByStepTwo(@Param("deptId") Integer deptId);
<!-- 第三种方法:分步查询-->
<!-- Emp getEmpAndDeptByStepOne(@Param("empId") Integer empId);-->
<resultMap id="empAndDeptByStepResultMap" type="Emp">
<id column="emp_id" property="empId"></id>
<result column="emp_name" property="empName"></result>
<result column="age" property="age"></result>
<result column="gender" property="gender"></result>
<association property="dept" fetchType="lazy"
select="com.gz.mybatis.mapper.DeptMapper.getEmpAndDeptByStepTwo"
column="dept_id"></association>
</resultMap>
<select id="getEmpAndDeptByStep" resultMap="empAndDeptByStepResultMap">
select * from t_emp where emp_id = #{empId}
</select>
<!-- Dept getEmpAndDeptByStepTwo(@Param("deptId") Integer deptId); -->
<select id="getEmpAndDeptByStepTwo" resultType="Dept">
select * from t_dept where dept_id = #{deptId}
</select>
3. 分步查询写着麻烦,有什么优势?
* 可以实现延迟加载
* 在分步查询中,可以先给出查询的第一步结果,第二步结果如果暂时不需要可以不给出。减少内存消耗
* 但要实现延迟加载,必须先在核心配置文件中设置两个全局配置信息
* lazyLoadingEnabled:延迟加载的全局开关。开启后,所有关联对象都会延迟加载;默认为false
* aggressiveLazyLoading:开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载;默认为true
<settings>
<!-- 将下划线映射为驼峰 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!-- 开启延迟加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 按需加载 -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
* 这两个方法可以实现按需加载,要获取的数据是什么,就执行相应的SQL。
* 此时存在一个问题,在核心配置文件中写了全局配置,会让所有分步查询都实现延迟加载。但如果需要一次将所有结果都查询出来,就会出现问题
解决方法:在association中使用fetchType:在开启了延迟加载的环境中,通过该属性设置当前的分步查询是否使用延迟加载
<!-- fetchType = "eager"(立即加载)-->
<!-- fetchType = "lazy"(延迟加载)-->
<association property="dept" fetchType="lazy"
select="com.gz.mybatis.mapper.DeptMapper.getEmpAndDeptByStepTwo"
column="dept_id"></association>
</resultMap>
9.一对多的映射关系
1.表关系映射到实体类中时,实体类如何处理对应的复杂的表的关系呢?
* 对一,对应一个对象;对多,对应一个集合
* 比如,员工表和部门表;一个部门对应多个员工;若在查员工时要查部门,就是多对一,此时在员工实体类对象中写上一个部门对象;
* 若是查询一个部门的多个员工,就是一对多;在部门的实体类对象中为多个员工写一个list集合
public class Dept {
private Integer deptId;
private String deptName;
private List<Map> emps;
}
2.处理一对多的映射关系的方法
* 第一种方法:collection(也是一个模版)
<!--
2.处理一对多的映射关系的方法
第一种方法:collection(也是一个模版)
-->
<!-- Dept getDeptAndEmpByDeptId(@Param("deptId") Integer deptId); -->
<resultMap id="getDeptIdByRestultMap" type="Dept">
<id column="dept_id" property="deptId"></id>
<result column="dept_name" property="deptName"></result>
<collection property="emps" ofType="Emp">
<id column="emp_id" property="empId"></id>
<result column="emp_name" property="empName"></result>
<result column="age" property="age"></result>
<result column="gender" property="gender"></result>
</collection>
</resultMap>
<select id="getDeptAndEmpByDeptId" resultMap="getDeptIdByRestultMap">
select *
from t_dept
left join t_emp
on t_dept.dept_id = t_emp.dept_id
where t_dept.dept_id = #{deptId}
</select>
* 第二种方法:分步查询
<!-- 第二种方法:分步查询-->
<!-- Dept getDeptAndEmpByStepOne(@Param("deptId") Integer deptId); -->
<!-- 分步查询的第一步-->
<resultMap id="getDeptAndEmpByStepOne" type="Dept">
<id column="dept_id" property="deptId"></id>
<result column="dept_name" property="deptName"></result>
<collection property="emps"
select="com.gz.mybatis.mapper.EmpMapper.getDeptAndEmpByStepTwo"
column="dept_id"></collection>
</resultMap>
<select id="getDeptAndEmpByStepOne" resultMap="getDeptAndEmpByStepOne">
select * from t_dept where dept_id = #{deptId}
</select>
<!-- List<Emp> getDeptAndEmpByStepTwo(@Param("deptId") Integer deptId); -->
<select id="getDeptAndEmpByStepTwo" resultType="Emp">
select * from t_emp where dept_id = #{deptId}
</select>
第四章 Mybatis其他方面
1.动态SQL
* 作用:根据特定条件动态拼装SQL语句的功能,存在的意义是为了解决拼接SQL语句字符串时的问题
* 第一个标签:if:通过test属性中的表达式判断标签中的内容是否有效(是否会拼接到SQL中)
* 第二个标签:where:(与if结合使用)
* 第一个功能:若where标签中有表达式成立,则自动生成where
* 第二个功能:能将内容中前面多余的and去掉,但是内容后面的and无法去掉
* 第三个功能:若where标签中的表达式都不成立,则where标签没有任何功能
* 第三个标签:trim:用来截取条件中的内容;具有四个标签
* prefix,suffix:在标签中内容的前面或后面添加指定内容
* prefixOverrides,suffixOverrides:在标签中内容前面或后面去掉指定内容
* 第四组标签:foreach(极其重要,掌握);可以实现批量添加和批量删除(三种方法),共有五个属性
* collection:设置要循环的数组或集合
* item:用一个字符串表示数组或集合中的每一个数据
* separator:设置每次循环的数据之间的分隔符
* open:循环的所有内容以什么开始
* close:循环的所有内容以什么结束
<!--
foreach:五个属性
collection:设置要循环的数组或集合
item:用一个字符串表示数组中的每一个数据
separator:设置每次循环的数据之间的分隔符
-->
<!-- void insertManyEmp(@Param("emps") List<Emp> emps); -->
<insert id="insertManyEmpOne">
insert into t_emp values
<foreach collection="emps" item="emp" separator="," open="(" close=")">
null,#{emp.empName},#{emp.age},#{emp.gender},null
</foreach>
</insert>
* 第五个标签:choose,when,otherwise:相当于:if-else if-else(用的不多)
* when至少设置一个;otherwise至多设置一个
* 第六个标签:sql片段:可以记录一段sql,在需要使用的地方使用include标签进行引用
<sql id="empColumns">
emp_id,emp_name,age,gender,dept_id
</sql>
2.多条件查询中条件拼接存在的问题
* 若条件中所有条件都不成立,就会出现很大的漏洞,这需要解决
* 第一种方法:where 后面加上一个恒成立的表达式:1=1
<select id="getEmpByConditionOne" resultType="Emp">
select * from t_emp where 1 = 1
<if test="empName != null and empName != ''">
and emp_name = #{empName}
</if>
<if test="age != null and age != ''">
and age = #{age}
</if>
<if test="gender != null and gender != ''">
gender = #{gender}
</if>
</select>
* 第二种方法:使用<where>标签
<select id="getEmpByConditionTwo" resultType="Emp">
select * from t_emp
<where>
<if test="empName != null and empName != ''">
and emp_name = #{empName}
</if>
<if test="age != null and age != ''">
and age = #{age}
</if>
<if test="gender != null and gender != ''">
and gender = #{gender}
</if>
</where>
</select>
* 第三种方法:使用<trim>标签
<select id="getEmpByConditionThree" resultType="Emp">
select * from t_emp
<trim prefix="where" suffixOverrides="and">
<if test="empName != null and empName != ''">
emp_name = #{empName} and
</if>
<if test="age != null and age != ''">
age = #{age} and
</if>
<if test="gender != null and gender != ''">
gender = #{gender}
</if>
</trim>
</select>
* 第四种方法:使用组标签:choose、when、otherwise
<select id="getEmpByCondition" resultType="Emp">
select <include refid="empColumns"></include> from t_emp where
<choose>
<when test="empName != null and empName != ''">
emp_name = #{empName}
</when>
<when test="age != null and age != ''">
age = #{age}
</when>
<when test="gender != null and gender != ''">
gender = #{gender}
</when>
</choose>
</select>
3.Mybatis的一级缓存
* 一级缓存:sqlSession级别,通过同一个sqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问(默认开启)
* 使一级缓存失效的四种情况
* 不同的sqlSession对应不同的一级缓存
* 同一个sqlSession但是查询条件不同
* 同一个sqlSession两次查询期间执行了任何一次增删改操作;原因:增删改会修改数据库中的信息,缓存信息可能会被改变,而查询的信息必须是数据库实时存在的信息,因此缓存必须被清空
* 同一个sqlSession两次查询期间手动清空了缓存(clearCache)
4. Mybatis的二级缓存
* 二级缓存是sqlSessionFactory级别的,通过同一个sqlSessionFactory创建的SqlSession查询的结果会被缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取
* 二级缓存开启的条件:
* 在核心配置文件中,设置全局配置属性cacheEnabled=true;当然默认为true,不需要设置(不用管)
* 在映射文件中设置标签<cache/>
<cache />
* 二级缓存必须在sqlSession关闭或提交之后有效(必须close)
* 查询的数据所转换的实体类类型必须实现序列化的接口
public class Emp implements Serializable {
private Integer empId;
private String empName;
private Integer age;
private String gender;
}
* 二级缓存失效的情况:
* 两次查询之间执行了任意的增删改,会使一级缓存和二级缓存同时失效
* 二级缓存存在缓存命中率,只要不为零,就说明缓存被命中了;也就是说明当前的所查询的数据在缓存中存在;
5.Mybatis缓存查询的顺序
* 先查询二级缓存,未命中;则查询一级缓存,又未命中;则查询数据库
* sqlSession关闭后,一级缓存中的数据会写入二级缓存
* 一级缓存中的数据不一定在二级缓存中;因为只有一级缓存关闭后才会写入二级缓存
6.二级缓存可以整合第三方缓存EHCache
7.Mybatis的逆向工程
* 正向工程:先创建java实体类,由框架负责根据实体类生成数据库表。Hibernate是支持正向工程的
* 逆向工程:先创建数据库表,由框架负责根据数据库表,反向生成:java实体类、Mapper接口、Mapper映射文件
* 逆向工程如果要是第二次生成时,要把前一次生成的删掉
* 逆向工程版本:
* 简单版:MyBatis3Simple-生成基本的curd
* MyBatis3:生成带条件的Curd:可以实现对单表几乎所有的操作;
8.sql中实现分页功能
* SQL语句中:使用:limit
* limit 数据偏移量 条目数
#每页显示2条数据,此时显示第一页
SELECT *
FROM t_user
LIMIT 2
#每页显示2条数据,此时显示第三页
SELECT *
FROM t_user
LIMIT 4,2
#每页显示2条数据,此时显示第四页
SELECT *
FROM t_user
LIMIT 6,2
* 每页显示pagesize条记录,此时显示第 pageNumber页:公式:
imit (pageNum-1)*pagesize,pagesize
* 如limit 2;表示第一页的前2条数据;默认表示第一页可以省略不写index;
9.使用分页插件
* pom.xml中添加依赖
* 配置分页插件
* 分页插件的使用:
* 第一步:查询功能之前使用PageHelper.startPage(pageNum,pageSize)开启分页功能
* pageNum:当前页的页码
* pageSize:每页显示的条数
* 第二步:在查询获取list集合之后,使用PageInfo pageInfo = new PageInfo(list, navigatePages)获取分页相关数据;
* list:分页之后的数据
* navigatePages:导航分页的页码数(就是一共能显示从第几页到第几页)
* 第三步:输出分页数据
代码示例如下:
@Test
public void testLimit2(){
SqlSession sqlSession = SqlSessionUtil.getSqlSession();
EmpMapper mapper = sqlSession.getMapper(EmpMapper.class);
//1.开启分页功能
Page<Object> page = PageHelper.startPage(5, 4);
//2.获取分页信息
List<Emp> list = mapper.selectByExample(null);
PageInfo<Emp> pageInfo = new PageInfo<>(list,5);
System.out.println(pageInfo);
}