文章目录
Mybatis 映射文件
映射文件指导着 MyBatis 如何进行数据库增删改查,有着非常重要的意义。
- cache – 该命名空间的缓存配置。
- cache-ref – 引用其它命名空间的缓存配置。
- resultMap – 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。
parameterMap – 老式风格的参数映射。此元素已被废弃,并可能在将来被移除!请使用行内参数映射。文档中不会介绍此元素。- sql – 可被其它语句引用的可重用语句块。
- insert – 映射插入语句。
- update – 映射更新语句。
- delete – 映射删除语句。
- select – 映射查询语句。
1. insert、update、delete元素
<!-- public void addEmp(Employee employee); -->
<!-- parameterType:参数类型,可以省略。-->
<insert id="addEmp" parameterType="com.ginger.mybatis.bean.Employee">
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>
<!-- public void deleteEmpById(Integer id); -->
<delete id="deleteEmpById">
delete from employee where id=#{id}
</delete>
Mybatis 允许增删改直接定义以下类型返回值 Integer、Long、Boolean、void。
/**
* 测试增删改
* 1、mybatis允许增删改直接定义以下类型返回值
* Integer、Long、Boolean、void
* 2、我们需要手动提交数据
* sqlSessionFactory.openSession();===》手动提交
* sqlSessionFactory.openSession(true);===》自动提交
* @throws IOException
*/
@Test
public void test03() throws IOException{
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
//1、获取到的SqlSession不会自动提交数据
SqlSession openSession = sqlSessionFactory.openSession();
try{
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
//测试添加
Employee employee = new Employee(1, "小强","xiaoqiang@qq.com", "1");
mapper.addEmp(employee);
System.out.println(employee.getId());
//测试修改
//Employee employee = new Employee(1, "Tom", "jerry@jerry.com", "0");
//boolean updateEmp = mapper.updateEmp(employee);
//System.out.println(updateEmp);
//测试删除
//mapper.deleteEmpById(2);
//2、手动提交数据
openSession.commit();
}finally{
openSession.close();
}
}
1.1 主键生成方式
若数据库支持自动生成主键的字段(比如 MySQL 和 SQL Server),则可以设置 useGeneratedKeys=”true”,然后再把 keyProperty 设置到目标属性上。
<!-- public void addEmp(Employee employee); -->
<!-- parameterType:参数类型,可以省略。
获取自增主键的值:
mysql支持自增主键,自增主键值的获取,mybatis也是利用statement.getGenreatedKeys()。
useGeneratedKeys="true":使用自增主键获取主键值策略。
keyProperty:指定对应的主键属性,也就是mybatis获取到主键值以后,将这个值封装给javaBean的哪个属性。
-->
<insert id="addEmp" parameterType="com.ginger.mybatis.bean.Employee"
useGeneratedKeys="true" keyProperty="id" databaseId="mysql">
insert into tbl_employee(last_name,email,gender)
values(#{lastName},#{email},#{gender})
</insert>
而对于不支持自增型主键的数据库(例如 Oracle),则可以使用 selectKey 子元素:selectKey 元素将会首先运行,id 会被设置,然后插入语句会被调用。
<!--
获取非自增主键的值:
Oracle不支持自增,Oracle使用序列来模拟自增。
每次插入的数据的主键是从序列中拿到的值;如何获取到这个值。
-->
<insert id="addEmp" databaseId="oracle">
<!--
keyProperty:查出的主键值封装给javaBean的哪个属性。
order="BEFORE":当前sql在插入sql之前运行。
AFTER:当前sql在插入sql之后运行。
resultType:查出的数据的返回值类型。
BEFORE运行顺序:
先运行selectKey查询id的sql,查出id值封装给javaBean的id属性。
在运行插入的sql,就可以取出id属性对应的值。
AFTER运行顺序:这种方式可能会有点问题,如果插入的是多条最后一个会把前面的覆盖掉,还是使用BEFORE更好。
先运行插入的sql(从序列中取出新值作为id)。
再运行selectKey查询id的sql。
-->
<selectKey keyProperty="id" order="BEFORE" resultType="Integer">
<!-- 编写查询主键的sql语句 -->
<!-- BEFORE-->
select EMPLOYEES_SEQ.nextval from dual
<!-- AFTER:
select EMPLOYEES_SEQ.currval from dual -->
</selectKey>
<!-- 插入时的主键是从序列中拿到的 -->
<!-- BEFORE: -->
insert into employees(EMPLOYEE_ID,LAST_NAME,EMAIL)
values(#{id},#{lastName},#{email})
<!-- AFTER:
insert into employees(EMPLOYEE_ID,LAST_NAME,EMAIL)
values(employees_seq.nextval,#{lastName},#{email}) -->
</insert>
1.1.1 selectKey
2. 参数(Parameters)传递
2.1 单个参数
单个参数:可以接受基本类型,对象类型,集合类型的值,这种情况 MyBatis 可直接使用这个参数,不需要经过任何处理。
单个参数:Mybatis 不会做特殊处理,#{参数名/任意名}:取出参数值。
<select id="getEmpById" resultType="com.ginger.mybatis.bean.Employee" databaseId="mysql">
<!--select * from employee where id = #{id}-->
select * from employee where id = #{suibian}
</select>
<select id="getEmpById" resultType="com.ginger.mybatis.bean.Employee" databaseId="mysql">
<!--select * from employee where id = #{id}-->
select * from employee where id = #{suibian}
</select>
//接口
public interface EmployeeMapper {
public Employee getEmpById(Integer id);
}
//测试
@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(employee);
} finally {
openSession.close();
}
}
2.2 多个参数
多个参数:任意多个参数,都会被 MyBatis 重新包装成一个Map传入。Map 的 key 是 param1,param2或者0,1 … 值就是参数的值。
多个参数:mybatis会做特殊处理。
多个参数会被封装成一个 map。
- key:param1…paramN,或者参数的索引也可以。
- value:传入的参数值。
- #{}就是从 map 中获取指定的key的值。
2.2.1 多个参数使用参数名获取值(会报错)
<!-- public Employee getEmpByIdAndLastName(Integer id,String lastName);-->
<select id="getEmpByIdAndLastName" resultType="com.ginger.mybatis.bean.Employee">
select * from employee where id = #{id} and last_name=#{lastName}
</select>
// 接口
public interface EmployeeMapper {
public Employee getEmpByIdAndLastName(Integer id, String lastName);
}
// 测试
@Test
public void test02() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
// 会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee employee = mapper.getEmpByIdAndLastName(1, "小明");
System.out.println(employee);
} finally {
openSession.close();
}
}
2.2.2 使用param1和param2或者0和1获取值
<!-- public Employee getEmpByIdAndLastName(Integer id,String lastName); -->
<select id="getEmpByIdAndLastName" resultType="com.ginger.mybatis.bean.Employee">
select * from employee where id = #{param1} and lastname=#{param2}
</select>
或者
<!-- public Employee getEmpByIdAndLastName(Integer id,String lastName);-->
<select id="getEmpByIdAndLastName" resultType="com.gigner.mybatis.bean.Employee">
select * from employee where id = #{0} and lastname=#{1}
</select>
// 接口
public interface EmployeeMapper {
public Employee getEmpByIdAndLastName( Integer id, String lastName);
}
// 测试
@Test
public void test02() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee employee = mapper.getEmpByIdAndLastName(1, "小明");
System.out.println(employee);
} finally {
openSession.close();
}
}
2.3 命名参数@Param
命名参数:为参数使用 @Param 起一个名字,MyBatis 就会将这些参数封装进 map 中, key 就是我们自己指定的名字。
命名参数:明确指定封装参数时 map 的 key,@Param(“id”)。
多个参数会被封装成 一个map。
- key:使用@Param注解指定的值。
- value:参数值。
- #{指定的key}取出对应的参数值。
<!-- public Employee getEmpByIdAndLastName(Integer id,String lastName); -->
<select id="getEmpByIdAndLastName" resultType="com.ginger.mybatis.bean.Employee">
select * from employee where id = #{id} and lastname=#{lastName}
</select>
// 接口
public Employee getEmpByIdAndLastName(@Param("id") Integer id, @Param("lastName") String lastName);
// 测试
@Test
public void test02() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
// 会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee employee = mapper.getEmpByIdAndLastName(2, "小王");
System.out.println(employee);
} finally {
openSession.close();
}
}
2.4 POJO
POJO:当这些参数属于我们业务POJO时,我们直接传递POJO。
POJO:如果多个参数正好是我们业务逻辑的数据模型,我们就可以直接传入pojo。
#{属性名}:取出传入的pojo的属性值。
<!--public Employee getEmpByEmployee(Employee employee);-->
<select id="getEmpByEmployee" resultType="com.ginger.mybatis.bean.Employee">
select * from employee where id = #{id} and lastname=#{lastName}
</select>
// 接口
public Employee getEmpByEmployee(Employee employee);
// 测试
@Test
public void test03() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
// 会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Employee employee = new Employee();
employee.setId(2);
employee.setLastName("小王");
Employee result = mapper.getEmpByEmployee(employee);
System.out.println(result);
} finally {
openSession.close();
}
}
2.5 Map
Map:我们也可以封装多个参数为map,直接传递。
Map:如果多个参数不是业务模型中的数据,没有对应的pojo,不经常使用,为了方便,我们也可以传入map。
#{key}:取出map中对应的值。
<!-- public Employee getEmpByMap(Map<String, Object> map); -->
<select id="getEmpByMap" resultType="com.ginger.mybatis.bean.Employee">
select * from employee where id=#{id} and lastname=#{lastName}
</select>
// 接口
public Employee getEmpByMap(Map<String, Object> map);
// 测试
@Test
public void test04() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
// 会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Map<String,Object> map = new HashMap<>();
map.put("id",3);
map.put("lastName", "小李");
Employee employee = mapper.getEmpByMap(map);
System.out.println(employee);
} finally {
openSession.close();
}
}
2.6 TO
如果多个参数不是业务模型中的数据,但是经常要使用,推荐来编写一个TO(Transfer Object)数据传输对象。
public Page{
int index;
int size;
}
2.7 参数传递扩展
// 传递多个参数,第一个参数指定了map的键值,可以使用#{id}或者#{param1}取值,第二个参数没有指定map键值,就只能#{param2}取值。
// 取值:id==>#{id/param1} lastName==>#{param2}
public Employee getEmp(@Param("id")Integer id,String lastName);
// 传递多个参数,第一个参数没有指定map键值就只能使用#{param1}取值,第二个参数指定了map键值,可以使用#{param2.lastName}
// 或者#{emp.lastName}取值。
public Employee getEmp(Integer id,@Param("emp")Employee emp);
// 取值:id==>#{param1} lastName===>#{param2.lastName/emp.lastName}
/*
特别注意:如果是参数是Collection(List、Set)类型或者是数组,也会特殊处理,也是把传入的list或者数组封装在map中。
key:Collection(传入List或者Set键值都是collection),如果是List键值还可以使用这个key(list)数组(array)。
*/
public Employee getEmpById(Collection<Integer> collection);
//取值:取出第一个id的值: #{collection[0]}
public Employee getEmpById(List<Integer> ids);
//取值:取出第一个id的值: #{list[0]}
public Employee getEmpById(Integer[] ids);
//取值:取出第一个id的值: #{array[0]}
3. 参数处理
3.1 结合源码 Mybatis 怎么处理参数
总结:参数多时会封装map,为了不混乱,我们可以使用 @Param 来指定封装时使用的key。
#{key}就可以取出map中的值;
(@Param("id")Integer id,@Param("lastName")String lastName);
ParamNameResolver解析参数封装map的。
//1、names:{0=id, 1=lastName};构造器的时候就确定好了
确定流程:
1.获取每个标了param注解的参数的 @Param 的值:id,lastName; 赋值给name;
2.每次解析一个参数给map中保存信息:(key:参数索引,value:name的值)
name的值:
标注了param注解:注解的值
没有标注:
1.全局配置:useActualParamName(jdk1.8):name=参数名
2.name=map.size();相当于当前元素的索引
{0=id, 1=lastName,2=2}
args【1,"Tom",'hello'】:
public Object getNamedParams(Object[] args) {
final int paramCount = names.size();
//1、参数为null直接返回
if (args == null || paramCount == 0) {
return null;
//2、如果只有一个元素,并且没有Param注解;args[0]:单个参数直接返回
} else if (!hasParamAnnotation && paramCount == 1) {
return args[names.firstKey()];
//3、多个元素或者有Param标注
} else {
final Map<String, Object> param = new ParamMap<Object>();
int i = 0;
//4、遍历names集合;{0=id, 1=lastName,2=2}
for (Map.Entry<Integer, String> entry : names.entrySet()) {
//names集合的value作为key; names集合的key又作为取值的参考args[0]:args【1,"Tom"】:
//eg:{id=args[0]:1,lastName=args[1]:Tom,2=args[2]}
param.put(entry.getValue(), args[entry.getKey()]);
// add generic param names (param1, param2, ...)param
//额外的将每一个参数也保存到map中,使用新的key:param1...paramN
//效果:有Param注解可以#{指定的key},或者#{param1}
final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
// ensure not to overwrite parameter named with @Param
if (!names.containsValue(genericParamName)) {
param.put(genericParamName, args[entry.getKey()]);
}
i++;
}
return param;
}
}
}
3.2 #{} 和 ${}参数值的获取
#{}:可以获取map中的值或者pojo对象属性的值。
${}:可以获取map中的值或者pojo对象属性的值。
#{}和${}区别:
select * from employee where id=${id} and lastname=#{lastName}
Preparing: select * from employee where id=3 and lastname=?
区别:
#{}:是以预编译的形式,将参数设置到sql语句中,PreparedStatement,防止sql注入。
${}:取出的值直接拼装在sql语句中,会有安全问题。
大多情况下,我们去参数的值都应该去使用#{}。
原生jdbc不支持占位符的地方我们就可以使用${}进行取值。
比如分表、排序...按照年份分表拆分。
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.ginger.mybatis.bean.Employee">
select * from ${tableName} where id=${id} and lastname=#{lastName}
</select>
// 接口
public Employee getEmpByMap(Map<String, Object> map);
// 测试
@Test
public void test05() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Map<String,Object> map = new HashMap<>();
map.put("id",3);
map.put("lastName", "小李");
map.put("tableName","employee");
Employee employee = mapper.getEmpByMap(map);
System.out.println(employee);
} finally {
openSession.close();
}
}
3.3 #{}更多的用法
- javaType 通常可以从参数对象中来去确定。
- 如果 null 被当作值来传递,对于所有可能为空的列,jdbcType 需要被设置。
- 对于数值类型,还可以设置小数点后保留的位数。
- mode 属性允许指定 IN,OUT 或 INOUT 参数。如果参数为 OUT 或 INOUT,参数对象属性的真实值将会被改变,就像在获取输出参数时所期望的那样。
- 参数位置支持的属性。
javaType、jdbcType、mode、numericScale、resultMap、typeHandler、jdbcTypeName、expression。 - 实际上通常被设置的是。
可能为空的列名指定 jdbcType。
#{}:更丰富的用法:
规定参数的一些规则:
javaType、 jdbcType、 mode(存储过程)、 numericScale、resultMap、 typeHandler、 jdbcTypeName、 expression(未来准备支持的功能)。
jdbcType通常需要在某种特定的条件下被设置:
在我们数据为null的时候,有些数据库可能不能识别mybatis对null的默认处理,比如Oracle(报错)。
JdbcType OTHER:无效的类型,因为mybatis对所有的null都映射的是原生Jdbc的OTHER类型,oracle不能正确处理,mysql是可以处理的。
由于全局配置中:jdbcTypeForNull=OTHER,oracle不支持。有两种办法解决
1、#{email,jdbcType=NULL};
2、jdbcTypeForNull=NULL
<setting name="jdbcTypeForNull" value="NULL"/>
MyBatis 默认对的空值映射为 JDBC OTHER 类型,OTHER 类型 Mysql 是认识的,但是Oracle是不认识的所以会出现报错。
<!-- Oracle 数据库环境 -->
<insert id="addEmp" databaseId="oracle">
<selectKey keyProperty="id" order="BEFORE" resultType="Integer">
<!-- 编写查询主键的sql语句 -->
<!-- BEFORE-->
select EMPLOYEES_SEQ.nextval from dual
</selectKey>
<!-- 插入时的主键是从序列中拿到的 -->
<!-- BEFORE:-->
insert into employees(EMPLOYEE_ID,LAST_NAME,EMAIL)
values(#{id},#{lastName},#{email<!-- ,jdbcType=NULL -->})
</insert>
// 接口
public Long addEmp(Employee employee);
// 测试
@Test
public void test03() throws IOException{
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try{
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
//测试添加
//在邮箱位置插入一个空值
Employee employee = new Employee(null, "jerry3",null, "1");
mapper.addEmp(employee);
System.out.println(employee.getId());
openSession.commit();
}finally{
openSession.close();
}
}
4. select 元素
- select元素来定义查询操作。
- Id:唯一标识符。
用来引用这条语句,需要和接口的方法名一致。 - parameterType:参数类型。
可以不传,MyBatis 会根据 TypeHandler 自动推断。 - resultType:返回值类型。
别名或者全类名,如果返回的是集合,定义集合中元素的类型,不能和resultMap同时使用。
4.1 返回结果自动映射
全局setting设置
- autoMappingBehavior 默认是 PARTIAL,开启自动映射的功能,唯一的要求是列名和 javaBean 属性名一致。
- 如果 autoMappingBehavior 设置为null则会取消自动映射。
- 数据库字段命名规范,POJO属性符合驼峰命名法,如A_COLUMN,我们可以开启自动驼峰命名规则映射功能,mapUnderscoreToCamelCase=true。
4.2 resultType
4.2.1 resultType 返回值是 List
<!-- public List<Employee> getEmpsByLastNameLike(String lastName); -->
<!--resultType:如果返回的是一个集合,要写集合中元素的类型 -->
<select id="getEmpsByLastNameLike" resultType="com.ginger.mybatis.bean.Employee">
select * from tbl_employee where lastname like #{lastName}
</select>
// 接口
public List<Employee> getEmpsByLastNameLike(String lastName);
// 测试
@Test
public void test06() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
List<Employee> like = mapper.getEmpsByLastNameLike("%小%");
for (Employee employee : like) {
System.out.println(employee);
}
} finally {
openSession.close();
}
}
4.2.2 resultType 返回值是 Map
4.2.2.1 返回一条记录
<!--public Map<String, Object> getEmpByIdReturnMap(Integer id); -->
<select id="getEmpByIdReturnMap" resultType="map">
select * from employee where id=#{id}
</select>
// 接口
// 返回一条记录的map,key就是列名,值就是对应的值。
public Map<String, Object> getEmpByIdReturnMap(Integer id);
// 测试
@Test
public void test07() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Map<String, Object> map = mapper.getEmpByIdReturnMap(1);
System.out.println(map);
} finally {
openSession.close();
}
}
4.2.2.2 返回多条记录
<!--public Map<Integer, Employee> getEmpByLastNameLikeReturnMap(String lastName); -->
<select id="getEmpByLastNameLikeReturnMap" resultType="com.ginger.mybatis.bean.Employee">
select * from employee where lastname like #{lastName}
</select>
// 接口
// 多条记录封装一个 map:Map<String,Employee>:键是这条记录的主键,值是记录封装后的javaBean。
// @MapKey:告诉mybatis封装这个map的时候使用哪个属性作为map的key,要注意@MapKey指定的键类型要和返回值Map的键类型要一样,不然会报错。
@MapKey("lastName") // 如果返回值是map并且返回多条记录,要指定一下map的key值如果不指定也会报错。
public Map<String, Employee> getEmpByLastNameLikeReturnMap(String lastName);
// 测试
@Test
public void test08() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Map<String, Employee> map = mapper.getEmpByLastNameLikeReturnMap("%小%");
Set<String> keySet = map.keySet();
for (String key : keySet) {
System.out.println(key+"---"+map.get(key));
}
System.out.println("--------------");
System.out.println(map);
} finally {
openSession.close();
}
}
4.3 resultMap
自定义resultMap,实现高级结果集映射。
constructor:类在实例化时,用来注入结果到构造方法中。
idArg:ID 参数,标记结果作为 ID 可以帮助提高整体效能。
arg:注入到构造方法的一个普通结果。
id:一个 ID 结果,标记结果作为 ID 可以帮助提高整体效能。
result:注入到字段或 JavaBean 属性的普通结果。
association:一个复杂的类型关联,许多结果将包成这种类型。
嵌入结果映射:结果映射自身的关联,或者参考一个。
collection:复杂类型的集
嵌入结果映射:结果映射自身的集,或者参考一个。
discriminator:使用结果值来决定使用哪个结果映射。
case:基于某些值的结果映射
嵌入结果映射:这种情形结果也映射它本身,因此可以包含很多相同的元素,或者它可以参照一个外部的结果映射。
4.3.1 id & result
id 和 result 映射一个单独列的值到简单数据类型(字符串,整型,双精度浮点数,日期等)的属性或字段。
<!--
自定义某个javaBean的封装规则
type:自定义规则的Java类型。
id:唯一id方便引用。
-->
<resultMap id="MySimpleEmp" type="com.ginger.mybatis.bean.Employee">
<!--
指定主键列的封装规则。
id定义主键会底层有优化。
column:指定哪一列。
property:指定对应的javaBean属性。
-->
<id column="id" property="id"/>
<!-- 定义普通列封装规则,为了演示我将数据库中的lastname列改为last_name。-->
<result column="last_name" property="lastName"/>
<!-- 其他不指定的列会自动封装:我们只要写 resultMap 就把全部的映射规则都写上。 -->
<!-- 如果数据库中有些列和javaBean的属性能够对应上其实不用写映射规则的,但是建议只要写 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>
// 接口
public Employee getEmpById(Integer id);
// 测试
@Test
public void test09() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapperPlus mapper = openSession.getMapper(EmployeeMapperPlus.class);
Employee employee = mapper.getEmpById(1);
System.out.println(employee);
} finally {
openSession.close();
}
}
4.3.2 association
复杂对象映射。
POJO中的属性可能会是一个对象。
我们可以使用联合查询,并以级联属性的方式封装对象。
4.3.2.1 使用级联属性的方式封装对象
<!--
场景一:
查询Employee的同时查询员工对应的部门
Employee===>Department
一个员工有与之对应的部门信息;
id last_nam email gender depa_id did dept_name (private Department dept)
-->
<!--
联合查询:级联属性封装结果集。
这里有个点一定要注意 employee,deparment 这两张表的id名称都是一样的一定要区别开来(可能都是使用第一个id),不然会有问题。
-->
<resultMap type="com.ginger.mybatis.bean.Employee" id="MyDifEmp">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="email" property="email"/>
<result column="gender" property="gender"/>
<result column="did" property="dept.id"/>
<result column="depa_name" property="dept.departmentName"/>
</resultMap>
<!--public Employee getEmpAndDept(Integer id);-->
<select id="getEmpAndDept" resultMap="MyDifEmp">
SELECT
e.id,
e.last_name last_name,
e.email,
e.gender,
e.depa_id,
d.id did,
d.depa_name
FROM
employee e,
department d
WHERE d.id = e.depa_id
AND d.id = #{id}
</select>
// 接口
public Employee getEmpAndDept(Integer id);
// 测试
@Test
public void test10() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapperPlus mapper = openSession.getMapper(EmployeeMapperPlus.class);
Employee employee = mapper.getEmpAndDept(1);
System.out.println(employee);
} finally {
openSession.close();
}
}
4.3.2.2 association 标签定义对象的封装规则
<!--
使用association定义关联的单个对象的封装规则。
-->
<resultMap type="com.ginger.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:可以指定联合的javaBean对象
property="dept":指定哪个属性是联合的对象
javaType:指定这个属性对象的类型[不能省略]
-->
<association property="dept" javaType="com.ginger.mybatis.bean.Department">
<id column="did" property="id"></id>
<result column="depa_name" property="departmentName"></result>
</association>
</resultMap>
<!-- public Employee getEmpAndDept(Integer id);-->
<select id="getEmpAndDept" resultMap="MyDifEmp2">
SELECT
e.id,
e.last_name last_name,
e.email,
e.gender,
e.depa_id,
d.id did,
d.depa_name
FROM
employee e,
department d
WHERE d.id = e.depa_id
AND d.id = #{id}
</select>
// 接口
public Employee getEmpAndDept(Integer id);
// 测试
@Test
public void test10() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapperPlus mapper = openSession.getMapper(EmployeeMapperPlus.class);
Employee employee = mapper.getEmpAndDept(1);
System.out.println(employee);
} finally {
openSession.close();
}
}
4.3.2.3 association 分段查询
<!--EmployeeMapper.xml-->
<!--
使用association进行分步查询:
1、先按照员工id查询员工信息。
2、根据查询员工信息中的d_id值去部门表查出部门信息。
3、部门设置到员工中。
-->
<!-- id last_name email gender depa_id -->
<resultMap type="com.ginger.mybatis.bean.Employee" id="MyDifEmp3">
<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 column="did"
select="com.ginger.mybatis.dao.DepartmentMapper.getDeptById"
property="dept"></association>
</resultMap>
<!-- public Employee getEmpByIdStep(Integer id);-->
<select id="getEmpByIdStep" resultMap="MyDifEmp3">
select * from employee where id=#{id}
</select>
<!--DepartmentMapper.xml-->
<!--public Department getDeptById(Integer id); -->
<select id="getDeptById" resultType="com.ginger.mybatis.bean.Department">
select id,depa_name departmentName from department where id=#{id}
</select>
// EmployeeMapper接口
public Employee getEmpAndDept(Integer id);
// DepartmentMapper接口
public Department getDeptById(Integer id);
// 测试
@Test
public void test11() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapperPlus mapper = openSession.getMapper(EmployeeMapperPlus.class);
Employee employee = mapper.getEmpAndDept(3);
System.out.println(employee);
} finally {
openSession.close();
}
}
4.3.2.3 association 分段查询延迟加载
其实可以使用延迟加载(懒加载)(按需加载):
Employee===>Dept:
我们每次查询Employee对象的时候,都将一起查询出来。
部门信息在我们使用的时候再去查询。
分段查询的基础之上加上两个配置。
在 Mybatis 的主配置文件中,在 setttings 中加入开启延迟加载配置。
<settings>
<!--显示的指定每个我们需要更改的配置的值,即使他是默认的。防止版本更新带来的问题 -->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
旧版本的 MyBatis 需要额外的支持包。
- asm-3.3.1.jar
- cglib-2.2.2.jar
我还是用上面的测试类测试有延迟加载没有延迟加载到底是怎么样的。
// 测试
@Test
public void test11() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapperPlus mapper = openSession.getMapper(EmployeeMapperPlus.class);
Employee employee = mapper.getEmpAndDept(3);
} finally {
openSession.close();
}
}
4.3.3 collection
4.3.3.1 collection 标签定义对象的封装规则
<!--
场景二:
查询部门的时候将部门对应的所有员工信息也查询出来。
-->
<!--嵌套结果集的方式,使用collection标签定义关联的集合类型的属性封装规则-->
<resultMap type="com.ginger.mybatis.bean.Department" id="MyDept">
<id column="id" property="id"/>
<result column="depa_name" property="departmentName"/>
<!--
collection:定义关联集合类型的属性的封装规则
ofType:指定集合里面元素的类型
-->
<collection property="emps" ofType="com.ginger.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,
d.depa_name,
e.id eid,
e.last_name,
e.email,
e.gender,
e.depa_id
FROM
department d
LEFT JOIN employee e
ON d.id = e.depa_id
WHERE d.id = #{id}
</select>
// 接口
public Department getDeptByIdPlus(Integer id);
// 测试
@Test
public void test13() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
DepartmentMapper mapper = openSession.getMapper(DepartmentMapper.class);
Department department = mapper.getDeptByIdPlus(1);
System.out.println(department);
} finally {
openSession.close();
}
}
4.3.3.2 collection 分段查询
<!-- DepartmentMapper.xml -->
<!-- collection:分段查询 -->
<resultMap type="com.ginger.mybatis.bean.Department" id="MyDept2">
<id column="id" property="id"/>
<id column="depa_name" property="departmentName"/>
<collection property="emps"
select="com.ginger.mybatis.dao.EmployeeMapperPlus.getEmpsByDeptId"
column="id" ></collection>
</resultMap>
<!-- public Department getDeptByIdStep(Integer id); -->
<select id="getDeptByIdStep" resultMap="MyDept2">
select id,depa_name from department where id=#{id}
</select>
<!-- EmployeeMapperPlus.xml -->
<!-- public List<Employee> getEmpsByDeptId(Integer deptId); -->
<select id="getEmpsByDeptId" resultType="com.ginger.mybatis.bean.Employee">
select * from employee where depa_id=#{deptId}
</select>
// 接口
public Department getDeptByIdStep(Integer id);
// 接口
public List<Employee> getEmpsByDeptId(Integer deptId);
// 测试
@Test
public void test14() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
DepartmentMapper mapper = openSession.getMapper(DepartmentMapper.class);
Department department = mapper.getDeptByIdStep(1);
System.out.println(department);
} finally {
openSession.close();
}
}
4.3.3.3 collection分段查询延迟加载
在 Mybatis 的主配置文件中,在setttings中加入开启延迟加载配置。
<settings>
<!--显示的指定每个我们需要更改的配置的值,即使他是默认的。防止版本更新带来的问题 -->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
我还是用上面的测试类测试有延迟加载没有延迟加载到底是怎么样的。
@Test
public void test14() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
DepartmentMapper mapper = openSession.getMapper(DepartmentMapper.class);
Department department = mapper.getDeptByIdStep(1);
System.out.println(department.getDepartmentName());
System.out.println(department.getEmps());
} finally {
openSession.close();
}
}
4.3.3.4 collection扩展
- 分步查询的时候通过column指定,将对应的列的数据传递过去,我们有时需要传递多列数据。
- 使用{key1=column1,key2=column2…}的形式。
- association 或者 collection 标签的 fetchType=eager/lazy 可以覆盖全局的延迟加载策略,指定立即加载(eager)或者延迟加载(lazy)。
扩展:多列的值传递过去
将多列的值封装map传递。
column="{key1=column1,key2=column2}"
fetchType="lazy"(默认):表示使用延迟加载,在association,collection标签中都有这个属性,就算开启了全局延迟加载,在这两个标签上也可以开启和关闭。
- lazy:延迟
- eager:立即
<!-- collection:分段查询 -->
<resultMap type="com.ginger.mybatis.bean.Department" id="MyDept2">
<id column="id" property="id"/>
<id column="depa_name" property="departmentName"/>
<collection property="emps"
select="com.ginger.mybatis.dao.EmployeeMapperPlus.getEmpsByDeptId"
<!--
{deptId=id} 这样可以传递多个值,这个键要和EmployeeMapperPlus.xml的sql中的键值要对应上,不然获取不到值。
select * from employee where depa_id=#{deptId} 这个sql是EmployeeMapperPlus.xml文件中的。
-->
column="{deptId=id}" fetchType="lazy"></collection>
</resultMap>
<!-- public Department getDeptByIdStep(Integer id); -->
<select id="getDeptByIdStep" resultMap="MyDept2">
select id,depa_name from department where id=#{id}
</select>
// 测试
@Test
public void test14() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
DepartmentMapper mapper = openSession.getMapper(DepartmentMapper.class);
Department department = mapper.getDeptByIdStep(1);
System.out.println(department);
} finally {
openSession.close();
}
}
这样也是可行的。
4.3.4 discriminator
<!-- EmployeeMapperPlus.xml -->
<!--
<discriminator javaType=""></discriminator>
鉴别器:mybatis可以使用discriminator判断某列的值,然后根据某列的值改变封装行为。
封装Employee:
如果查出的是女生:就把部门信息查询出来,否则不查询。
如果是男生,把last_name这一列的值赋值给email。
-->
<resultMap id="MyDisEmp" type="com.ginger.mybatis.bean.Employee">
<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.ginger.mybatis.bean.Employee">
<association column="depa_id"
select="com.ginger.mybatis.dao.DepartmentMapper.getDeptById"
property="dept"
fetchType="eager">
</association>
</case>
<!--男生:如果是男生,把last_name这一列的值赋值给email。 -->
<case value="1" resultType="com.ginger.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 getEmpByIdStep(Integer id);-->
<select id="getEmpByIdStep" resultMap="MyDisEmp">
select * from employee where id=#{id}
</select>
<!-- DepartmentMapper.xml -->
<!-- collection:分段查询 -->
<resultMap type="com.ginger.mybatis.bean.Department" id="MyDept2">
<id column="id" property="id"/>
<id column="depa_name" property="departmentName"/>
<collection property="emps"
select="com.ginger.mybatis.dao.EmployeeMapperPlus.getEmpsByDeptId"
column="{deptId=id}" fetchType="lazy"></collection>
</resultMap>
<!-- public Department getDeptByIdStep(Integer id); -->
<select id="getDeptByIdStep" resultMap="MyDept2">
select id,depa_name from department where id=#{id}
</select>
// 接口
public Employee getEmpByIdStep(Integer id);
// 接口
public Department getDeptById(Integer id);
// 测试
@Test
public void test15() throws IOException {
// 1、获取sqlSessionFactory对象
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
// 2、获取sqlSession对象
SqlSession openSession = sqlSessionFactory.openSession();
try {
// 3、获取接口的实现类对象
//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
EmployeeMapperPlus mapper = openSession.getMapper(EmployeeMapperPlus.class);
Employee employee1 = mapper.getEmpByIdStep(1);
System.out.println(employee1);
System.out.println("----------------------");
Employee employee2 = mapper.getEmpByIdStep(4);
System.out.println(employee2);
} finally {
openSession.close();
}
}