MyBatis映射文件
本小记学习目标
1.MyBatis的核心配置文件(全局配置文件)的介绍
2.掌握MyBatis的SQL映射文件(重点掌握)
3.掌握MyBatis的级联查询实现方法
一、MyBatis核心配置文件
MyBatis核心配置文件,配置了很多影响MyBatis行为的信息,这些信息通常来说也只会配置在一个文件中,并且一般是不会去轻易改变的,当它与Spring整合后,这些配置信息会配置到Spring的配置文件中,在实际的开发过程中需要去编写或修改这个配置文件的情况不多。
MyBatis核心配置文件的模板如下:
<?
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
/>
<!-- 设置 -->
<
settings
>
<
setting
name=
""
value=
""
/>
</
settings
>
<!-- 类型命名(别名) -->
<
typeAliases
/>
<!-- 类型处理器 -->
<
typeHandlers
/>
<!-- 对象工厂 -->
<
objectFactory
type=
""
/>
<!-- 插件 -->
<
plugins
>
<
plugin
interceptor=
""
></
plugin
>
</
plugins
>
<!-- 配置环境 -->
<
environments
default=
""
>
<!-- 环境变量 -->
<
environment
id=
""
>
<!-- 事务管理器 -->
<
transactionManager
type=
""
/>
<!-- 数据源 -->
<
dataSource
type=
""
/>
</
environment
>
</
environments
>
<!-- 数据库厂商标识 -->
<
databaseIdProvider
type=
""
/>
<!-- 映射器,通知MyBatis去哪里去找映射文件 -->
<
mappers
>
<
mapper
resource=
"com/XXX/XXX/XXXMapper.xml"
/>
</
mappers
>
</
configuration
>
二、MyBatis映射文件
MyBatis映射器介绍
映射器,它是MyBatis中最为重要的组件,它是由接口加上Xml文件(sql映射文件)组成。MyBatis也可以由注解来完成,但是一般来说不常用注解做Sql映射
Sql映射文件的常用配置元素有如下一些:
select 查询语句 可以自定义参数、返回结果集……
insert 插入语句 执行后返回一个整数,表示插入的记录行数
update 更新语句 执行后返回一个整数,表示更新的记录行数
delete 删除语句 执行后返回一个整数,表示删除的记录行数
sql 定义一部分sql
resultMap 用来描述由数据库结果集来加载对象
select元素
<select>元素用来映射SQL的select语句。例如:
<!-- 根据id查询一条记录 -->
<
select
id=
"selectStudentById"
parameterType=
"Integer"
resultType=
"com.xiaoxie.pojo.Student"
>
select id,name,age from student where 1=1 and id=#{id}
</
select
>
在上面的配置中parameterType指定为Integer,表明参数是一个Integer类型
resultType指定为com.xiaoxie.pojo.Student,表明返回的是一个Student类的对象
sql语句中#{id},表示传入的参数是id属性的值
select元素的常用属性
id:它与Mapper的命名空间组合使用,是一个唯一的标识符,供MyBatis调用
parameterType:它表示传入sql语句的参数类型的全限定名或别名。它是一个可选属性,MyBatis可以推断出具体传入语句的参数
resultType:指定返回的类型(全限定名或别名)如果是集合类型,返回的是集合元素的类型,返回时可以使用resultType或resultMap之一
resultMap:它是映射集的引用,与<resultMap元素一起使用>,返回时可以使用reusltType或resultMap之一
flushCache:用于设置在调用sql语句后时否要求MyBatis清空之前查询的本地缓存和二级缓存,默认是false
useCache:启动二级缓存开关,默认是true
timeout:设置超时时间,单位是秒,如果超时则会抛出异常
fetchSize:获取记录的总条数设定
statementType:告诉MyBatis使用哪个JDBC的Statement工作,可取值:STATEMENT(Statement)、PREPARED(PreparedStatement)、CALLABLE(CallbleStatement)
resutlSetType:针对JDBC的ResultSet接口而言,它的值可以设置为FORWARD_ONLY(只可以向前访问)、SCROLL_SENSITIVE(双向滚动,但不及时更新)、SCROLL_INSENSITIVE(双向滚动,及时更新)
关于多个参数传递的问题
方式一:使用Map做为参数传递
在实际的应用开发过程中,查询sql语句通常需要多个查询参数(多个条件),这个时候可以在parameterType属性中指定为map
在Mapper中做如下查询定义
<!-- 测试多参数传递 -->
<
select
id=
"selectStudentByNameAndAge"
resultType=
"com.xiaoxie.pojo.Student"
parameterType=
"map"
>
select id,name,age from student
where 1=1
and name=#{name}
and age=#{age}
</
select
>
这里可以看到parameterType指定的为map
在对应的Dao接口中对以上标识定义接口方法与之对应
//测试多参数查询,条件:姓名、年龄
List<Student> selectStudentByNameAndAge(Map<String,Object>
param);
在测试类中新增如下方法进行测试
System.
out.println(
"测试多条件参数:");
//Map条件
Map<String,Object>
param =
new HashMap<>();
param.put(
"name",
"张飞");
param.put(
"age",26);
StudentDao
dao = (StudentDao)
context.getBean(
"studentDao");
List<Student>
stu =
dao.selectStudentByNameAndAge(
param);
for (Student
student :
stu) {
System.
out.println(
student);
}
从上面我们可以看到我们定义了一个map,map的key对应的就是查询语句中的参数名称
方式二:使用Java Bean来传递参数
对于参数复杂的情况下我们使用Map的方式传递参数是比较复杂的需要对参数进行初始化
使用JavaBean作为参数则是专门为参数建立一个JavaBean的类
在com.xiaoxie.dao.param包下建立一个基础的参数pojo类
package com.xiaoxie.dao.param;
public
class StudnetNameAndAgeParam {
private String
name;
private Integer
age;
public String getName() {
return
name;
}
public
void setName(String
name) {
this.
name =
name;
}
public Integer getAge() {
return
age;
}
public
void setAge(Integer
age) {
this.
age =
age;
}
}
在Mapper中做如下定义
<!-- 测试多参数的传递,这里使用JavaBean的方式传入多个参数 -->
<
select
id=
"selectStudentByNameAndAgeUseJavaBean"
resultType=
"com.xiaoxie.pojo.Student"
parameterType=
"com.xiaoxie.dao.param.StudnetNameAndAgeParam"
>
select id,name,age from student
where 1=1
and name=#{name}
and age=#{age}
</
select
>
在dao接口中定时对应的接口方法
//测试多参数查询,条件:姓名、年龄,作用JavaBean
List<Student> selectStudentByNameAndAgeUseJavaBean(StudnetNameAndAgeParam
param);
在测试类的main方法中做如下测试可以把建立的JavaBean对象作为参数传递进去
System.
out.println(
"使用JavaBean作为参数传递");
StudnetNameAndAgeParam
beanParam =
new StudnetNameAndAgeParam();
beanParam.setName(
"刘备");
beanParam.setAge(30);
stu =
dao.selectStudentByNameAndAgeUseJavaBean(
beanParam);
for (Student
student :
stu) {
System.
out.println(
student);
}
对于多参数的传递方式是使用Map还是使用JavaBean,这个可以根据实际的情况灵活确定,如果觉得参数过多则可以选择JavaBean这种方式
insert元素
<insert>元素用于映射插入语句,在MyBatis插入完成后会返回一个整数表示它执行影响的行数。它的属性与select元素的不只属性大部分都是一样的,有如下几个特有的属性
keyProperty:这个属性是把插入或更新操作时返回值赋给po类的某个属性上,通常来说会设置为主键对应的属性,如果是联合主键,可以用逗号把多个值隔开
keyColumn:这个属性是用来设置第几例为主键,当主键不是第一列的时候是需要设置的,如果是联合主键则可以使用多个值用逗号隔开
useGeneratedKeys:这个属性是MyBatis使用JDBC的getGeneratedKeys()方法获取数据库内部产生的主键,默认值是false
对于mysql、sqlServer数据库一般会采用自动递增值作为主键,在insert数据完成后,有时是需要刚和新增数据的主键值的,这个时候则可以在mapper中对于insert元素添加keyProperty和useGeneratedKeys属性
自动回填主键
在mapper中定义insert元素如下
<!-- 添加一条记录 -->
<
insert
id=
"addStudent"
parameterType=
"com.xiaoxie.pojo.Student"
keyProperty=
"id"
useGeneratedKeys=
"true"
>
insert into student(name,age) values(#{name},#{age})
</
insert
>
这里表示主键值是id属性
通过如下方式新增记录
Student
stu1 =
new Student(
"刘备",30);
int
rows =
studentDao.addStudent(
stu1);
System.
out.println(
"===============添加"+
rows +
"条记录==============");
System.
out.println(
"新增记录的主键为:" +
stu1.getId());
那么新增记录的主键会回写到stu1的id属性上
自主设定主键
有些数据库是不支持自增主键的这个时候则需要自主去设置主键,这个时候可以使用<selectKey>元素来实现
在Mapper中需要做如下定义
<
insert
id=
"addStudent"
parameterType=
"com.xiaoxie.pojo.Student"
>
<
selectKey
keyProperty=
"id"
resultType=
"Integer"
order=
"BEFORE"
>
select if(max(id) is null,1,max(id)+1) as mid from student
</
selectKey
>
insert into student(id,name,age) values(#{id},#{name},#{age})
</
insert
>
注:一般对于mySql这样的数据库是不可以这样操作的,因为原表的数据可能会被删除记录,这个时候max出来的主键是不正确的,非要使用则需要使用额外表来做记录主键当前的递增值
<selectKey>元素中的keyProperty属性表表示查询到的返回给到Po类的哪个属性,order表示在什么时机执行(可选值:BEFORE、AFTER)这里的之前和之后是相对于执行insert语句来说的。
update元素和delete元素
它们分别对应的是sql语句中的update和delete,其属性与insert、select属性差不多,它在执行后也会返回一个整数,表示影响到了数据库多少行记录
mapper中的定义可以为如下
<!-- 修改一条记录 -->
<
update
id=
"updateStudentById"
parameterType=
"com.xiaoxie.pojo.Student"
>
update student set name=#{name},age=#{age} where 1=1 and id=#{id}
</
update
>
<!-- 删除一条记录 -->
<
delete
id=
"deleteStudentById"
parameterType=
"Integer"
>
delete from student where id=#{id}
</
delete
>
sql元素
这个元素主要是用来自定义sql代码片段,这些sql片段可以在后续的配置中进行引用
如下示例:可以把需要使用到的列名用sql元素进行定义,在后续需要使用的地方进行引用即可
<!-- 使用
sql
标签自定义部分
sql
语句供其他配置中引用 -->
<
sql
id=
"studentColumns"
> id,name,age
</
sql
>
<
select
id=
"selectStudentByColumns"
resultType=
"com.xiaoxie.pojo.Student"
>
select
<
include
refid=
"studentColumns"
/> from student
</
select
>
resultMap元素
它是MyBatis中最为重要的元素,主要用来定义映射规则、级联的更新以及定义类型转化器……
<resultMap>元素的结构说明:
<resultMap type="" id="">
<constructor> <!-- 类在实例化的时候用来注入结果到构造方法 -->
<idArg/> <!-- ID参数,结果为ID -->
<arg/> <!-- 注入到构造方法的一个普通结果 -->
</constructor>
<id/> <!-- 用来表示哪个列是主键 -->
<result/> <!-- 注入到字段或JavaBean属性的普通结果 -->
<association property=""/> <!-- 用于一对一关联 -->
<collection property=""/> <!-- 用于一对多、多对多关联 -->
<discriminator javaType=""> <!-- 使用结果值来决定使用哪个结果映射 -->
<case value=""/> <!-- 基于某些值的结果映射 -->
</discriminator>
</resultMap>
简单说明:
resultMap的type属性表示需要的pojo,id属性则表示resultMap的唯一值
子元素<constructor>用来配置构造方法(在POJO中没有定义无参构造时使用)
子元素<id>表示哪个列为主键
子元素<result>表示POJO和数据表普通列的映射关系
子元素<association>、<collection>、<discriminator>用在级联的情况
使用Map来存储结果集
指的是在配置select元素时,指定resultType="map"
在Mapper中可以做如下定义
<!-- 使用Map来存储结果集 -->
<
select
id=
"selectStudentUseMap"
resultType=
"map"
>
select * from student
</
select
>
在Dao接口中新增对应的接口方法
//使用Map来存储查询的结果集
List<Map<String,Object>> selectStudentUseMap();
在测试类中新增如下代码进行测试
System.
out.println(
"使用Map来存储查询返回的结果集");
List<Map<String, Object>>
studentMap =
dao.selectStudentUseMap();
Set<Map.Entry<String, Object>>
entrySet =
null;
for (Map<String, Object>
map :
studentMap) {
//遍历map
entrySet =
map.entrySet();
for (Map.Entry<String, Object>
entry :
entrySet) {
System.
out.println(
entry.getKey() +
":" +
entry.getValue());
}
System.
out.println(
"------------");
}
这里我们通过测试的结果可以看到,当使用map做结果集的存储时,map中的key为字段名,value则是查询结果返回的对应字段的值,使用这种方式对于少量的记录(比如一条记录)很方便,但是可读性并不高。
使用POJO存储结果集
使用POJO进行结果集的存储有两个方面是比较好的
第一,可以使用resultType属性
第二,当有复杂的映射或级联,这个时候可以使用select元素的resultMap属性配置映射集合
在mapper中配置resultMap元素
<!-- resultMap元素进行映射 -->
<
resultMap
type=
"com.xiaoxie.pojo.Student"
id=
"studentResult"
>
<
id
property=
"id"
column=
"id"
/>
<!-- 指定主键 -->
<
result
property=
"name"
column=
"name"
/>
<
result
property=
"age"
column=
"age"
/>
</
resultMap
>
<
select
id=
"selectStudentUseResultMap"
resultMap=
"studentResult"
>
select * from student
</
select
>
在Dao接口中新增对应的接口方法
//使用resultMap进行结果集的映射
List<Student> selectStudentUseResultMap();
在测试类中新增如下代码进行测试
System.
out.println(
"使用resultMap进行结果集的映射");
List<Student>
resultMap =
dao.selectStudentUseResultMap();
for (Student
student :
resultMap) {
System.
out.println(
student);
}
级联查询
级联查询,它有三种:一对一级联,一对多级联,多对多级联
优点:可以方便地获取关联数据
缺点:过多的级联会增加数据库系统的复杂性,同时影响系统的性能
实际开发过程中需要根据实际的情况做取舍,一般不建议做数据库级别的级联,关联的关系放在程序逻辑中即可
何为级联查询?
当有一个表A,它其中有一个字段做外键引用了表B的主键,这个时候通过表A的外键把表B对应的记录查询出来,这个就是级联查询
一对一级联查询
MyBatis中对于一对一级联查询的处理方式是,通过<resultMap>元素的子元素<association>元素处理,在这个子元素中通常使用以下属性
property:指定映射到实体类的对象属性
column:指定表中对应的字段
javaType:指定映射到实体对象属性的类型
select:指定引入嵌套查询的子sql语句
在数据库中建立如下两张表
-- 身份证表
create table idcard(
id BIGINT(19) NOT NULL auto_increment COMMENT '主键ID',
code varchar(18) NOT NULL DEFAULT '' COMMENT '身份证号码',
PRIMARY key(id)
);
-- 身份信息表
CREATE TABLE `person` (
`id` bigint(19) NOT NULL AUTO_INCREMENT COMMENT '主键',
`name` varchar(20) DEFAULT '' COMMENT '姓名',
`age` int(11) NOT NULL DEFAULT '0' COMMENT '年龄',
`address` varchar(512) DEFAULT '' COMMENT '常住地址',
`idcard_id` bigint(19) DEFAULT NULL COMMENT 'idcard表的id',
PRIMARY KEY (`id`),
KEY `idcard` (`idcard_id`),
CONSTRAINT `idcard` FOREIGN KEY (`idcard_id`) REFERENCES `idcard` (`id`)
);
新增POJO实体类:IdCard,Person
IdCard:
package com.xiaoxie.pojo;
public
class IdCard {
private Integer
id;
private String
code;
/*Setter与Getter*/
public Integer getId() {
return
id;
}
public
void setId(Integer
id) {
this.
id =
id;
}
public String getCode() {
return
code;
}
public
void setCode(String
code) {
this.
code =
code;
}
@Override
public String toString() {
return
"idCard[id=+"+
id+
",code="+
code+
"]";
}
}
Person:
package com.xiaoxie.pojo;
public
class Person {
private Integer
id;
private String
name;
private Integer
age;
private String
address;
private IdCard
idcard;
//身份证信息
public Integer getId() {
return
id;
}
public
void setId(Integer
id) {
this.
id =
id;
}
public String getName() {
return
name;
}
public
void setName(String
name) {
this.
name =
name;
}
public Integer getAge() {
return
age;
}
public
void setAge(Integer
age) {
this.
age =
age;
}
public String getAddress() {
return
address;
}
public
void setAddress(String
address) {
this.
address =
address;
}
public IdCard getIdcard() {
return
idcard;
}
public
void setIdcard(IdCard
idcard) {
this.
idcard =
idcard;
}
@Override
public String toString() {
return
"Person[id=+"+
id+
",name="+
name+
",age="+
age+
",address="+
address+
",idcard="+
idcard+
"]";
}
}
新增Mapper映射文件
IdCardMapper:
<?
xml
version=
"1.0"
encoding=
"UTF-8"
?>
<!
DOCTYPE
mapper
<
mapper
namespace=
"com.xiaoxie.dao.IdCardDao"
>
<
select
id=
"selectIdCardById"
parameterType=
"Integer"
resultType=
"com.xiaoxie.pojo.IdCard"
>
select * from
idcard where id = #{id}
</
select
>
</
mapper
>
PersonMapper:
<?
xml
version=
"1.0"
encoding=
"UTF-8"
?>
<!
DOCTYPE
mapper
<
mapper
namespace=
"com.xiaoxie.dao.PersonDao"
>
<!-- 一对一级联,执行两个
sql
进行查询 -->
<
resultMap
type=
"com.xiaoxie.pojo.Person"
id=
"idCardAdnPerson_1"
>
<
id
property=
"id"
column=
"id"
/>
<!-- 指定ID主键列 -->
<!-- 普通列的映射关系 -->
<
result
property=
"name"
column=
"name"
/>
<
result
property=
"age"
column=
"age"
/>
<
result
property=
"address"
column=
"address"
/>
<!-- 一对一的级联查询 -->
<
association
property=
"idcard"
column=
"idcard_id"
javaType=
"com.xiaoxie.pojo.IdCard"
select=
"com.xiaoxie.dao.IdCardDao.selectIdCardById"
/>
</
resultMap
>
<
select
id=
"selectPersonById_1"
parameterType=
"Integer"
resultMap=
"idCardAdnPerson_1"
>
select * from person where id=#{id}
</
select
>
<!-- 一对一级联,执行一个
sql
语句 -->
<
resultMap
type=
"com.xiaoxie.pojo.Person"
id=
"idCardAdnPerson_2"
>
<
id
property=
"id"
column=
"id"
/>
<!-- 指定主键列 -->
<!-- 普通列的映射关系 -->
<
result
property=
"name"
column=
"name"
/>
<
result
property=
"age"
column=
"age"
/>
<
result
property=
"address"
column=
"address"
/>
<!-- 一对一的级联查询 -->
<
association
property=
"idcard"
javaType=
"com.xiaoxie.pojo.IdCard"
>
<
id
property=
"id"
column=
"idcard_id"
/>
<
result
property=
"code"
column=
"code"
/>
</
association
>
</
resultMap
>
<
select
id=
"selectPersonById_2"
parameterType=
"Integer"
resultMap=
"idCardAdnPerson_2"
>
select p.*,c.code from person p left join
idcard c on p.idcard_id = c.id
where p.id=#{id}
</
select
>
<!-- 一对一级联,根据id查询个人信息,连接查询(使用
pojo
存储查询结结果) -->
<
select
id=
"selectPersonById_3"
parameterType=
"Integer"
resultType=
"com.xiaoxie.dao.result.IdCardAdnPerson"
>
select p.*,c.code from person p left join
idcard c on p.idcard_id = c.id
where p.id=#{id}
</
select
>
</
mapper
>
对应新增Dao接口
idCardDao:
package com.xiaoxie.dao;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import com.xiaoxie.pojo.IdCard;
@Repository(
"idCardDao")
@Mapper
public
interface IdCardDao {
IdCard selectIdCardById(Integer
id);
}
PersonDao:
package com.xiaoxie.dao;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import com.xiaoxie.dao.result.IdCardAdnPerson;
import com.xiaoxie.pojo.Person;
@Repository(
"personDao")
@Mapper
public
interface PersonDao {
Person selectPersonById_1(Integer
id);
Person selectPersonById_2(Integer
id);
IdCardAdnPerson selectPersonById_3(Integer
id);
}
新增实体类来保存MyBatis查询返回的结果
IdCardAdnPerson:
package com.xiaoxie.dao.result;
public
class IdCardAdnPerson {
private Integer
id;
private String
name;
private Integer
age;
private String
address;
private String
code;
public Integer getId() {
return
id;
}
public
void setId(Integer
id) {
this.
id =
id;
}
public String getName() {
return
name;
}
public
void setName(String
name) {
this.
name =
name;
}
public Integer getAge() {
return
age;
}
public
void setAge(Integer
age) {
this.
age =
age;
}
public String getAddress() {
return
address;
}
public
void setAddress(String
address) {
this.
address =
address;
}
public String getCode() {
return
code;
}
public
void setCode(String
code) {
this.
code =
code;
}
@Override
public String toString() {
return
"IdCardAdnPerson[id=+"+
id+
",name="+
name+
",age="+
age+
",address="+
address+
",code="+
code+
"]";
}
}
在MyBatis的核心配置文件中把Mapper的映射添加进去
<?
xml
version=
"1.0"
encoding=
"UTF-8"
?>
<!
DOCTYPE
configuration
PUBLIC
"-//
mybatis.org//DTD
Config 3.0//EN"
"
http://mybatis.org/dtd/mybatis-3-config.dtd
"
>
<
configuration
>
<!-- 在MyBatis进行嵌套查询时,使用延迟加载可以提高一定的性能所以在这里可把把这个配置打开 -->
<
settings
>
<!-- 延迟加载 -->
<
setting
name=
"lazyLoadingEnabled"
value=
"true"
/>
<!-- 按需加载 -->
<
setting
name=
"aggressiveLazyLoading"
value=
"false"
/>
</
settings
>
<!-- 查找映射文件 -->
<
mappers
>
<
mapper
resource=
"com/xiaoxie/dao/mybatis/StudentMapper.xml"
/>
<
mapper
resource=
"com/xiaoxie/dao/mybatis/IdCardMapper.xml"
/>
<
mapper
resource=
"com/xiaoxie/dao/mybatis/PersonMapper.xml"
/>
</
mappers
>
</
configuration
>
新增Controller类
package com.xiaoxie.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import com.xiaoxie.dao.PersonDao;
import com.xiaoxie.dao.result.IdCardAdnPerson;
import com.xiaoxie.pojo.Person;
@Transactional
@Controller(
"personController1")
public
class PersonController1 {
@Autowired
private PersonDao
personDao;
public
void test() {
Person
P1 =
personDao.selectPersonById_1(1);
System.
out.println(
P1);
System.
out.println(
"----------------------");
Person
p2 =
personDao.selectPersonById_2(1);
System.
out.println(
p2);
System.
out.println(
"-----------------------");
IdCardAdnPerson
p3 =
personDao.selectPersonById_3(1);
System.
out.println(
p3);
}
}
在测试类中调用
ApplicationContext
context =
new ClassPathXmlApplicationContext(
"spring-config.xml");
PersonController1
personController1 = (PersonController1)
context.getBean(
"personController1");
personController1.test();
注意:上述的例子中使用了数据库的外键使用数据库的两个表有了级联关系,实际上在数据库中没有这种强制的级联关系,只要逻辑上是这样的也是可以使用上面的这种方式做级联查询的(实际应用中应该这种方式是更常见的,实际上这样设计会更合理些)
一对多级联查询
一对多级联是指的一个实体表A的记录对应关联实体表B中的多条记录,比如一个用户购物时会产生多个订单,那么用户与订单是一对多的关系。
新增两个数据库表
-- user表
CREATE TABLE `user` (
`id` bigint(19) NOT NULL AUTO_INCREMENT COMMENT '主键',
`user_code` varchar(50) NOT NULL DEFAULT '' COMMENT '用户编码',
`user_name` varchar(50) NOT NULL DEFAULT '' COMMENT '用户名称',
PRIMARY KEY (`id`)
)
-- order表
CREATE TABLE `order` (
`id` bigint(19) NOT NULL AUTO_INCREMENT COMMENT '主键',
`order_no` varchar(25) NOT NULL DEFAULT '' COMMENT '订单编号',
`user_code` varchar(50) NOT NULL DEFAULT '' COMMENT '用户编码',
PRIMARY KEY (`id`)
)
新增POJO实体类
Order:
package com.xiaoxie.pojo;
public
class Order {
private Integer
id;
private String
order_no;
private String
user_code;
public Integer getId() {
return
id;
}
public
void setId(Integer
id) {
this.
id =
id;
}
public String getOrder_no() {
return
order_no;
}
public
void setOrder_no(String
order_no) {
this.
order_no =
order_no;
}
public String getUser_code() {
return
user_code;
}
public
void setUser_code(String
user_code) {
this.
user_code =
user_code;
}
@Override
public String toString() {
return
"Order[id=+"+
id+
",order_no="+
order_no+
",user_code="+
user_code+
"]";
}
}
User:
package com.xiaoxie.pojo;
import java.util.List;
public
class User {
private Integer
id;
private String
user_code;
private String
user_name;
private List<Order>
orders;
public Integer getId() {
return
id;
}
public
void setId(Integer
id) {
this.
id =
id;
}
public String getUser_code() {
return
user_code;
}
public
void setUser_code(String
user_code) {
this.
user_code =
user_code;
}
public String getUser_name() {
return
user_name;
}
public
void setUser_name(String
user_name) {
this.
user_name =
user_name;
}
public List<Order> getOrders() {
return
orders;
}
public
void setOrders(List<Order>
orders) {
this.
orders =
orders;
}
@Override
public String toString() {
return
"User[id=+"+
id+
",user_code="+
user_code+
",user_name="+
user_name+
",orders:"+
orders+
"]";
}
}
新增Mapper映射文件
OrderMapper.xml:
<?
xml
version=
"1.0"
encoding=
"UTF-8"
?>
<!
DOCTYPE
mapper
<
mapper
namespace=
"com.xiaoxie.dao.OrderDao"
>
<
select
id=
"selectOrderByUserCode"
parameterType=
"String"
resultType=
"com.xiaoxie.pojo.Order"
>
select * from `order` where user_code = #{user_code}
</
select
>
</
mapper
>
UserMapper:
<?
xml
version=
"1.0"
encoding=
"UTF-8"
?>
<!
DOCTYPE
mapper
<
mapper
namespace=
"com.xiaoxie.dao.UserDao"
>
<
resultMap
type=
"com.xiaoxie.pojo.User"
id=
"userAndOrders_1"
>
<
id
property=
"id"
column=
"id"
/>
<
result
property=
"user_code"
column=
"user_code"
/>
<
result
property=
"user_name"
column=
"user_name"
/>
<!-- 一对多的级联,这里面ofType表示集合的元素类型,把user_code作为参数传入(嵌套查询) -->
<
collection
property=
"orders"
ofType=
"com.xiaoxie.pojo.Order"
column=
"user_code"
select=
"com.xiaoxie.dao.OrderDao.selectOrderByUserCode"
/>
</
resultMap
>
<
select
id=
"selectUserOrdersById_1"
parameterType=
"Integer"
resultMap=
"userAndOrders_1"
>
select * from user where id=#{id}
</
select
>
<!-- 一对多的级联,一个查询嵌套结果 -->
<
resultMap
type=
"com.xiaoxie.pojo.User"
id=
"userAndOrders_2"
>
<
id
property=
"id"
column=
"id"
/>
<
result
property=
"user_code"
column=
"user_code"
/>
<
result
property=
"user_name"
column=
"user_code"
/>
<
collection
property=
"orders"
ofType=
"com.xiaoxie.pojo.Order"
>
<
id
property=
"id"
column=
"id"
/>
<
result
property=
"order_no"
column=
"order_no"
/>
</
collection
>
</
resultMap
>
<
select
id=
"selectUserOrdersById_2"
parameterType=
"Integer"
resultMap=
"userAndOrders_2"
>
select u.*,o.id,o.order_no,o.user_code from user u left join `order` o on u.user_code = o.user_code
where u.id = #{id}
</
select
>
<!-- 一对多级联,使用
pojo
类存储结果 -->
<
select
id=
"selectUserOrdersById_3"
parameterType=
"Integer"
resultType=
"com.xiaoxie.dao.result.UserAndOrders"
>
select u.*,o.order_no from user u left join `order` o on u.user_code = o.user_code
where u.id = #{id}
</
select
>
</
mapper
>
新增对应的Dao接口
OrderDao:
package com.xiaoxie.dao;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import com.xiaoxie.pojo.Order;
@Repository(
"orderDao")
@Mapper
public
interface OrderDao {
List<Order> selectOrderByUserCode(String
user_code);
}
UserDao:
package com.xiaoxie.dao;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import com.xiaoxie.dao.result.UserAndOrders;
import com.xiaoxie.pojo.User;
@Mapper
@Repository(
"userDao")
public
interface UserDao {
User selectUserOrdersById_1(Integer
id);
User selectUserOrdersById_2(Integer
id);
List<UserAndOrders> selectUserOrdersById_3(Integer
id);
}
新增实体类来保存MyBatis返回的结果
UserAndOrders:
package com.xiaoxie.dao.result;
public
class UserAndOrders {
private Integer
id;
private String
user_code;
private String
user_name;
private String
order_no;
public Integer getId() {
return
id;
}
public
void setId(Integer
id) {
this.
id =
id;
}
public String getUser_code() {
return
user_code;
}
public
void setUser_code(String
user_code) {
this.
user_code =
user_code;
}
public String getUser_name() {
return
user_name;
}
public
void setUser_name(String
user_name) {
this.
user_name =
user_name;
}
public String getOrder_no() {
return
order_no;
}
public
void setOrder_no(String
order_no) {
this.
order_no =
order_no;
}
@Override
public String toString() {
return
"UserOrders[id=+"+
id+
",user_code="+
user_code+
",user_name="+
user_name+
",order_no:"+
order_no+
"]";
}
}
在MyBatis的核心配置文件中添加对这两个Mapper映射文件的扫描
<?
xml
version=
"1.0"
encoding=
"UTF-8"
?>
<!
DOCTYPE
configuration
PUBLIC
"-//
mybatis.org//DTD
Config 3.0//EN"
"
http://mybatis.org/dtd/mybatis-3-config.dtd
"
>
<
configuration
>
<!-- 在MyBatis进行嵌套查询时,使用延迟加载可以提高一定的性能所以在这里可把把这个配置打开 -->
<
settings
>
<!-- 延迟加载 -->
<
setting
name=
"lazyLoadingEnabled"
value=
"true"
/>
<!-- 按需加载 -->
<
setting
name=
"aggressiveLazyLoading"
value=
"false"
/>
</
settings
>
<!-- 查找映射文件 -->
<
mappers
>
<
mapper
resource=
"com/xiaoxie/dao/mybatis/StudentMapper.xml"
/>
<
mapper
resource=
"com/xiaoxie/dao/mybatis/IdCardMapper.xml"
/>
<
mapper
resource=
"com/xiaoxie/dao/mybatis/PersonMapper.xml"
/>
<
mapper
resource=
"com/xiaoxie/dao/mybatis/OrderMapper.xml"
/>
<
mapper
resource=
"com/xiaoxie/dao/mybatis/UserMapper.xml"
/>
</
mappers
>
</
configuration
>
新增Controller类
UserOrderController:
package com.xiaoxie.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import com.xiaoxie.dao.UserDao;
import com.xiaoxie.dao.result.UserAndOrders;
import com.xiaoxie.pojo.User;
@Controller(
"userOrderController")
@Transactional
public
class UserOrderController {
@
Autowired
private UserDao
userDao;
public
void test() {
User
userOrders1 =
userDao.selectUserOrdersById_1(1);
System.
out.println(
userOrders1);
System.
out.println(
"-------------------------------");
User
userOrder2 =
userDao.selectUserOrdersById_2(1);
System.
out.println(
"-------------------------------");
List<UserAndOrders>
userOrder3 =
userDao.selectUserOrdersById_3(1);
System.
out.println(
userOrder3);
}
}
在测试类中进行测试:
ApplicationContext
context =
new ClassPathXmlApplicationContext(
"spring-config.xml");
UserOrderController
userOrderController = (UserOrderController)
context.getBean(
"userOrderController");
userOrderController.test();
多对多级联查询
MyBatis是没有对多对多的级联实现的,因为多对多级联是可以通过两个一对多的级联来实现的。
如我们的订单表与订单商品表一般就是多对多的关系,一个订单A、订单B可以有商品A、商品B
我们再向数据库新增两个表
-- order_detail表
CREATE TABLE `order_detail` (
`id` bigint(19) NOT NULL AUTO_INCREMENT COMMENT '主键',
`order_no` varchar(25) NOT NULL DEFAULT '' COMMENT '订单编号',
`goods_no` varchar(50) NOT NULL DEFAULT '' COMMENT '商品编码',
PRIMARY KEY (`id`)
);
-- goods表
CREATE TABLE `goods` (
`id` bigint(19) NOT NULL AUTO_INCREMENT COMMENT '主键',
`goods_no` varchar(50) NOT NULL DEFAULT '' COMMENT '商品编码',
PRIMARY KEY (`id`)
);
新增POJO实体类
Goods
package com.xiaoxie.pojo;
import java.util.List;
public
class Goods {
private Integer
id;
private String
goods_no;
//商品对应订单是一个对多的关系
private List<Order>
orders;
public Integer getId() {
return
id;
}
public
void setId(Integer
id) {
this.
id =
id;
}
public String getGoods_no() {
return
goods_no;
}
public
void setGoods_no(String
goods_no) {
this.
goods_no =
goods_no;
}
public List<Order> getOrders() {
return
orders;
}
public
void setOrders(List<Order>
orders) {
this.
orders =
orders;
}
@Override
public String toString() {
return
"Goods[id="+
id+
",goods_no="+
goods_no+
",orders="+
orders+
"]";
}
}
修改Order实体类把对goods的一对多关系加入进去
package com.xiaoxie.pojo;
import java.util.List;
public
class Order {
private Integer
id;
private String
order_no;
private String
user_code;
private List<Goods>
goods;
public Integer getId() {
return
id;
}
public
void setId(Integer
id) {
this.
id =
id;
}
public String getOrder_no() {
return
order_no;
}
public
void setOrder_no(String
order_no) {
this.
order_no =
order_no;
}
public String getUser_code() {
return
user_code;
}
public
void setUser_code(String
user_code) {
this.
user_code =
user_code;
}
public List<Goods> getGoods() {
return
goods;
}
public
void setGoods(List<Goods>
goods) {
this.
goods =
goods;
}
@Override
public String toString() {
return
"Order[id=+"+
id+
",order_no="+
order_no+
",user_code="+
user_code+
",goods="+
goods+
"]";
}
}
在OrderMapper的映射文件中新增<resultMap>来处理多对多的查询
<?
xml
version=
"1.0"
encoding=
"UTF-8"
?>
<!
DOCTYPE
mapper
<
mapper
namespace=
"com.xiaoxie.dao.OrderDao"
>
<
select
id=
"selectOrderByUserCode"
parameterType=
"String"
resultType=
"com.xiaoxie.pojo.Order"
>
select * from `order` where user_code = #{user_code}
</
select
>
<!-- 多对多的级联 -->
<
resultMap
type=
"com.xiaoxie.pojo.Order"
id=
"OrdersAndGoods"
>
<
id
property=
"id"
column=
"id"
/>
<
result
property=
"order_no"
column=
"order_no"
/>
<
result
property=
"user_code"
column=
"user_code"
/>
<!-- 多对多 -->
<
collection
property=
"goods"
ofType=
"com.xiaoxie.pojo.Goods"
>
<
id
property=
"id"
column=
"id"
/>
<
result
property=
"goods_no"
column=
"goods_no"
/>
</
collection
>
</
resultMap
>
<
select
id=
"selectAllOrderAndGoods"
resultMap=
"OrdersAndGoods"
>
select o.*,g.* from `order` o
left join order_detail d on o.order_no = d.order_no
left join goods g on d.goods_no = g.goods_no
</
select
>
</
mapper
>
新增对应的Dao接口方法
package com.xiaoxie.dao;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import com.xiaoxie.pojo.Order;
@Repository(
"orderDao")
@Mapper
public
interface OrderDao {
List<Order> selectOrderByUserCode(String
user_code);
List<Order> selectAllOrderAndGoods();
//新增的多对多接口方法
}
新增Controller类
OrdersAndGoodsController:
package com.xiaoxie.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.transaction.annotation.Transactional;
import com.xiaoxie.dao.OrderDao;
import com.xiaoxie.pojo.Order;
@Controller(
"ordersAndGoodsController")
@Transactional
public
class OrdersAndGoodsController {
@Autowired
private OrderDao
orderDao;
public
void test() {
List<Order>
orders =
orderDao.selectAllOrderAndGoods();
System.
out.println(
orders);
}
}
在测试类中进行测试
ApplicationContext
context =
new ClassPathXmlApplicationContext(
"spring-config.xml");
OrdersAndGoodsController
ogc
= (OrdersAndGoodsController)
context
.getBean(
"ordersAndGoodsController"
);
ogc.test();