MyBatis基本使用
一、HelloWorld
1. 非接口式编程
1.1 创建全局配置文件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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="Hukeai0322"/>
</dataSource>
</environment>
</environments>
</configuration>
1.2 创建sql映射文件EmployeeMapper.xml
sql映文件中配置了每一个sql以及sql的封装规则等。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.mybatis.dao.EmployeeMapper">
<!--
namespace:名称空间,随便起名,com.atguigu.mybatis.EmployeeMapper
id:唯一标识,随便起名,selectEmp
resultType:返回值类型
#{id}:从传递过来的参数中取出id值
-->
<select id="selectEmp" resultType="com.atguigu.mybatis.bean.Employee">
<!-- 由于表中字段和属性名不一致,所以sql中要起别名 -->
select id,last_name lastName,email,gender from tbl_employee where id = #{id}
</select>
</mapper>
1.3 将sql映射文件注册在全局映射文件中
<?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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="Hukeai0322"/>
</dataSource>
</environment>
</environments>
<!-- 将我们写好的sql映射文件(EmployeeMapper.xml)注册到全局配置文件(mybatis-config.xml)中 -->
<mappers>
<mapper resource="EmployeeMapper.xml"/>
</mappers>
</configuration>
1.4 写代码
public void test() throws IOException{
//1、根据xml文件创建一个SqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2、获取SqlSession实例,能直接执行已经映射的sql语句
SqlSession session = sqlSessionFactory.openSession();
//参数1:sql的唯一标识,一般是namespace+id
//参数2:执行sql要用的参数
Employee employee;
try {
employee = session.selectOne("com.atguigu.mybatis.EmployeeMapper.selectEmp", 1);
System.out.println(employee);
} finally {
session.close();
}
}
1.根据全局配置文件得到SqlSessionFactory。
2.使用SqlSessionFactory,获取到SqlSession对象以进行增删改查一个SqlSession就代表和数据库的一次会话,用完关闭。
3.使用sql的唯一标识来告诉MyBatis执行哪个sql,sql都是保存在sql映射文件中的。
2. 接口式编程
1.1 创建全局配置文件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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="Hukeai0322"/>
</dataSource>
</environment>
</environments>
</configuration>
1.2 创建操作Employee表的接口EmployeeMapper
public interface EmployeeMapper {
public Employee getEmpById(Integer id);
}
1.3 创建sql映射文件EmployeeMapper.xml
sql映文件中配置了每一个sql以及sql的封装规则等。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.mybatis.dao.EmployeeMapper">
<!--
namespace:名称空间,指定为接口的全类名:com.atguigu.mybatis.dao.EmployeeMapper
id:唯一标识,指定为我接口中的方法名:getEmpById
resultType:返回值类型
#{id}:从传递过来的参数中取出id值
-->
<select id="getEmpById" resultType="com.atguigu.mybatis.bean.Employee">
select id,last_name lastName,email,gender from tbl_employee where id = #{id}
</select>
</mapper>
1.4 将sql映射文件注册在全局映射文件中
<?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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="Hukeai0322"/>
</dataSource>
</environment>
</environments>
<!-- 将我们写好的sql映射文件(EmployeeMapper.xml)注册到全局配置文件(mybatis-config.xml)中 -->
<mappers>
<mapper resource="EmployeeMapper.xml"/>
</mappers>
</configuration>
1.5 写代码
public void test1() throws IOException{
//1、根据xml文件创建一个SqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2、获取SqlSession实例,能直接执行已经映射的sql语句
SqlSession session = sqlSessionFactory.openSession();
try {
//3、获取接口的实现类对象
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
System.out.println(mapper);
Employee employee = mapper.getEmpById(1);
System.out.println(employee);
} finally {
session.close();
}
}
1.根据全局配置文件得到SqlSessionFactory。
2.使用SqlSessionFactory,获取到SqlSession对象以进行增删改查一个SqlSession就代表和数据库的一次会话,用完关闭。
3.调用接口中定义的方法,MyBatis会为接口自动的创建一个代理对象,代理对象去执行增删改查方法,该方法已经与sql文件中的sql语句进行了动态的绑定。
3. 总结
3.1 两个重要的配置文件
- mybatis的全局配置文件:包含数据库连接池信息,事务管理器信息等系统运行环境信息。
- sql映射文件:保存了每一个sql语句的映射信息,将sql抽取出来。
3.2 SqlSession
- SqlSession代表和数据库的一次会话,用完必须关闭。
- SqlSession和connection一样,都是非线程安全的,每次实现都应该去获取新的对象。
3.3 接口式编程优越性
- MyBatis采用接口式编程之前,我们只能利用sqlSession的原生方法来操作数据库,在该方法中指明需要映射的sql映射文件中的sql语句,此时无法对参数类型进行限制,也无法明确返回值的类型,没有一种合理的规范。
- MyBatis采用接口式编程之后,我们定义一个接口,将sql映射文件中的方法与接口中的方法进行绑定,此时,如果我们调用接口的方法时,mybatis会为这个接口生成一个代理对象,通过调用这个代理对象的方法,来调用sql映射文件中绑定的sql语句,实现对数据库的增删改查。通过接口可以限制传入的参数类型,也可以对返回值更加明确,接口就是一个标准的规范。
二、全局配置文件
<?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.mybatis的settings中包含很多重要的设置项
setting:用来设置每一个设置项
name:设置项名
value:设置项取值
-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
<!-- 3.typeAliases:别名处理器,可以为我们的java类型起别名,别名不区分大小写
① typeAlias:为某个java类型起别名
type:指定要起别名的类型的全类名
alias:指定新的别名,若不写则默认为类名小写
② package:为某个包下的所有类批量起别名
name:指定包名,为当前包以及下面所有的后代包的每一个类都起一个默认别名(类名小写)
③ 批量起别名的情况下,使用@Alias注解为某个类型指定新的别名
<typeAliases>
<typeAlias type="com.atguigu.mybatis.bean.Employee" alias="emp"/>
<package name="com.atguigu.mybatis.bean"/>
<typeAliases/>
-->
<typeAliases></typeAliases>
<!-- 4.environments:环境们,mybatis可以配置多种环境,default指定使用某种环境,以实现快速切换环境的目的
environment:配置一个环境信息,id代表当前环境的唯一标识,environment必须有两个标签,分别是transactionManager和dataSource
transactionManaget:事务管理器,type是事务管理器的类型,主要分为: JDBC(JdbcTranscationFactory)|
MANAGED(ManagerTranscationFactory)|
自定义事务管理器:实现TransactionFactory接口,此时type写全类名
dataSource:数据源,type是数据源类型,主要分为: UNPOOLED(UnpooledDataSourceFactory)|
POOLED(PooledDataSourceFactory)|
JNDI(JndiDataSourceFactory)|
自定义数据源:实现DataSourceFactory接口,此时type是全类名
-->
<environments default="dev_mysql">
<environment id="dev_mysql">
<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>
<!-- 5.databaseIdProvider:支持多数据库厂商的,type="DB_VENDOR"即VendorDatabaseIdProvider的作用就是得到数据库厂商的标识
(驱动getDatabaseProductName()),mybatis就能根据数据库厂商的标识来执行不同的sql,包括MySQL、Oracle、SQL Server等
-->
<databaseIdProvider type="DB_VENDOR">
<!-- 为不同的数据库厂商起别名 -->
<property name="MySQL" value="mysql"/>
<property name="Oracle" value="oracle"/>
<property name="SQL Server" value="sqlserver"/>
</databaseIdProvider>
<!-- 6.mappers:将sql映射注册到全局配置中
mapper:注册一个sql映射
① 注册映射文件:resource:引用类路径下的sql映射文件,url:引用网络路径或者磁盘路径下的sql映射文件
② 注册接口:class:接口全类名
1.有sql映射文件,要求把映射文件和接口放在同一路径下,并且映射文件要和接口同名
2.没有sql映射文件,所有sql都是利用注解写在接口上
推荐:比较重要的,复杂的Dao接口我们来写sql映射文件
不重要的,简单的Daoi接口为了开发快速可以使用注解
③ 批量注册:package:包名
-->
<!-- 将我们写好的sql映射文件(EmployeeMapper.xml)注册到全局配置文件(mybatis-config.xml)中 -->
<mappers>
<!-- <mapper resource="mybatis/mapper/EmployeeMapper.xml"/> -->
<!-- <mapper class="com.atguigu.mybatis.dao.EmployeeMapper"/> -->
<!-- <mapper class="com.atguigu.mybatis.dao.EmployeeMapperAnnotation"/> -->
<package name="com.atguigu.mybatis.dao"/>
</mappers>
</configuration>
1. 引入外部配置文件
- MyBatis可以使用
<properties>
标签来引入外部properties配置文件的内容,该标签有两个属性:① resource:引入类路径下的资源。② url:引入网络路径或者磁盘路径下的资源。此时,如果我们想要获取properties配置文件的内容,只需要使用${文件属性名}
即可。
2. 设置项
MyBatis的<settings>
范围内可以利用<setting>
标签设置很多重要的设置项,<setting>
标签有两个属性:① name:设置项名。② value:设置项取值。
3. 别名处理器
- MyBatis中可以通过设置
<typeAliases>
标签来统一为我们的java类型起别名,别名不区分大小写。 - 在
<typeAliases>
标签范围内,可以使用以下两个标签:
1.<typeAlias>
:为某个java类型起别名,标签包括两个属性:① type:指定要起别名的类型的全类名。
② alias:指定新的别名,若不写则默认为类名小写。
2.<package>
:为某个包下的所有类批量起别名,标签包括一个name属性,用于指定包名,为当前包以及下面所有的后代包的每一个类都起一个默认别名(类名小写)。在批量起别名的情况下,可以在bean上使用@Alias注解为该java类指定新的别名。
4. 多环境选择
- MyBatis可以配置多种环境,可以通过设置
<environments>
标签中的属性default为指定环境的id就可以指定要使用哪个环境。 - 在
<environments>
范围内,可以为每个环境配置一个<environment>
标签,每个<environments>
标签都有一个属性id,代表当前环境的唯一标识。 - 在
<environment>
标签范围内,可以设置如下标签:
1.<transactionManager>
:事务管理器,通过设置type属性,来定义事务管理器的类型,主要分为JDBC、MANAGED和自定义事务管理器(实现TransactionFactory接口,此时type写全类名)。
2.<dataSource>
:数据源,通过设置type属性,来定义数据源类型,主要分为 UNPOOLED、POOLED、JNDI和自定义数据源(实现DataSourceFactory接口,此时type是全类名)。
5. 数据库厂商支持
- MyBatis可以支持对不同厂商的数据库进行增删改查,由于sql语句是我们自己写的,所以我们需要自己在sql映射文件的标签中通过将其datbaseId属性赋予指定数据库标识来我们需要指定使用的是哪个数据库。
- 通过设置
<databaseIdProvider>
,并定义其属性type为DB_VENDOR,MyBatis就可以从数据库驱动中自动获得数据库厂商的标识,比如MySQL数据库的标识为MySQL,ORACLE数据库的标识为Oracle。 - 在
<databaseIdProvider>
的范围内,可以设置<property>
标签,通过设置该标签的name属性和value属性来自定义数据库的标识,name为MyBatis从驱动中获得的数据库标识,value为我们定义的值。
6. 将sql映射文件注册到全局配置
MyBatis通过<mapper>
标签来注册sql映射文件
6.1 方式一:通过注册sql映射文件来注册sql映射文件
<mapper>
中有两个属性,分别为:① resource:引用类路径下的sql映射文件。② url:引用网络路径或者磁盘路径下的sql映射文件。
6.2 方式二:通过注册接口来注册sql映射文件
大多数情况下,我们都采用接口式编程的方式来利用MyBatis,此时,除了直接注册映射文件外,我们还可以通过注册接口的方式来将sql映射文件注册到全局配置中。此时需要利用<mapper>
标签中的class属性。
6.2.1 存在sql映射文件
- 在class属性中指定接口的全类名,此时,要求把映射文件和接口放在同一路径下,并且映射文件要和接口同名。
6.2.2 没有sql映射文件
- 在class属性中指定接口的全类名,此时,所有sql都是利用注解写在接口上。
6.2.3 批量注册
- 通过设置
<mapper>
中的属性package,在package中指定需要批量导入的接口所在的包即可,但注意,此时,如果该接口存在sql映射文件仍需要把映射文件和接口放在同一路径下,并且映射文件要和接口同名。如果不存在时,则是将sql语句利用注解写在了接口上。
三、sql映射文件
1. insert、delete、update简单测试
1.1 insert
MyBatis允许增删改在接口的方法中直接定义以下类型返回值:Integer、Long、Boolean、void,而不需要在sql映射文件的标签中添加resultType属性。
<!-- public Boolean addEmp(Employee employee); -->
<insert id="addEmp">
insert into tbl_employee(last_name,email,gender)
values(#{lastName},#{email},#{gender})
</insert>
1.2 delete
<!-- public Long deleteEmpById(Integer id); -->
<delete id="deleteEmpById">
delete from tbl_employee where id = #{id}
</delete>
1.3 update
<!-- public Integer updateEmp(Employee employee); -->
<update id="updateEmp">
update tbl_employee
set last_name=#{lastName},email=#{email},gender=#{gender}
where id=#{id}
</update>
1.4 注意
此时因为sqlSessionFactory.openSession()创建的是非自动提交的SqlSession,所以我们操作数据库的代码需要在关闭该SqlSession之前,调用session.commit()方法手动提交数据。
public void test3() throws IOException{
//1、根据xml文件创建一个SqlSessionFactory对象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//2、获取SqlSession实例,能直接执行已经映射的sql语句
//注:获取到的SqlSession不会自动提交数据
SqlSession session = sqlSessionFactory.openSession();
try {
//3、获取接口的实现类对象
EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
//① 测试添加
Employee employee = new Employee(null, "jerry", "jerry@atguigu.com", "1");
mapper.addEmp(employee);
System.out.println(employee.getId());
//② 测试修改
// Employee employee = new Employee(1, "jerry","jerry@atguigu.com", "0");
// mapper.updateEmp(employee);
//③ 测试删除
// Long result = mapper.deleteEmpById(2);
// System.out.println(result);
//4.手动提交数据
session.commit();
} finally {
session.close();
}
}
2. insert进阶:MySQL支持自增主键,如何获取自增主键?
<insert>
标签中,除了id属性还额外存在两个属性:① userGeneratedKeys:默认关闭,需要人为置为true进行开启,开启后表示,使用自增主键获取主键值策略。② keyProperty:指定对应的主键属性,即MyBatis获取到主键值以后,将其封装给javaBean的哪个属性。(添加后主键值会自动赋予我们使用的对象的指定属性中)
<!-- public Boolean addEmp(Employee employee); -->
<!-- MySQL支持自增主键,自增主键的获取MyBatis也是利用statement.getGeneratedKeys() -->
<insert id="addEmp" useGeneratedKeys="true" keyProperty="id" databaseId="mysql">
insert into tbl_employee(last_name,email,gender)
values(#{lastName},#{email},#{gender})
</insert>
3. select
3.1 基本属性
- id:唯一标识符。用来引用这条语句,需要和接口的方法名一致。
- parameterType:参数类型。可以不传,MyBatis会根据TypeHandler自动推断。
- resultType:返回值类型。别名或者全类名,如果返回的是集合,定义集合中元素的类型。可以替换为resultMap,且不能和resultMap同时使用。
3.1 返回值是一个List
<!-- public List<Employee> getEmpByLastNameLike(String lastName); -->
<!-- resultType:如果返回的是一个集合,要写集合中元素的类型 -->
<select id="getEmpByLastNameLike" resultType="com.atguigu.mybatis.bean.Employee">
select * from tbl_employee where last_name like #{lastName}
</select>
3.2 返回值是一个Map
- 返回一条记录的map:key就是列名,值就是对应的值。注意:此时的resultType=map中的map为别名,是MyBatis自动为Map集合起的别名。
<!-- public Map<String, Object> getEmpByIdReturnMap(Integer id); --> <select id="getEmpByIdReturnMap" resultType="map"> select * from tbl_employee where id = #{id} </select>
- 多条记录封装一个map,则Map<Integer, Employee>键是我们在接口方法中通过
@MapKey 指定的属性
,值是记录封装后的JavaBean。resultType写的是集合里面value的类型。<!-- @MapKey("id") public Map<Integer, Employee> getEmpByLastNameReturnMap(String lastName); --> <select id="getEmpByLastNameReturnMap" resultType="com.atguigu.mybatis.bean.Employee"> select * from tbl_employee where last_name like #{lastName} </select>
4. resultMap详解
4.1 resultMap的基本使用
<!-- 自定义某个javaBean的封装规则
type:自定义规则的Java类型
id:唯一id,方便引用
-->
<resultMap type="com.atguigu.mybatis.bean.Employee" id="MyEmp">
<!-- 指定主键列的封装规则
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="MyEmp">
select * from tbl_employee where id = #{id}
</select>
1.resultMap和resultType功能类似,但是resultMap的功能更加强大一些,我们可以利用resutlMap将表中的字段和实体类进行映射,来处理实体类中属性名和表中字段名不一致的问题。
2.使用:① <resultMap>
标签中有两个属性:type
属性放置全类名,用来指明自定义规则的Java类型;
id
属性作为该标签的唯一id,以方便后续引用。② <resultMap>
标签中可以定义<id>
标签和<result>
标签。③ <id>
用于定义主键,其内部属性column
指定表中主键字段,property
属性指定对应实体类的属性名。④ <result>
用于定义普通字段,其内部属性column
指定表中字段,property
属性指定对应实体类的属性名。⑤ 不指定的列会自动封装,但最好我们只要写resultMap就把全部的映射规则都写上。
4.2 resultMap的进阶-关联查询一(封装单个对象)
<!--
resultMap的使用场景一:
查询Employee的同时查询员工对应的部门
Employee===Department
一个员工有与之对应的部门信息
-->
<!-- 联合查询:级联属性封装结果集 -->
<resultMap type="com.atguigu.mybatis.bean.Employee" id="MyDifEmp1">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="email" property="email"/>
<result column="gender" property="gender"/>
<result column="d_id" property="dept.id"/>
<result column="dept_name" property="dept.departmentName"/>
</resultMap>
<!-- public Employee getEmpAndDept(Integer id); -->
<select id="getEmpAndDept" resultMap="MyDifEmp1">
select e.id id,e.last_name last_name,e.email email,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>
1.说明:当员工的实体类中含有另一个部门实体类信息时,需要在数据库层面进行多表联查,同时查询员工表与部门表,此时,查询出来的数据可以通过级联赋值的方式赋予员工实体类中的部门属性。
4.3 resultMap的进阶-关联查询二(封装单个对象)
<!--
使用association定义单个对象的封装规则
association可以指定联合的JavaBean对象
property:指定哪个属性是联合的对象
javaType:指定这个属性对象的类型(不能省略)
-->
<resultMap type="com.atguigu.mybatis.bean.Employee" id="MyDifEmp2">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="email" property="email"/>
<result column="gender" property="gender"/>
<association property="dept" javaType="com.atguigu.mybatis.bean.Department">
<id column="d_id" property="id"/>
<result column="dept_name" property="departmentName"/>
</association>
</resultMap>
<!-- public Employee getEmpAndDept(Integer id); -->
<select id="getEmpAndDept" resultMap="MyDifEmp2">
select e.id id,e.last_name last_name,e.email email,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>
1.说明::当员工的实体类中含有另一个部门实体类信息时,需要在数据库层面进行多表联查,同时查询员工表与部门表,此时,我们可以在<resultMap>
标签中定义<assocation>
标签,来对员工实体类中的部门属性进行赋值。
2.详解:<association>
可以指定联合的JavaBean对象。它包含两个属性,property
属性指定哪个属性是联合的对象。javaType
属性指定这个属性对象的类型(不能省略)。
4.4 resultMap的进阶-关联查询三(封装单个对象)
<!-- 使用association进行分步查询 -->
<!--
property:指定哪个属性是联合的对象
select:表明当前属性是调用select指定的方法查出的结果
column:指定将哪一列的值传给这个方法
流程:使用select指定的方法(传入column指定的这列参数的值)查出对象,并封装给property指定的属性
-->
<!-- 可以使用延迟加载(懒加载):按需加载
Employee==>Dept:
我们每次查询Employee对象的时候,都将部门信息一起查询出来
如果要求,部门信息在我们使用的时候再去查询,则需要在全局配置文件中增加两个配置
-->
<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 property="dept" select="com.atguigu.mybatis.dao.DepartmentMapper.getDeptById" column="d_id">
</association>
</resultMap>
<!-- public Employee getEmpByStep(Integer id); -->
<select id="getEmpByStep" resultMap="MyEmpByStep">
select * from tbl_employee where id = #{id}
</select>
<!-- 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>
1.说明:上述两种方法书写的SQL语句过于冗长,实际上,在Department表创建时,已经创建了一个根据部门id查询部门信息的方法和对应的SQL语句,所以,此时我们实际上只需要进行分步查询,先查询出员工信息,再查询出部门信息即可。
2.详解:<association>
标签中可以定义三个属性。property
属性指定哪个属性是联合的对象;select
属性表明当前属性是调用select指定的方法查出的结果;column
属性指定将哪一列的值传给这个方法。
3.流程:使用select指定的方法(传入column指定的这列参数的值)查出对象,并封装给property指定的属性。
4.延迟加载:我们每次查询Employee对象的时候,都会将部门信息一起查询出来。我们可以使用延迟加载,即要求,部门信息在我们使用的时候再去查询。此时,需要在全局配置文件中增加如下两个配置。
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
4.5 resultMap的进阶-关联查询四(封装多个对象)
<!--
resultMap的使用场景:查询部门的时候将部门对应的所有员工的信息也查询出来
-->
<!-- collection嵌套结果集的方式,定义关联的集合类型元素的封装规则 -->
<resultMap type="com.atguigu.mybatis.bean.Department" id="MyDept">
<id column="did" property="id"/>
<result column="dept_name" property="departmentName"/>
<!--
collection定义关联集合类型的属性的封装规则
property:要关联的属性
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>
1.说明:一个部门中可能存在多个员工,所以此时的部门实体中的员工属性实际上是一个集合,此时,我们需要将关联查询中查询出来的员工封装成一个存放员工实体类的列表。
2.详解: <collection>
标签可以定义关联集合类型的属性的封装规则,其内部包含两个标签,property
指明要关联的属性,ofType
指定集合里面元素的类型。
4.6 resultMap的进阶-关联查询五(封装多个对象)
<!-- 使用collection进行分步查询 -->
<resultMap type="com.atguigu.mybatis.bean.Department" id="MyDeptStep">
<id column="did" property="id"/>
<result column="dept_name" property="departmentName"/>
<collection property="emps" select="com.atguigu.mybatis.dao.EmployeeMapperPlus.getEmpsByDeptId"
column="id"></collection>
</resultMap>
<!-- public Department getDeptByStep(Integer id); -->
<select id="getDeptByStep" resultMap="MyDeptStep">
select id, dept_name from tbl_dept where id = #{id}
</select>
<!-- public List<Employee> getEmpsByDeptId(Integer deptId); -->
<select id="getEmpsByDeptId" resultType="com.atguigu.mybatis.bean.Employee">
select * from tbl_employee where d_id = #{deptId}
</select>
1.说明:上述方法仍然存在书写的SQL语句过于冗长的问题,因此,为了简化开发,可以采用分步查询的方式。
2.详解:<collection>
标签property
属性指定哪个属性是联合的对象;select
属性表明当前属性是调用select指定的方法查出的结果;column
属性指定将哪一列的值传给这个方法。
3.延迟加载:同样的,依然可以通过在全局配置文件中增加如下两个配置开启延迟加载。
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
4.7 association和collection的扩展小知识
1.column
属性的多值传递:将多列的值封装为map传递,例如column=“{key1=column1, key2=column2}”。
2.fetchType
属性控制延迟加载:默认值为lazy可选择lazy(延迟)和eager(立即)。
4.8 鉴别器的使用
<!--
鉴别器 discriminator
MyBatis可以使用discriminator判断某列的值,然后根据某列的值改变封装行为封装Employee
例如:如果查出的是女生,则把部门信息查出来,如果查出的是男生,则把last_name这一列的值赋值给email
-->
<resultMap type="com.atguigu.mybatis.bean.Employee" id="MyEmpByStep1">
<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>
<!-- 男生 -->
<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>
<!-- public Employee getEmpByStep(Integer id); -->
<select id="getEmpByStep" resultMap="MyEmpByStep1">
select * from tbl_employee where id = #{id}
</select>
1.说明:有时,我们查询的信息需要根据某列的值改变封装行为,鉴别器就可以实现这个功能。
2.详解:<discriminator>
标签中,有两个属性,column
用于指明进行判定的列名 ;javaType
表示列值对应的java类型。在该标签中,可以定义<case >
标签,该标签value
属性指明了<discriminator>
标签中column
的值为该值时,要进行的操作。<resultType>
属性与该<select>
标签的<resultType>
一致即可。
3.注意:<case>
标签中如果指定了<discriminator>
标签外的属性,则按照<case>
中的规则进行封装,否则,按照<discriminator>
标签外的规则进行封装。
5. 参数处理
5.1 单个参数
- MyBatis不会做特殊处理。
- 取值方式:通过
#{参数名}
取出参数值,但其实此时大括号里也不一定要写参数名,因为只有一个参数。<!-- public Employee getEmpById(Integer id); --> <select id="getEmpById" resultType="com.atguigu.mybatis.bean.Employee" databaseId="mysql"> select * from tbl_employee where id = #{id} </select>
5.2 多个参数
- MyBatis要做特殊处理。多个参数会被封装成一个map,其中key为param1、param2…paramN;value为传入的参数值,
#{}
就是从map中获取指定的key值。 - 取值方式一:利用封装好的key或者下标取值,即
#{paramN}
或者#{0}
。<!-- public Employee getEmpByIdAndLastName(Integer id, String lastName); --> <select id="getEmpByIdAndLastName" resultType="com.atguigu.mybatis.bean.Employee"> <!-- select * from tbl_employee where id = #{param1} and last_name = #{param2} --> select * from tbl_employee where id = #{0} and last_name = #{1} </select>
- 取值方式二:命名参数时就利用注解
@Param
明确指定封装参数时map的key。此时,可以除了使用方式一还可以使用#{参数名}
的方式取值。<!-- public Employee getEmpByIdAndLastName(@param("id")Integer id, @param("lastName")String lastName);; --> <select id="getEmpByIdAndLastName" resultType="com.atguigu.mybatis.bean.Employee"> select * from tbl_employee where id = #{id} and last_name = #{lastName} </select>
5.3 POJO
- 如果多个参数正好是我们业务逻辑的数据模型,我们就可以在参数中直接传入POJO对象。此时可以通过
#{属性名}
来取出传入的POJO属性。
5.4 Map
- 如果多个参数不是业务模型中的数据,没有对应的POJO,不经常使用,为了方便,我们也可以传入map,然后通过
#{key}
来取出map中对应的值。<!-- public Employee getEmpByMap(Map<String,Object> map); --> <select id="getEmpByMap" resultType="com.atguigu.mybatis.bean.Employee"> select * from ${tableName} where id = #{id} and last_name = #{lastName} </select>
5.5 TO
- 如果多个参数不是业务模型中的数据,但是经常要使用,推荐来编写一个TO(Transfer Object)数据传输对象。
5.6 深入理解
public Employee getEmp(@Param("id")Integer id,String lastName)
取值:id==>#{id/param1} lastName==>#{param2}
public Employee getEmp(Integer id, @Param("e")Employee emp)
取值:id==>#{param1} lastName==>#{param2.lastName/e.lastName}
public Employee getEmpById(List<Integer> ids);
取值:取出第一个id的值#{list[0])
特别注意:如果是Collection(List、Set)类型或者数组类型,也会特殊处理,将传入的list或者数组封装在map中。此时的map中的key为集合类的小写,即如果是Collection则为collection,如果是List则为list,如果是Array则为array。
5.7 #{}和${}的异同
select * from tbl_employee where id = ${id} and last_name = #{lastName}
select * from tbl_employee where id = 1 and last_name = ?
5.7.1 相同点
- 可以获取map中的值或者POJO对象属性的值。
5.7.2 不同点
- #{}:是以预编译的形式,将参数设置到sql语句中,PreparedStatement;防止sql注入。
- ${}:取出的值直接拼装到sql语句中;会有安全问题。
5.7.3 使用场景
select * from ${year}_salary where xxx;
select * from tbl_employee order by ${f_name} ${order}
<!-- public Employee getEmpByMap(Map<String,Object> map); -->
<select id="getEmpByMap" resultType="com.atguigu.mybatis.bean.Employee">
select * from ${tableName} where id = #{id} and last_name = #{lastName}
</select>
- 大多数情况下,我们取参数的值都应该去使用#{}
- 原生jdbc不支持占位符的地方我们就可以使用${}进行取值,比如传入的参数是字段的一部分、分表、排序,等等。