1. MyBatis概述
Mybatis:和数据库进行交互,持久化层框架(SQL映射框架)。
从原始的JDBC----dbutils----JdbcTemplate;成为工具(不用)
- 原生JDBC:连接数据库–>获取PeraredStatment–>写sql–>预编译参数–>执行sql封装结果。
- sql语句是硬编码在Java程序中(耦合:数据库层和java编码)。
MyBatis优点:
-
MyBatis将重要的步骤抽取出来可以人工定制,其他步骤自动化。
-
重要的步骤都是写在配置文件中的(好维护);
-
完全解决数据库的优化问题。
-
MyBatis底层就是对原生JDBC的简单封装。
-
既将Java编码与sql抽取出来,还不会失去自动化,半自动的持久化框架。
2. 快速开始
项目结构如下:
![项目结构](https://i-blog.csdnimg.cn/blog_migrate/7fa9188816bca257155759ffe1a4beb8.png)
- 创建数据库和Employee数据表
- 创建数据表对应的POJO
public class Employee {
private Integer id;
private String lastName;
private String email;
private String gender;
...
}
- 导入jar包:
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.11</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.8</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
</dependencies>
- 创建全局配置文件:mybatis-config.xml
- 导入jdbc.properties
- 从jdbc.properties中读取数据库信息
- 将sql映射文件(EmployeeMapper.xml)一定要注册到全局配置文件(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="${driverClass}"/>
<property name="url" value="${url}"/>
<property name="username" value="${user}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!-- 将我们写好的sql映射文件(EmployeeMapper.xml)一定要注册到全局配置文件(mybatis-config.xml)中 -->
<mappers>
<mapper resource="EmployeeMapper.xml" />
</mappers>
</configuration>
- 创建DAO:EmployeeMapper
public interface EmployeeMapper {
public Employee getEmpById(Integer id);
}
- 创建Mapper映射文件:EmployeeMapper.xml
- namespace:名称空间;指定为接口的全类名
- id:唯一标识,与接口的方法一定同名
- resultType:返回值类型,与接口的返回值类型相同
- #{id}:从传递过来的参数中取出id值
<?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.xz.mybatis.dao.EmployeeMapper">
<!--
namespace:名称空间;指定为接口的全类名
id:唯一标识
resultType:返回值类型
#{id}:从传递过来的参数中取出id值
public Employee getEmpById(Integer id);
-->
<select id="getEmpById" resultType="com.xz.mybatis.bean.Employee">
select id,last_name lastName,email,gender from tbl_employee where id = #{id}
</select>
</mapper>
- 编写测试类
/**
* 1、接口式编程
* 原生: Dao ====> DaoImpl
* mybatis: Mapper ====> xxMapper.xml
*
* 2、SqlSession代表和数据库的一次会话;用完必须关闭;
* 3、SqlSession和connection一样都是非线程安全。每次使用都应该去获取新的对象。
* 4、mapper接口没有实现类,但是mybatis会为这个接口生成一个代理对象。
* (将接口和xml进行绑定)
* EmployeeMapper empMapper = sqlSession.getMapper(EmployeeMapper.class);
* 5、两个重要的配置文件:
* mybatis的全局配置文件:包含数据库连接池信息,事务管理器信息等...系统运行环境信息
* sql映射文件:保存了每一个sql语句的映射信息:将sql抽取出来。
*/
public class MyBatisTest {
public SqlSessionFactory getSqlSessionFactory() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
return new SqlSessionFactoryBuilder().build(inputStream);
}
/**
* 1、根据xml配置文件(全局配置文件)创建一个SqlSessionFactory对象 有数据源一些运行环境信息
* 2、sql映射文件;配置了每一个sql,以及sql的封装规则等。
* 3、将sql映射文件注册在全局配置文件中
* 4、写代码:
* 1)、根据全局配置文件得到SqlSessionFactory;
* 2)、使用sqlSession工厂,获取到sqlSession对象使用他来执行增删改查
* 一个sqlSession就是代表和数据库的一次会话,用完关闭
* 3)、使用sql的唯一标志来告诉MyBatis执行哪个sql。sql都是保存在sql映射文件中的。
* @throws IOException
*/
@Test
public void test() throws IOException {
// 2、获取sqlSession实例,能直接执行已经映射的sql语句
// sql的唯一标识:statement Unique identifier matching the statement to use.
// 执行sql要用的参数:parameter A parameter object to pass to the statement.
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
SqlSession openSession = sqlSessionFactory.openSession();
try {
Employee employee = openSession.selectOne(
"com.xz.mybatis.EmployeeMapper.selectEmp", 1);
System.out.println(employee);
} finally {
openSession.close();
}
}
@Test
public void test01() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
//3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee employee = mapper.getEmpById(1);
System.out.println(mapper.getClass());
System.out.println(employee);
} finally {
openSession.close();
}
}
}
3. 全局配置文件设置
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>
<!--
1、mybatis可以使用properties来引入外部properties配置文件的内容;
resource:引入类路径下的资源
url:引入网络路径或者磁盘路径下的资源
-->
<properties resource="dbconfig.properties"></properties>
<!--
2、settings包含很多重要的设置项
setting:用来设置每一个设置项
name:设置项名
value:设置项取值
-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!-- 3、typeAliases:别名处理器:可以为我们的java类型起别名
别名不区分大小写
-->
<typeAliases>
<!-- 1、typeAlias:为某个java类型起别名
type:指定要起别名的类型全类名;默认别名就是类名小写;employee
alias:指定新的别名
-->
<!-- <typeAlias type="com.xz.mybatis.bean.Employee" alias="emp"/> -->
<!-- 2、package:为某个包下的所有类批量起别名
name:指定包名(为当前包以及下面所有的后代包的每一个类都起一个默认别名(类名小写),)
-->
<package name="com.xz.mybatis.bean"/>
<!-- 3、批量起别名的情况下,使用@Alias注解为某个类型指定新的别名 -->
</typeAliases>
<!--
4、environments:环境们,mybatis可以配置多种环境 ,default指定使用某种环境。可以达到快速切换环境。
environment:配置一个具体的环境信息;必须有两个标签;id代表当前环境的唯一标识
transactionManager:事务管理器;
type:事务管理器的类型;JDBC(JdbcTransactionFactory)|MANAGED(ManagedTransactionFactory)
自定义事务管理器:实现TransactionFactory接口.type指定为全类名
dataSource:数据源;
type:数据源类型;UNPOOLED(UnpooledDataSourceFactory)
|POOLED(PooledDataSourceFactory)
|JNDI(JndiDataSourceFactory)
自定义数据源:实现DataSourceFactory接口,type是全类名
-->
<environments default="dev_mysql">
<environment id="dev_mysql">
<transactionManager type="JDBC"></transactionManager>
<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>
<environment id="dev_oracle">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${orcl.driver}" />
<property name="url" value="${orcl.url}" />
<property name="username" value="${orcl.username}" />
<property name="password" value="${orcl.password}" />
</dataSource>
</environment>
</environments>
<!-- 5、databaseIdProvider:支持多数据库厂商的;
type="DB_VENDOR":VendorDatabaseIdProvider
作用就是得到数据库厂商的标识(驱动getDatabaseProductName()),mybatis就能根据数据库厂商标识来执行不同的sql;
MySQL,Oracle,SQL Server,xxxx
-->
<databaseIdProvider type="DB_VENDOR">
<!-- 为不同的数据库厂商起别名 -->
<property name="MySQL" value="mysql"/>
<property name="Oracle" value="oracle"/>
<property name="SQL Server" value="sqlserver"/>
</databaseIdProvider>
<!-- 将我们写好的sql映射文件(EmployeeMapper.xml)一定要注册到全局配置文件(mybatis-config.xml)中 -->
<!-- 6、mappers:将sql映射注册到全局配置中 -->
<mappers>
<!--
mapper:注册一个sql映射
注册配置文件
resource:引用类路径下的sql映射文件
mybatis/mapper/EmployeeMapper.xml
url:引用网路路径或者磁盘路径下的sql映射文件
file:///var/mappers/AuthorMapper.xml
注册接口
class:引用(注册)接口,
1、有sql映射文件,映射文件名必须和接口同名,并且放在与接口同一目录下;
2、没有sql映射文件,所有的sql都是利用注解写在接口上;
推荐:
比较重要的,复杂的Dao接口我们来写sql映射文件
不重要,简单的Dao接口为了开发快速可以使用注解;
-->
<!-- <mapper resource="mybatis/mapper/EmployeeMapper.xml"/> -->
<!-- <mapper class="com.xz.mybatis.dao.EmployeeMapperAnnotation"/> -->
<!-- 批量注册: -->
<package name="com.xz.mybatis.dao"/>
</mappers>
</configuration>
3.1 properties
mybatis可以使用properties来引入外部properties配置文件的内容;
resource:引入类路径下的资源
url:引入网络路径或者磁盘路径下的资源
<properties resource="dbconfig.properties"></properties>
<environments default="dev_mysql">
<environment id="dev_mysql">
<transactionManager type="JDBC"></transactionManager>
<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>
3.2 settings
settings包含很多重要的设置项
setting:用来设置每一个设置项
name:设置项名
value:设置项取值
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
3.3 typeAliases
typeAliases:别名处理器:可以为我们的java类型起别名;别名不区分大小写,一般推荐不用别名。
- 方式一:给全类名起别名
<typeAliases>
<!-- 以后在Mapper.xml中使用此类型时无需再写全类名,直接写User即可 -->
<typeAlias type="com.qizegao.pojo.User" alias="User"/>
</typeAliases>
- 方式二:扫描指定包下的类,某一类的别名默认是类名首字母小写
<typeAliases>
<!-- 此包下的User类的别名成为user -->
<package name="com.qizegao.pojo"/>
</typeAliases>
注意:可以在对应的类上加注解@Alias(“xxx”),则类名的别名成为指定的xxx
3.4 environments
- environments:环境们,mybatis可以配置多种环境 ,default指定使用某种环境。可以达到快速切换环境。
- environment:配置一个具体的环境信息;必须有两个标签;id代表当前环境的唯一标识
- transactionManager:事务管理器;
- type:
- 事务管理器的类型:JDBC(JdbcTransactionFactory)|MANAGED(ManagedTransactionFactory)
- 自定义事务管理器:实现TransactionFactory接口.type指定为全类名
- dataSource:数据源; type:数据源类型;UNPOOLED(UnpooledDataSourceFactory);|POOLED(PooledDataSourceFactory);|JNDI(JndiDataSourceFactory)自定义数据源:实现DataSourceFactory接口,type是全类名
- type:
<!--配置两个环境;通过default切换环境-->
<environments default="dev_mysql">
<!--第一个环境;id标识不同的环境-->
<environment id="dev_mysql">
<transactionManager type="JDBC"></transactionManager>
<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>
<!--第二个环境;oracle需要在properties文件里面配置参数-->
<environment id="dev_oracle">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${orcl.driver}" />
<property name="url" value="${orcl.url}" />
<property name="username" value="${orcl.username}" />
<property name="password" value="${orcl.password}" />
</dataSource>
</environment>
</environments>
3.5 databaseIdProvider
databaseIdProvider:支持多数据库厂商的;
type=“DB_VENDOR”:VendorDatabaseIdProvider
作用就是得到数据库厂商的标识(驱动getDatabaseProductName()),mybatis就能根据数据库厂商标识来执行不同的sql;
MySQL,Oracle,SQL Server,xxxx
<databaseIdProvider type="DB_VENDOR">
<!-- 为不同的数据库厂商起别名 -->
<property name="MySQL" value="mysql"/>
<property name="Oracle" value="oracle"/>
<property name="SQL Server" value="sqlserver"/>
</databaseIdProvider>
使用:
- 使用不同的环境需要在environments里面切换环境
- 在mapper里面需要用databaseId标识使用的数据库。
<select id="getEmpById" resultType="com.xz.mybatis.bean.Employee"
databaseId="mysql">
select * from tbl_employee where id = #{id}
</select>
<select id="getEmpById" resultType="com.xz.mybatis.bean.Employee"
databaseId="oracle">
select EMPLOYEE_ID id,LAST_NAME lastName,EMAIL email
from employees where EMPLOYEE_ID=#{id}
</select>
3.6 mappers
mappers:将sql映射注册到全局配置中
- 方式一:resource:引用类路径下的sql映射文件
<mapper resource="mybatis/mapper/EmployeeMapper.xml"/>
-
方式二:url:引用网路路径或者磁盘路径下的sql映射文件
-
方式三:class:引用(注册)接口
- 有mapper文件:映射文件名必须和接口同名,并且放在与接口同一目录下;
- 没有mapper文件:所有的sql都是利用注解写在接口上;
<mapper class="com.xz.mybatis.dao.EmployeeMapperAnnotation"/>
public interface EmployeeMapperAnnotation {
@Select("select * from tbl_employee where id=#{id}")
public Employee getEmpById(Integer id);
}
- 方式四:package:同class。
4. SQL映射文件
EmployeeMapper.xml内容:
<mapper namespace="com.xz.dao.EmployeeMapper">
<select id="getEmpByLastNameLikeReturnMap" resultType="java.util.Map">
select * from Employee where email like #{lastName}
</select>
<select id="getEmpByIdReturnMap" resultType="java.util.Map">
select id,last_name,email,gender from Employee where id=#{id};
</select>
<select id="getEmpsByLastNameLike" resultType="com.xz.bean.Employee">
select * from Employee where last_name like #{lastName};
</select>
<select id="getEmpByList" resultType="com.xz.bean.Employee">
select id,last_name,email,gender from Employee where id=#{list[0]};
</select>
<select id="getEmpByMap" resultType="com.xz.bean.Employee">
select id,last_name,email,gender from ${table} where id=#{id} and last_name=#{lastName};
</select>
<select id="getEmpByIdAndLastName" resultType="com.xz.bean.Employee">
select id,last_name,email,gender from Employee where id=#{id} and last_name=#{last_name};
</select>
<!-- public Employee getEmpById(Integer id);-->
<select id="getEmpById" resultType="com.xz.bean.Employee">
select id,last_name,email,gender from Employee where id=#{id};
</select>
<!--public void addEmp(Employee employee);-->
<!--parameterType:传入的参数,可以省略-->
<insert id="addEmp" parameterType="com.xz.bean.Employee"
useGeneratedKeys="true" keyProperty="id">
insert into Employee(last_name,email,gender) values (#{lastName},#{email},#{gender});
</insert>
<!--public void updateEmp(Employee employee);-->
<update id="updateEmp">
update Employee
set last_name=#{lastName},
email=#{email},
gender=#{gender}
where id = #{id};
</update>
<delete id="deleteEmpById">
delete from Employee where id=#{id};
</delete>
</mapper>
EmployeeMapper.java内容:
public interface EmployeeMapper {
@MapKey("last_name")
public Map<String, Employee> getEmpByLastNameLikeReturnMap(String lastName);
public Map<String, Object> getEmpByIdReturnMap(Integer id);
public List<Employee> getEmpsByLastNameLike(String lastName);
public Employee getEmpByList(List<Integer> ids);
public Employee getEmpByMap(Map<String, Object> map);
public Employee getEmpByIdAndLastName(@Param("id") Integer id, @Param("last_name") String lastName);
public Employee getEmpById(Integer id);
public void addEmp(Employee employee);
public void updateEmp(Employee employee);
public void deleteEmpById(Integer id);
}
4.1 传入唯一参数
若传入参数只有唯一值,则填入任何参数都可以。例如这里的#{id}也可以写成#{idabc}
resultType:返回的对象
<!-- public Employee getEmpById(Integer id);-->
<select id="getEmpById" resultType="com.xz.bean.Employee">
select id,last_name,email,gender from Employee where id=#{id};
</select>
4.2 传入对象
若传入的是一个对象参数,例如这里的Employee,则在value里面填入Employee对象的相关属性即可。
parameterType:传入的参数,可以省略.
values (#{lastName},#{email},#{gender});
<insert id="addEmp" parameterType="com.xz.bean.Employee"
useGeneratedKeys="true" keyProperty="id">
insert into Employee(last_name,email,gender) values (#{lastName},#{email},#{gender});
</insert>
4.3 传入多个参数
4.3.1 有@Param()注解
- 在接口方法对应的行参前面加上@Param(“”)注解,可以在mapper文件中直接使用传入的参数。@Param(“id”) Integer id,
public Employee getEmpByIdAndLastName(@Param("id") Integer id, @Param("last_name") String lastName);
- 在Mapper文件中:这里的id和last_name必须要和接口方法对应。id=#{id}
<select id="getEmpByIdAndLastName" resultType="com.xz.bean.Employee">
select id,last_name,email,gender from Employee where id=#{id} and last_name=#{last_name};
</select>
4.3.2 无@Param()注解
- 普通方法,没有注解:
public Employee getEmpByIdAndLastName(Integer id, String lastName);
- 在Mapper文件中必须使用param1…paramN或者是0…N-1:id=#{param1}
<select id="getEmpByIdAndLastName" resultType="com.xz.bean.Employee">
select id,last_name,email,gender from Employee where id=#{param1} and last_name=#{param2};
</select>
或者:id=#{0}
<select id="getEmpByIdAndLastName" resultType="com.xz.bean.Employee">
select id,last_name,email,gender from Employee where id=#{0} and last_name=#{1};
</select>
4.3.3传入Map
若想传入多个参数,也可以在接口方法中传入一个map
public Employee getEmpByMap(Map<String, Object> map);
在Mapper文件中:id=#{id} and last_name=#{lastName};
<select id="getEmpByMap" resultType="com.xz.bean.Employee">
select id,last_name,email,gender from Employee where id=#{id} and last_name=#{lastName};
</select>
则在测试类中传入map即可,但是注意键值对之间的对应:map.put(“id”, 1); map.put(“lastName”, “xz”);
@Test
public void test07() throws IOException {
SqlSession openSession = getSqlSessionFactory().openSession();
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
try {
Map map = new HashMap();
map.put("id", 1);
map.put("lastName", "xz");
Employee xz = mapper.getEmpByMap(map);
System.out.println(xz);
} finally {
openSession.close();
}
}
4.3.4 返回集合
resultType:如果返回的是一个集合,要写集合中元素的类型,而非List类型
<!-- public List<Employee> getEmpsByLastNameLike(String lastName); -->
<!--resultType:如果返回的是一个集合,要写集合中元素的类型 -->
<select id="getEmpsByLastNameLike" resultType="com.xz.mybatis.bean.Employee">
select * from tbl_employee where last_name like #{lastName}
</select>
public List<Employee> getEmpsByLastNameLike(String lastName);
4.3.5 返回Map
-
返回的是一条map信息:
这里idea会报错没有@Mapkey(),但是因为是返回一条map信息,所以运行没有异常。key就是列名,值就是对应的值。
//返回一条记录的map;key就是列名,值就是对应的值
public Map<String, Object> getEmpByIdReturnMap(Integer id);
<!--public Map<String, Object> getEmpByIdReturnMap(Integer id); -->
<select id="getEmpByIdReturnMap" resultType="map">
select * from tbl_employee where id=#{id}
</select>
控制台输出信息:{gender=0, last_name=jerry, id=2, email=jerry@qq.com}
- 返回多条map
此时需要注解@Mapkey(),表示哪个属性作为map的key
//多条记录封装一个map:Map<Integer,Employee>:键是这条记录的主键,值是记录封装后的javaBean
//@MapKey:告诉mybatis封装这个map的时候使用哪个属性作为map的key
@MapKey("lastName")
public Map<String, Employee> getEmpByLastNameLikeReturnMap(String lastName);
<!--public Map<Integer, Employee> getEmpByLastNameLikeReturnMap(String lastName); -->
<select id="getEmpByLastNameLikeReturnMap" resultType="com.xz.mybatis.bean.Employee">
select * from tbl_employee where last_name like #{lastName}
</select>
控制台输出信息:{xz={gender=1, last_name=xz, id=1, email=zxxz@qqcom}, ZELDA={gender=0, last_name=ZELDA, id=9, email=ZELDA@qq.com}}
4.4 ${}和#{}
#{}:是以预编译的形式,将参数设置到sql语句中;PreparedStatement;防止sql注入
${}:取出的值直接拼装在sql语句中;会有安全问题;
例如:${}直接将id拼在sql语句中
select * from tbl_employee where id=${id} and last_name=#{lastName}
Preparing: select * from tbl_employee where id=2 and last_name=?
一般不推荐使用 , 但 是 在 下 面 这 些 情 况 时 只 能 只 用 {},但是在下面这些情况时只能只用 ,但是在下面这些情况时只能只用{}:
-
原生jdbc不支持占位符的地方我们就可以使用${}进行取值
比如分表、排序。。。;按照年份分表拆分select * from ${year}_salary where xxx; select * from tbl_employee order by ${f_name} ${order}
5. 映射文件进阶
文件结构:
![截屏2021-08-19 20.59.49.png](https://i-blog.csdnimg.cn/blog_migrate/bcea15fc6222cd36019722b021f4673b.png)
在Employee表的基础上再创建tbl_dept表,其中Employee表d_id外链接于tbl_dept表:
- Employee.java
public class Employee {
private Integer id;
private String lastName;
private String email;
private String gender;
private Department department;
...
}
- Department.java
public class Department {
private Integer id;
private String departmentName;
private List<Employee> emps;
...
}
- DepartmentMapper.java
public interface DepartmentMapper {
public Department getDeptById(Integer id);
public Department getDeptByIdPlus(Integer id);
public Department getDeptByIdStep(Integer id);
}
- EmployeeMapperPlus.java
public interface EmployeeMapperPlus {
public Employee getEmpById(Integer id);
public Employee getEmpAndDept(Integer id);
public Employee getEmpByIdStep(Integer id);
public List<Employee> getEmpsByDeptId(Integer deptId);
}
- EmployeeMapperPlus.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.xz.dao.EmployeeMapperPlus">
<resultMap id="MySimpleEmp" type="com.xz.bean.Employee">
<id column="id" property="id"></id>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
</resultMap>
<select id="getEmpById" resultMap="MySimpleEmp">
select * from Employee where id=#{id};
</select>
<resultMap id="MyDifEmp1" type="com.xz.bean.Employee">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="d_id" property="department.id"/>
<result column="dept_name" property="department.departmentName"/>
</resultMap>
<resultMap id="MyDifEmp2" type="com.xz.bean.Employee">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<association property="department" javaType="com.xz.bean.Department">
<id column="d_id" property="id"/>
<result column="dept_name" property="departmentName"/>
</association>
</resultMap>
<select id="getEmpAndDept" resultMap="MyDifEmp2">
SELECT e.id,e.last_name,e.email,e.gender,e.d_id,d.dept_name FROM Employee e,tbl_dept d WHERE e.d_id=d.id and e.id=#{id};
</select>
<resultMap id="MyEmpByStep" type="com.xz.bean.Employee">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<association property="department" select="com.xz.dao.DepartmentMapper.getDeptById" column="d_id"/>
</resultMap>
<select id="getEmpByIdStep" resultMap="MyEmpByStep">
select * from Employee where id=#{id};
</select>
<select id="getEmpsByDeptId" resultType="com.xz.bean.Employee">
select * from Employee where d_id=#{deptId};
</select>
</mapper>
- DepartmentMapper.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.xz.dao.DepartmentMapper">
<select id="getDeptById" resultType="com.xz.bean.Department">
select id,dept_name departmentName FROM tbl_dept WHERE id=#{id};
</select>
<resultMap id="MyDept" type="com.xz.bean.Department">
<id column="did" property="id"/>
<result column="dept_name" property="departmentName"/>
<collection property="emps" ofType="com.xz.bean.Employee">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="email" property="email"/>
<result column="gender" property="gender"/>
<association property="department" javaType="com.xz.bean.Department">
<id column="id" property="id"/>
<result column="dept_name" property="departmentName"/>
</association>
</collection>
</resultMap>
<select id="getDeptByIdPlus" resultMap="MyDept">
SELECT e.id, e.last_name, e.email, e.gender, d.id did, d.dept_name
FROM tbl_dept d
LEFT JOIN Employee e ON e.d_id = d.id
WHERE d.id = #{id};
</select>
<resultMap id="MyDeptStep" type="com.xz.bean.Department">
<id column="id" property="id"/>
<result column="dept_name" property="departmentName"/>
<collection property="emps" select="com.xz.dao.EmployeeMapperPlus.getEmpsByDeptId" column="{deptId=id}"/>
</resultMap>
<select id="getDeptByIdStep" resultMap="MyDeptStep">
select id, dept_name
from tbl_dept
where id = #{id};
</select>
</mapper>
5.1 resultMap
resultMap:可以指定sql语句中和java对象所对应的值。
在中不需要写resultType,而写resultMap对应的id值
id定义主键会底层有优化;
column:指定哪一列
property:指定对应的javaBean属性
-
其中last_name是sql中的属性,而lastName是javaBean中的属性,不需要开启驼峰命名规则,在result中即可对应。
<resultMap type="com.atguigu.mybatis.bean.Employee" id="MySimpleEmp">
<!--指定主键列的封装规则
id定义主键会底层有优化;
column:指定哪一列
property:指定对应的javaBean属性
-->
<id column="id" property="id"/>
<!-- 定义普通列封装规则 -->
<result column="last_name" property="lastName"/>
<!-- 其他不指定的列会自动封装:我们只要写resultMap就把全部的映射规则都写上。 -->
<result column="email" property="email"/>
<result column="gender" property="gender"/>
</resultMap>
<!-- resultMap:自定义结果集映射规则; -->
<!-- public Employee getEmpById(Integer id); -->
<select id="getEmpById" resultMap="MySimpleEmp">
select * from tbl_employee where id=#{id}
</select>
5.2 普通联合查询
联合查询:级联属性封装结果集
property使用:对象属性.属性。
<resultMap type="com.atguigu.mybatis.bean.Employee" id="MyDifEmp">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
<result column="did" property="dept.id"/>
<result column="dept_name" property="dept.departmentName"/>
</resultMap>
<select id="getEmpAndDept" resultMap="MyDifEmp">
SELECT e.id,e.last_name,e.email,e.gender,e.d_id,d.dept_name FROM Employee e,tbl_dept d WHERE e.d_id=d.id and e.id=#{id};
</select>
5.3 association联合查询
使用association定义关联的单个对象的封装规则;和上述的普通的联合查询效果一致。
association可以指定联合的javaBean对象
property=“dept”:指定哪个属性是联合的对象
javaType:指定这个属性对象的类型[不能省略]
<resultMap type="com.atguigu.mybatis.bean.Employee" id="MyDifEmp2">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
<!-- association可以指定联合的javaBean对象
property="dept":指定哪个属性是联合的对象
javaType:指定这个属性对象的类型[不能省略]
-->
<association property="dept" javaType="com.atguigu.mybatis.bean.Department">
<id column="did" property="id"/>
<result column="dept_name" property="departmentName"/>
</association>
</resultMap>
<!-- public Employee getEmpAndDept(Integer id);-->
<select id="getEmpAndDept" resultMap="MyDifEmp">
SELECT e.id id,e.last_name last_name,e.gender gender,e.d_id d_id,
d.id did,d.dept_name dept_name FROM tbl_employee e,tbl_dept d
WHERE e.d_id=d.id AND e.id=#{id}
</select>
5.4 association分步查询
1、先按照员工id查询员工信息
2、根据查询员工信息中的d_id值去部门表查出部门信息
3、部门设置到员工中;
- 先按照员工id查询员工信息:在EmployeeMapperPlus中:
这里通过方法传入的id值可以获取到对应employee所有的信息,包括department的d_id
<select id="getEmpByIdStep" resultMap="MyEmpByStep">
select last_name,email,gender,d_id from Employee where id=#{id};
</select>
- 根据查询员工信息中的d_id值去部门表查出部门信息
这里传入的id=#{id}就是从上面的d_id传入的,但是名字问题用下面代码解答:
<!--public Department getDeptById(Integer id); -->
<select id="getDeptById" resultType="com.atguigu.mybatis.bean.Department">
select id,dept_name departmentName from tbl_dept where id=#{id}
</select>
-
部门设置到员工中:使用association
property=“dept” :是需要设置的Employee中department中对应的dept属性。
column=“d_id”:在select语句中得到的结果,需要传入到.DepartmentMapper的getDeptById方法的值。
select=“com.atguigu.mybatis.dao.DepartmentMapper.getDeptById”:i_id需要传入的地址。
由上面分析可以知道,EmployeeMapperPlus全部代码如下
<!-- id last_name email gender d_id -->
<resultMap type="com.atguigu.mybatis.bean.Employee" id="MyEmpByStep">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="email" property="email"/>
<result column="gender" property="gender"/>
<!-- association定义关联对象的封装规则
select:表明当前属性是调用select指定的方法查出的结果
column:指定将哪一列的值传给这个方法
流程:使用select指定的方法(传入column指定的这列参数的值)查出对象,并封装给property指定的属性
-->
<association property="dept"
select="com.atguigu.mybatis.dao.DepartmentMapper.getDeptById"
column="d_id">
</association>
</resultMap>
<!-- public Employee getEmpByIdStep(Integer id);-->
<select id="getEmpByIdStep" resultMap="MyEmpByStep">
select * from tbl_employee where id=#{id}
</select>
5.5 collection联合查询
查询部门的时候将部门对应的所有员工信息也查询出来:
在DepartmentMapper中:ofType:指定集合里面元素的类型
<!--嵌套结果集的方式,使用collection标签定义关联的集合类型的属性封装规则 -->
<resultMap type="com.atguigu.mybatis.bean.Department" id="MyDept">
<id column="did" property="id"/>
<result column="dept_name" property="departmentName"/>
<!--
collection定义关联集合类型的属性的封装规则
ofType:指定集合里面元素的类型
-->
<collection property="emps" ofType="com.atguigu.mybatis.bean.Employee">
<!-- 定义这个集合中元素的封装规则 -->
<id column="eid" property="id"/>
<result column="last_name" property="lastName"/>
<result column="email" property="email"/>
<result column="gender" property="gender"/>
</collection>
</resultMap>
<!-- public Department getDeptByIdPlus(Integer id); -->
<select id="getDeptByIdPlus" resultMap="MyDept">
SELECT d.id did,d.dept_name dept_name,
e.id eid,e.last_name last_name,e.email email,e.gender gender
FROM tbl_dept d
LEFT JOIN tbl_employee e
ON d.id=e.d_id
WHERE d.id=#{id}
</select>
5.6 collection分步查询
这里和association步骤类似,不多做赘述。
在DepartmentMapper中:
<!-- collection:分段查询 -->
<resultMap type="com.atguigu.mybatis.bean.Department" id="MyDeptStep">
<id column="id" property="id"/>
<id column="dept_name" property="departmentName"/>
<collection property="emps"
select="com.atguigu.mybatis.dao.EmployeeMapperPlus.getEmpsByDeptId"
column="{deptId=id}" fetchType="lazy"></collection>
</resultMap>
<!-- public Department getDeptByIdStep(Integer id); -->
<select id="getDeptByIdStep" resultMap="MyDeptStep">
select id,dept_name from tbl_dept where id=#{id}
</select>
在EmployeeMapperPlus中:注意这里返回的employee是多个值,是List
<!-- public List<Employee> getEmpsByDeptId(Integer deptId); -->
<select id="getEmpsByDeptId" resultType="com.atguigu.mybatis.bean.Employee">
select * from tbl_employee where d_id=#{deptId}
</select>
注意⚠️
可以看到collection和association是类似的,两者都是在两个相关的表中实现联合查询。
同时:和中**column="{deptId=id}"**可以传入多个值,也可以向即中传入多个值。
fetchType=“lazy”:在这里也可以实现懒加载。
但是两者具有一定的区别:collection主要是实现在两个表都返回一条记录时使用;而association主要是在其中一条记录值是List时使用
而且在中使用的是javaType,而是ofType。
5.6 懒加载
可以使用延迟加载(懒加载);(按需加载)
Employee==>Dept:
我们每次查询Employee对象的时候,都将一起查询出来。
部门信息在我们使用的时候再去查询;
分段查询的基础之上加上两个配置:
在mybatis-config.xml中:
<settings>
<!--显示的指定每个我们需要更改的配置的值,即使他是默认的。防止版本更新带来的问题 -->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
5.7 鉴别器
鉴别器:mybatis可以使用discriminator判断某列的值,然后根据某列的值改变封装行为
封装Employee:
如果查出的是女生:就把部门信息查询出来,否则不查询;
如果是男生,把last_name这一列的值赋值给email;
<resultMap type="com.atguigu.mybatis.bean.Employee" id="MyEmpDis">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="email" property="email"/>
<result column="gender" property="gender"/>
<!--
column:指定判定的列名
javaType:列值对应的java类型 -->
<discriminator javaType="string" column="gender">
<!--女生 resultType:指定封装的结果类型;不能缺少。/resultMap-->
<case value="0" resultType="com.atguigu.mybatis.bean.Employee">
<association property="dept"
select="com.atguigu.mybatis.dao.DepartmentMapper.getDeptById"
column="d_id">
</association>
</case>
<!--男生 ;如果是男生,把last_name这一列的值赋值给email; -->
<case value="1" resultType="com.atguigu.mybatis.bean.Employee">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="last_name" property="email"/>
<result column="gender" property="gender"/>
</case>
</discriminator>
</resultMap>
6. 动态SQL
动态SQL:一种更复杂的查询,可以设置各种查询条件。
- EmployeeMapperDynamic.java
public interface EmployeeMapperDynamic {
public List<Employee> getEmpsByConditionIf(Employee employee);
public List<Employee> getEmpsByConditionTrim(Employee employee);
public List<Employee> getEmpsByConditionChoose(Employee employee);
public void updateEmp(Employee employee);
public List<Employee> getEmpsByConditionForeach(@Param("ids")List<Integer> ids);
public void addEmps(@Param("emps")List<Employee> emps);
}
- EmployeeMapperDynamic.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.xz.dao.EmployeeMapperDynamic">
<select id="getEmpsByConditionIf" resultType="com.xz.bean.Employee">
select * from Employee
<where>
<if test="id!=null">
id=#{id}
</if>
<if test="gender==0 or gender=1">
and gender=#{gender}
</if>
<if test="email!=null && email.trim()!=""">
and email=#{email}
</if>
<if test="lastName!=null">
and last_name like #{lastName};
</if>
</where>
</select>
<select id="getEmpsByConditionTrim" resultType="com.xz.bean.Employee">
select * from Employee
<trim prefix="where" suffixOverrides="and">
<if test="id!=null">
id=#{id} and
</if>
<if test="lastName!=null && lastName!=""">
last_name like #{lastName} and
</if>
<if test="email!=null and email.trim()!=""">
email=#{email} and
</if>
<!-- ognl会进行字符串与数字的转换判断 "0"==0 -->
<if test="gender==0 or gender==1">
gender=#{gender}
</if>
</trim>
</select>
<select id="getEmpsByConditionChoose" resultType="com.xz.bean.Employee">
select * from Employee
<where>
<choose>
<when test="id>3">
id=#{id}
</when>
<when test="lastName!=null">
last_name=#{lastName}
</when>
<otherwise>
id=1;
</otherwise>
</choose>
</where>
</select>
<update id="updateEmp">
update Employee
<set>
<if test="lastName!=null">
last_name=#{lastName},
</if>
<if test="email!=null">
email=#{email},
</if>
<if test="gender!=null">
gender=#{gender},
</if>
<if test="department!=null">
d_id=#{department.id}
</if>
</set>
where id=#{id};
</update>
<select id="getEmpsByConditionForeach" resultType="com.xz.bean.Employee">
select * from Employee
<foreach collection="ids" item="item_id" separator="," open="where id in (" close=")">
#{item_id}
</foreach>
</select>
<insert id="addEmps">
insert into Employee(<include refid="insertColumn"/>) values
<foreach collection="emps" item="emp" separator=",">
(#{emp.lastName},#{emp.email},#{emp.gender})
</foreach>
</insert>
<sql id="insertColumn">
last_name,email,gender
</sql>
</mapper>
6.1 if&where
:判断。
test:设置判断条件,遇见特殊符号应该去写转义字符。
:相当于直接在sql后面加where的自动优化方法
<!-- 查询员工,要求,携带了哪个字段查询条件就带上这个字段的值 -->
<!-- public List<Employee> getEmpsByConditionIf(Employee employee); -->
<select id="getEmpsByConditionIf" resultType="com.atguigu.mybatis.bean.Employee">
select * from tbl_employee
<!-- where -->
<where>
<!-- test:判断表达式(OGNL)
OGNL参照PPT或者官方文档。
c:if test
从参数中取值进行判断
遇见特殊符号应该去写转义字符:
&&:
-->
<if test="id!=null">
id=#{id}
</if>
<if test="lastName!=null && lastName!=""">
and last_name like #{lastName}
</if>
<if test="email!=null and email.trim()!=""">
and email=#{email}
</if>
<!-- ognl会进行字符串与数字的转换判断 "0"==0 -->
<if test="gender==0 or gender==1">
and gender=#{gender}
</if>
</where>
</select>
6.2 trim
trim可以替代where,是一种更全面的字符拼串方式。
后面多出的and或者or where标签不能解决
prefix="":前缀:trim标签体中是整个字符串拼串 后的结果。
prefix给拼串后的整个字符串加一个前缀
prefixOverrides="":
前缀覆盖: 去掉整个字符串前面多余的字符
suffix="":后缀
suffix给拼串后的整个字符串加一个后缀
suffixOverrides=""
后缀覆盖:去掉整个字符串后面多余的字符
<!--public List<Employee> getEmpsByConditionTrim(Employee employee); -->
<select id="getEmpsByConditionTrim" resultType="com.atguigu.mybatis.bean.Employee">
select * from tbl_employee
<!-- 后面多出的and或者or where标签不能解决
prefix="":前缀:trim标签体中是整个字符串拼串 后的结果。
prefix给拼串后的整个字符串加一个前缀
prefixOverrides="":
前缀覆盖: 去掉整个字符串前面多余的字符
suffix="":后缀
suffix给拼串后的整个字符串加一个后缀
suffixOverrides=""
后缀覆盖:去掉整个字符串后面多余的字符
-->
<!-- 自定义字符串的截取规则 -->
<trim prefix="where" suffixOverrides="and">
<if test="id!=null">
id=#{id} and
</if>
<if test="lastName!=null && lastName!=""">
last_name like #{lastName} and
</if>
<if test="email!=null and email.trim()!=""">
email=#{email} and
</if>
<!-- ognl会进行字符串与数字的转换判断 "0"==0 -->
<if test="gender==0 or gender==1">
gender=#{gender}
</if>
</trim>
</select>
6.3 choose
相当于java语句中的case,只会选择一个满足条件的进行匹配。
when:是choose的查询条件,相当于前面说的if
<!-- public List<Employee> getEmpsByConditionChoose(Employee employee); -->
<select id="getEmpsByConditionChoose" resultType="com.atguigu.mybatis.bean.Employee">
select * from tbl_employee
<where>
<!-- 如果带了id就用id查,如果带了lastName就用lastName查;只会进入其中一个 -->
<choose>
<when test="id!=null">
id=#{id}
</when>
<when test="lastName!=null">
last_name like #{lastName}
</when>
<when test="email!=null">
email = #{email}
</when>
<otherwise>
gender = 0
</otherwise>
</choose>
</where>
</select>
6.3 set
在更新语句中使用,使用方式和前面的where一样,可以用trim代替
<!--public void updateEmp(Employee employee); -->
<update id="updateEmp">
<!-- Set标签的使用 -->
update tbl_employee
<set>
<if test="lastName!=null">
last_name=#{lastName},
</if>
<if test="email!=null">
email=#{email},
</if>
<if test="gender!=null">
gender=#{gender}
</if>
</set>
where id=#{id}
</update>
使用trim代替:
<trim prefix="set" suffixOverrides=",">
<if test="lastName!=null">
last_name=#{lastName},
</if>
<if test="email!=null">
email=#{email},
</if>
<if test="gender!=null">
gender=#{gender}
</if>
</trim>
where id=#{id}
6.4 foreach
collection:指定要遍历的集合:
list类型的参数会特殊处理封装在map中,map的key就叫list
item:将当前遍历出的元素赋值给指定的变量
separator:每个元素之间的分隔符
open:遍历出所有结果拼接一个开始的字符
close:遍历出所有结果拼接一个结束的字符
index:索引。遍历list的时候是index就是索引,item就是当前值
遍历map的时候index表示的就是map的key,item就是map的值
#{变量名}就能取出变量的值也就是当前遍历出的元素
<!--public List<Employee> getEmpsByConditionForeach(List<Integer> ids); -->
<select id="getEmpsByConditionForeach" resultType="com.atguigu.mybatis.bean.Employee">
select * from tbl_employee
<foreach collection="ids" item="item_id" separator=","
open="where id in(" close=")">
#{item_id}
</foreach>
</select>
第二种实例:
<!-- 批量保存 -->
<!--public void addEmps(@Param("emps")List<Employee> emps); -->
<!--MySQL下批量保存:可以foreach遍历 mysql支持values(),(),()语法-->
<insert id="addEmps">
insert into tbl_employee(
<include refid="insertColumn"></include>
)
values
<foreach collection="emps" item="emp" separator=",">
(#{emp.lastName},#{emp.email},#{emp.gender},#{emp.dept.id})
</foreach>
</insert><!-- -->
6.5 sql
抽取可重用的sql片段。方便后面引用
1、sql抽取:经常将要查询的列名,或者插入用的列名抽取出来方便引用
2、include来引用已经抽取的sql:
3、include还可以自定义一些property,sql标签内部就能使用自定义的属性
include-property:取值的正确方式${prop},
#{不能使用这种方式}
抽取sql:
<sql id="insertColumn">
<if test="_databaseId=='oracle'">
employee_id,last_name,email
</if>
<if test="_databaseId=='mysql'">
last_name,email,gender,d_id
</if>
</sql>
include使用sql:
<insert id="addEmps">
insert into Employee(<include refid="insertColumn"/>) values
<foreach collection="emps" item="emp" separator=",">
(#{emp.lastName},#{emp.email},#{emp.gender})
</foreach>
</insert>
6.6 两个内置参数
两个内置参数:
不只是方法传递过来的参数可以被用来判断,取值。。。
mybatis默认还有两个内置参数:
_parameter:代表整个参数
单个参数:_parameter就是这个参数
多个参数:参数会被封装为一个map;_parameter就是代表这个map
_databaseId:如果配置了databaseIdProvider标签。
_databaseId就是代表当前数据库的别名oracle
<!--public List<Employee> getEmpsTestInnerParameter(Employee employee); -->
<select id="getEmpsTestInnerParameter" resultType="com.atguigu.mybatis.bean.Employee">
<!-- bind:可以将OGNL表达式的值绑定到一个变量中,方便后来引用这个变量的值 -->
<bind name="_lastName" value="'%'+lastName+'%'"/>
<if test="_databaseId=='mysql'">
select * from tbl_employee
<if test="_parameter!=null">
where last_name like #{lastName}
</if>
</if>
<if test="_databaseId=='oracle'">
select * from employees
<if test="_parameter!=null">
where last_name like #{_parameter.lastName}
</if>
</if>
</select>
7. 缓存机制
mysql8中已经默认关闭查询缓存了,而如今缓存用的更多的则是redis,这里知道即可。