# Mybatis
## ORM思想:对象(Java对象)关系(关系型数据库)映射思想
## Mybatis 封装了JDBC
### 其他持久层技术的简介:
1、JDBC:
SQL夹杂在Java代码中耦合度高,导致硬编码内伤
维护不易且实际开发需求中SQL有变化,频繁修改的情况多见
代码冗长,开发效率低
2、Hibernate 和 JPA
操作相当简便,开发效率相当高
程序中的长难复杂SQL需要绕过框架
内部自动生产的SQL,不容易做特殊优化
基于全映射的全自动框架,大量字段的POJO进行部分映射时比较困难
反射操作太多,导致数据库性能下降
3、Mybatis
轻量级、性能出色
SQL和Java编码分开,功能边界清晰。Java代码专注业务,SQl语句专注数据
开发效率稍逊于Hibernate,但是完全能够接受
## 注意:
```java
// int insertUser();
//id和方法名保持一致
<insert id="insertUser">
INSERT INTO t_user(id,username,password,age,gender,email)
VALUES(null,#{username},#{password},#{age},#{gender},#{email});
</insert>
```
### 1、代理对象重写方法的过程:
**代理模式 **
创建代理对象的实现类 重写方法的过程为:根据接口名找到映射文件、根据方法名找到映射文件中的id、然后重写方法
其实就是框架为我们重写方法呗 跟我们重写一样
```java
/*获取核心配置文件的输入流*/
InputStream resource = Resources.getResourceAsStream("mybatis-config.xml");
/*获取SqlSessionFactoryBuilder对象 用来创建工厂 */
SqlSessionFactoryBuilder session = new SqlSessionFactoryBuilder();
/* 用来获取工厂 */
SqlSessionFactory factory = session.build(resource);
/* sql会话对象 */
/*SqlSession sqlSession = factory.openSession(true); 自动提交*/
SqlSession sqlSession = factory.openSession();/*不会自动提交*/
/* 获取代理对象 底层创建实现类 代理模式 */
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.insertUser();
sqlSession.commit();
sqlSession.close();
```
### 2、日志的级别(log4j中)
FATAL(致命)>ERROR(错误)>WARN(警告)>INFO(信息)>DEBUG(调试)
从左到右打印的越来越详细
如果 : 连WARN都要记录,那么比它等级更高的ERROR和FATAL也要记录
### 3、resultType与resultMap
```xml
<!--
resultType:设置结果类型,查询的数据转换为的Java类型
resultMap:自定义映射,处理一对多或多对一的映射关系
-->
<select id="getUserById" resultType="com.ls.pojo.User">
SELECT id, username, password, age, gender, email
FROM t_user
WHERE id = 1;
</select>
```
mybatis框架的配置文件
```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>
<!--mybatis核心配置文件中的标签必须按照指定的顺序配置
(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,
objectWrapperFactory?,reflectorFactory?,plugins?,environments?,
databaseIdProvider?,mappers?)
-->
<!--引入properties文件,此后可以在当前文件中使用${key}的方式访问value -->
<properties resource="jdbc.properties"></properties>
<!--
typeAliases:设置类型别名,即为某个具体的类型设置别名
在mybatis范围中,就可以使用别名表示具体的类型
-->
<typeAliases>
<!--
type:设置需要起别名的类型
alias:设置某个类型的别名
若不设置alias:当前类型拥有默认的别名,即类名不区分大小写
-->
<!-- <typeAlias type="com.ls.pojo.User" alias="User"></typeAlias>-->
<!--通过包来设置类型别名,指定包下所有的类 都有默认的别名,即类名不区分大小写 -->
<package name="com.ls.pojo"/>
</typeAliases>
<!--default:设置默认使用的环境id-->
<environments default="development">
<!--
environment:设置一个具体的链接数据库的环境
属性:
id:设置环境的唯一标识,不能重复
-->
<environment id="development">
<!--
transactionManager:设置事务管理器
属性:
type:设置事务管理方式
type:"JDBC/MANAGED"
JDBC:表示使用JDBC原生的事务管理方式 原生就是:可以设置手动提交 也可以设置自动提交 或者回滚 都是可以操控的
MANAGED:被管理,例如Spring中的transaction
-->
<transactionManager type="JDBC"/>
<!--
dataSource:设置数据源
属性:
type:设置数据源的类型
type:"POOLED/UNPOOLED/JNDI"
POOLED:表示使用数据库连接池
UNPOOLED:表示不使用数据库连接池 也就是说每次获取的链接都是重新创建的
JNDI:表示使用上下文中的数据源
-->
<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>
<!--引入mybatis的映射文件-->
<mappers>
<!-- <mapper resource="com/ls/dao/UserMapper.xml"/>-->
<!--
以包的方式引入映射文件但是必须满足两个条件:
1、mapper接口和映射文件所在的包必须一致
2、mapper接口的名字和映射文件的名字必须一致
-->
<package name="com.ls.dao"/>
</mappers>
</configuration>
```
### 4、MyBatis获取参数值的两种方式
MyBatis获取参数值的两种方式: ${}和#{}
${}的本质就是字符串拼接,#{}的本质就是占位符赋值I
${}使用字符串拼接的方式拼 接sql,若为字符串类型或日期类型的字段进行赋值时,需要手动加单引号;
但是#{}使用占位符赋值的方式拼接sq|,此时为字符串类型或日期类型的字段进行赋值时,可以自动添加单引号
### 5、MyBatis获取参数值的两种方式: #{}和${}
\#{}的本质是占位符赋值,${}的本质是字符串拼接
#### 1,若mapper 接口方法的参数为单个的字面量类型
此时可以通过#{}和\${}以任意的内容获取参数值,一-定要注意${} 的单引号问题
#### 2、若mapper接口方法的参数为多个的字面量类型
此时MyBatis会将参数放在map集合中,以两种方式存储数据
a> l以arg0, arg1...为键,以参数为值
b> l以param1, param2...为键,以参数为值
因此,只需要通过#{} 和\${}访问map集合的键,就可以获取相对应的值, - -定要注意${}的单 引号问题
```.xml
<select id="checkLogin" resultType="user">
SELECT *
FROM t_user
WHERE username = #{param1}
and password = #{param2};
</select>
```
#### 3、若mapper接口方法的参数为map集合类型的参数
只需要通过#{}和\${}访问map集合的键,就可以获取相对应的值, - -定要注意${}的单引号问题
```java
/*通过Map集合验证登录*/
User checkLoginByMap(Map<String,Object> map);
<select id="checkLoginByMap" resultType="user">
SELECT *
FROM t_user
WHERE username = #{username}
and password = #{password};
</select>
```
#### 4、若mapper接口方法的参数为实体类类型的参数
只需要通过#{}和\${}访问实体类中的属性名,就可以获取相对应的属性值,- -定 要注意${}的单引号问题
#### 5、可以在mapper 接口方法的参数上设置@Param注解 常用
此时MyBatis会将这些参数放在map中,以两种方式进行存储
a>以@Param注解的value属性值为键,以参数为值
b>以param1 , param2 ......为键,以参数为值
因此,只需要通过#{} 和\${}访问map集合的键,就可以获取相对应的值, - -定要注意${}的单 引号问题
```java
User checkLoginByParam(@Param("username") String username, @Param("password") String password);
```
## 常用的查询类型
### **Inetger**
```xml
<!-- Integer getCount();-->
<select id=" getcount" resultType="java.lang.Integer">
select count(*) from t_ user
</select>
```
### **Map**
```xml
<!-- Map<String,Object> getUserByIdToMap(@Param("id") Integer id); -->
<!-- 查询的结果中 如果某个字段的值为null 是不会被放到map集合中的 -->
<select id="getUserByIdToMap" resultType="map">
SELECT * FROM t_user WHERE id=#{id};
</select>
```
### **特殊的Map和List**
```java
/* 查询所有的用户信息为map集合
若查询的数据有多条时,并且要将每条数据转换为map集合
此时有两种解决方案:
1.将mapper接口方法的返回值设置为泛型是map的ist集合
2、 可以将每条数据转换的map集合放在- 一个大的map中, 但是必须要通过@MapKey注解
将查询的某个字段的值作为大的map的键
*/
List<LinkedHashMap<String,Object>> getAllUserToListMap(); //LinkedHashMap只是为了排序
@MapKey("id")
Map<Integer,Object> getAllUserToMap();
```
### 模糊查询
```xml
<!--模糊查询-->
<select id="getUserByLike" resultType="com.ls.pojo.User">
<!-- SELECT * FROM t_user where username like '%${mohu}%'; -->
<!--字符串拼接-->
<!-- SELECT * FROM t_user where username like concat('%',#{mohu},'%'); -->
<!--最常用-->
SELECT * FROM t_user where username like "%"#{mohu}"%";
</select>
```
### 批量删除
```xml
<!--批量删除-->
<delete id="deleteMoreUser">
delete from t_user where id in (${ids});
</delete>
```
### 获取自增的主键
```xml
<!--void insertUser(User user);
useGeneratedKeys使用自增的主键
keyProperty 获取自增的主键并添加到user的id属性中
-->
<insert id="insertUser" useGeneratedKeys="true" keyProperty="id" >
INSERT INTO t_user(id, username, password, age, gender, email)
values (null, #{username}, #{password}, #{age}, #{gender}, #{email});
</insert>
```
### 字段名和属性名不一致
修改配置文件
```xml
<!--将下划线命名映射为驼峰命名-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
```
映射文件
```xml
<!--
字段名和属性名不一-致的情况, 如何处理映射关系
1.为查询的字段设置别名,和属性名保持一致
2,当字段符合MySQL的要求使用_,而属性符合java的要求使用驼峰
此时可以在yBatis的核心配置文件中设置一个全局配置, 可以自动将下划线映射为驼峰
emp_id:empId emp_name:empName
-->
<select id="getEmpByEmpId" resultType="com.ls.pojo.Emp">
<!--select emp_id empId , emp_name empName , age , gender ,dept_id deptId from t_emp where emp_id = #{empId};-->
select * from t_emp where emp_id = #{empId};
</select>
```
### 使用resultMap
```xml
<!--
resultMap:设置自定义的映射关系
id:唯一标识
type:处理映射关系的实体类的类型
常用的标签:
id:处理主键和实体类中属性的映射关系
result:处理普通字段和实体类中属性的映射关系
column:设置映射关系中的字段名,必须是sql查询出的某个字段
property:设置映射关系中属性的属性名,必须是处理的实体类类型中的属性名
association:处理多对一的映射关系(处理实体类类型的属性)
collectiqn:处理一对多的映射关系(处理集合类型的属性)
-->
<resultMap id="EmpMap" type="com.ls.pojo.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="deptId"></result>
</resultMap>
<select id="getEmpByEmpId" resultMap="EmpMap">
select * from t_emp where emp_id = #{empId};
</select>
```
### 多对一(对象) 必会
#### 处理方式一:
```xml
实体类:{
@Data
public class Emp {
private Integer empId;
private String empName;
private Integer age;
private char gender;
private Dept dept;
}
}
<!-- 通过 级联的方式 -->
<resultMap id="EmpAndDept" type="com.ls.pojo.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="EmpAndDept">
select t_emp.*, t_dept.*
from t_emp
left join t_dept on t_emp.dept_id = t_dept.dept_id
where emp_id = #{empId};
</select>
```
#### **处理方式二(常用):**
```xml
实体类:{
@Data
public class Emp {
private Integer empId;
private String empName;
private Integer age;
private char gender;
private Dept dept;
}
}
<!--
association:处理多对一-的映射关系(处理实体类类型的属性)
property:设置需要处理映射关系的属性的属性名
javaType:设置要处理的属性的类型
-->
<resultMap id="EmpAndDept" type="com.ls.pojo.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="com.ls.pojo.Dept">
<id column="dept_id" property="deptId"></id>
<result column="dept_name" property="deptName"></result>
</association>
</resultMap>
<select id="getEmpAndDeptByEmpId" resultMap="EmpAndDept">
select t_emp.*, t_dept.*
from t_emp
left join t_dept on t_emp.dept_id = t_dept.dept_id
where emp_id = #{empId};
</select>
```
#### 处理方式三(分步查询):
分步查询的优点:可以实现延迟加载
但是必须在核心配置文件中设置全局配置信息:
lazyLoadingEnabled:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载
aggressiveLqzyLoading:当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载
此时就可以实现按需加载,获取的数据是什么,就只会执行相应的sql。 此时可通过association和 collection
中的fetchType属性设置当前的分步查询是否使用延迟加载,fetchType="lazy(延迟加 载)| eager(立即加载)"
```xml
<association property="dept" fetchType="eager" <!--实现立即加载 也可以设置成lazy设置成懒加载-->
select="com.ls.mapper.DeptMapper.getDeptById"
column="dept_id"></association>
```
```xml
一个Mapper.xml
<!--
property:设置需要处理映射关系的属性的属性名
select:设置分步查询的sql的唯一标识
column:将查询出的某个字段作为分步查询的sqL的条件
-->
<resultMap id="EmpAndDeptStep" type="com.ls.pojo.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" select="com.ls.mapper.DeptMapper.getDeptById"
column="dept_id"></association>
</resultMap>
<select id="getEmpAndDeptByStepOne" resultMap="EmpAndDeptStep">
select * from t_emp where emp_id=#{empId};
</select>
另一个Mapper.xml
<select id="getDeptById" resultType="dept">
select * from t_dept where dept_id=#{deptId};
</select>
```
### 一对多(集合)必会
#### 处理方式一:
```xml
<!--
处理一对多的映射关系:
1、collection
2、分步查询
-->
<resultMap id="DeptAndEmp" type="dept">
<id column="dept_id" property="deptId"></id>
<result column="dept_name" property="deptName"></result>
<!--
ofType:设置集合中存储的数据的类型
-->
<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="DeptAndEmp">
select t_emp.*, t_dept.*
from t_dept
left Join t_emp on t_dept.dept_id = t_emp.dept_id
where t_dept.dept_id = #{deptId};
</select>
```
#### 处理方式二(分页查询):
```xml
<!--
处理- -对多的映射关系:
1、collection
2、分步查询
-->
<resultMap id="DeptAndEmp" type="dept">
<id column="dept_id" property="deptId"></id>
<result column="dept_name" property="deptName"></result>
<collection property="emps" fetchType="lazy"
select="com.ls.mapper.DeptMapper.getEmpById"
column="dept_id"
></collection>
</resultMap>
<select id="getDeptAndEmpByDeptId" resultMap="DeptAndEmp">
select * from t_dept where dept_id = #{deptId}
</select>
<select id="getEmpById" resultType="emp">
select *
from t_emp
where dept_id = #{deptId};
</select>
```
## 动态SQL
### if标签和Where标签
```xml
<!--
动态SQL :
1. if,通过test属性中的表达式判断标签中的内容是否有效(是否会拼接到sql中)
2、where
a.若where标签中有条件成立,会自动生成where关键字
b.会自动将where标签中内容前多余的and去掉,但是内容后多余的and无法去掉
c.若where标签中没有任何一个条件成立,则where没有任何功能
-->
<select id="getEmpByCondition" resultType="com.ls.pojo.Emp">
select * from t_emp
<where>
<if test="empName != null and empName != ''">
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标签
```xml
<!--
trim
prefix,suffix: 在标签中内容前面或后面添加指定内容
prefixoverrides,suffixoverrides: 在标签中内容前面或后面去排指定内容
-->
<select id="getEmpByCondition" resultType="com.ls.pojo.Emp">
select * from t_emp
<trim prefix="where" prefixOverrides="and">
<if test="empName != null and empName != ''">
emp_name = #{empName}
</if>
<if test="age != null and age != ''">
and age = #{age}
</if>
<if test="gender != null and gender != ''">
and gender = #{gender}
</if>
</trim>
</select>
```
### choose、when、otherwise三个标签联合使用
```xml
<!--
choose, when, otherwise
相当于java中的if...else if...else
when至少设置一一个,otherwise最多设置 1个
-->
<select id="getEmpByCondition" resultType="com.ls.pojo.Emp">
select * from t_emp
<where>
<choose>
<when test="empName != null and empName != ''">
emp_name = #{empName}
</when>
<when test="age != null and age != ''">
and age = #{age}
</when>
<when test="gender != null and gender != ''">
and gender