使用mybatis访问数据库,将数据库表与pojo对象映射的方式有两种:Auto Mapping特性自动映射和resultMap标签手动映射。
Auto Mapping特性
当数据库表列名与实体类的属性名一致时可以使用自动映射。
例如,数据库表结构为:
实体类设计为:
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.test.mapper">
<select id="selectAll" resultType="com.test.pojo.People">
select * from people
</select>
</mapper>
因为数据库表列名和实体类属性名相同,只需要resultType指定返回值类型,不需要配置resultMap属性即可实现自动映射。
测试sql语句执行:
public class Test {
public static void main(String[] args) throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession session = factory.openSession();
List<People> selectList = session.selectList("com.test.mapper.selectAll");
session.commit();
for (People people : selectList) {
System.out.println(people);
}
}
}
从结果可以看到,正确的执行了查询全部的sql语句,并且实体类的每一个属性值都成功映射到了。
下面我们修改一下实体类的name属性名,使之与数据库表的列名不同:
我们再看一下执行结果:
从结果看到,此时它依然能够正确的执行,并正确映射,与我们之前的前提“数据库表列名与实体类属性名相同”不相符。
在实体类中,除了属性声明外,我们还定义了属性的getter个setter方法,所以很有可能此时是通过setter方法进行的自动映射,为了验证,再把setName方法名改为setName1测试一下:
Sql语句能正常执行,但是查询结果中实体类对象的name属性值变成了null,说明此时name属性没有正确的映射。那么mybatis是通过实体类的属性名还是setter方法来进行自动映射的呢?
我们再将第一次执行的实体类的所有属性的setter方法删掉,测试一下是否能自动映射:
依然可以正确的执行自动映射,我们在此基础上修改name属性名:
从上结果可以看到,此时属性name就不能成功自动映射。
再试一下属性名可映射,setter方法无法映射的情况:
此时也可以进行映射。
那么我们就可以得出结论:
1、通过属性名映射和通过setter方法映射的方式是互补的。
2、当实体类中有setter方法(属性名无法映射)时,mybatis自动映射机制是通过setter方法执行的,setter方法名称正确(set+列名)即可正确映射;
3、当实体类中没有setter方法(setter方法无法映射)时,mybatis自动映射机制是通过属性名进行的,当属性名与列名相同即可完成映射。
4、mybatis自动映射不区分大小写;
5、可以在sql语句中给列名设置别名与实体类进行映射;
<select id="selectAll" resultType="com.test.pojo.People">
select id, name as name1, age from people
</select>
6、映射是通过反射技术实现的。
resultMap标签单表映射
除了使用mybatis的Auto Mapping特性自动映射外,还可以使用resultMap标签进行手动映射。
<?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.test.mapper">
<resultMap type="com.test.pojo.People" id="peoMap">
<id column="id" property="id"/>
<result column="name" property="name1"/>
<result column="age" property="age"/>
</resultMap>
<select id="selectAll" resultMap="peoMap">
select * from people
</select>
</mapper>
手动映射需要编写resultMap标签,select中需要使用resultMap属性声明使用到的resultMap标签的id属性,同时不再需要配置resultType属性。
resultMap标签中需要配置resultType属性声明映射的实体类,使用id属性标识此resultMap标签。同时映射的数据库表中主键字段需要使用id标签配置映射,其他字段使用result标签配置映射。
上述mapper文件映射的实体类如下:
从下面的执行结果看,可以正确的映射:
也可以两种映射方法混合使用,如下:
<?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.test.mapper">
<resultMap type="com.test.pojo.People" id="peoMap">
<result column="name" property="name1"/>
</resultMap>
<select id="selectAll" resultMap="peoMap">
select * from people
</select>
</mapper>
即name列使用resultMap手动映射,其他列使用Auto Mapping自动映射。
resultMap标签实现多表查询关联单个对象
N+1方式
N+1方式,先查询出某个表的全部信息,根据这个表的信息查询另一个表的信息,如下所示:
<resultMap type="student" id="stuMap">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="age" column="age"/>
<result property="tid" column="tid"/>
<!-- 如果关联一个对象-->
<association property="teacher"
select="com.test.mapper.TeacherMapper.selById"
column="tid">
</association>
</resultMap>
<select id="selAll" resultMap="stuMap">
select * from student
</select>
<association>
标签用于装配一个对象。其中property属性用于声明对象在类中的属性名,select属性用于声明通过哪个查询查询出这个对象的信息,column属性用于声明把当前表的哪个列的值做为参数传递给另一个查询。
另外,使用N+1 方式.时如果列名和属性名相同可以不配置,使用Auto mapping 特性.但是mybatis 默认只会给列专配一次,即tid被传入查询teacher信息的sql语句中之后不会再映射到student中的tid属性中。所以上面的sql语句映射可以简写成如下形式:
<resultMap type="student" id="stuMap">
<result column="tid" property="tid"/>
<!-- 如果关联一个对象 -->
<association property="teacher" select="com.test.mapper.TeacherMapper.selById" column="tid"></association>
</resultMap>
<select id="selAll" resultMap="stuMap">
select * from student
</select>
TeacherMapper中的sql语句映射为:
<select id="selById" resultType="teacher" parameterType="int">
select * from teacher where id=#{0}
</select>
Student实体类如下:
package com.test.pojo;
public class Student {
private int id;
private String name;
private int age;
private int tid;
private Teacher teacher;
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getTid() {
return tid;
}
public void setTid(int tid) {
this.tid = tid;
}
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + ", age=" + age + ", tid=" + tid + ", teacher=" + teacher + "]";
}
}
Teacher实体类如下:
package com.test.pojo;
public class Teacher {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Teacher [id=" + id + ", name=" + name + "]";
}
}
测试查询结果:
package com.test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import com.test.pojo.Student;
public class Test {
public static void main(String[] args) throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession session = factory.openSession();
List<Student> list = session.selectList("com.test.mapper.StudentMapper.selAll");
System.out.println(list);
session.close();
System.out.println("程序执行结束");
}
}
查询的log来看,是先执行了查询Student表的sql语句,再执行的查询Teacher表的sql语句。
联合查询方式
即使用连接查询方式,需要使用<association>
标签装配Teacher对象,javaType属性用于声明返回的Java对象类型。如下sql语句映射使用的是左外连接方式查询,并且resultMap中使用的别名映射。
<resultMap type="Student" id="stuMap1">
<id column="sid" property="id"/>
<result column="sname" property="name"/>
<result column="age" property="age"/>
<result column="tid" property="tid"/>
<association property="teacher" javaType="Teacher" >
<id column="tid" property="id"/>
<result column="tname" property="name"/>
</association>
</resultMap>
<select id="selAll1" resultMap="stuMap1">
select s.id sid,s.name sname,age age,t.id tid,t.name tname FROM student s left outer join teacher t on s.tid=t.id
</select>
resultMap标签实现多表查询关联集合对象
关联集合对象,即用于一对多的场景,即一个老师对应多个学生。
Teacher实体类:
package com.test.pojo;
import java.util.List;
public class Teacher {
private int id;
private String name;
private List<Student> list;
public List<Student> getList() {
return list;
}
public void setList(List<Student> list) {
this.list = list;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Teacher [id=" + id + ", name=" + name + ", list=" + list + "]";
}
}
Teacher类中list属性为老师对应的学生集合。
Student实体类:
package com.test.pojo;
public class Student {
private int id;
private String name;
private int age;
private int tid;
private Teacher teacher;
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getTid() {
return tid;
}
public void setTid(int tid) {
this.tid = tid;
}
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + ", age=" + age + ", tid=" + tid + ", teacher=" + teacher + "]";
}
}
N+1方式
TeacherMapper中sql语句映射使用N+1方式,如下:
<resultMap type="teacher" id="mymap">
<id column="id" property="id"/>
<result column="name" property="name"/>
<collection property="list" select="com.test.mapper.StudentMapper.selByTid" column="id"></collection>
</resultMap>
<select id="selAll" resultMap="mymap">
select * from teacher
</select>
StudentMapper中sql语句映射如下:
<select id="selByTid" parameterType="int" resultType="student">
select * from student where tid=#{0}
</select>
测试sql执行代码:
package com.test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import com.test.pojo.Teacher;
public class Test {
public static void main(String[] args) throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession session = factory.openSession();
List<Teacher> list = session.selectList("com.test.mapper.TeacherMapper.selAll");
for (Teacher teacher : list) {
System.out.println(teacher);
}
// System.out.println(list);
session.close();
System.out.println("程序执行结束");
}
}
联合查询方式
TeacherMapper中sql语句映射使用左连接:
<resultMap type="teacher" id="mymap1">
<id column="tid" property="id"/>
<result column="tname" property="name"/>
<collection property="list" ofType="student" >
<id column="sid" property="id"/>
<result column="sname" property="name"/>
<result column="age" property="age"/>
<result column="tid" property="tid"/>
</collection>
</resultMap>
<select id="selAll1" resultMap="mymap1">
select t.id tid,t.name tname,s.id sid,s.name sname,age,tid from teacher t LEFT JOIN student s on t.id=s.tid;
</select>
使用Auto Mapping 结合别名实现多表查询
Auto Mapping实现多表查询,只能使用连接查询结合别名实现,并且不支持关联集合对象,只能关联单个对象。
<select id="selAll" resultType="student">
select t.id `teacher.id`,t.name `teacher.name`,s.id id,s.name name,age,tid
from student s LEFT JOIN teacher t on t.id=s.tid
</select>
业务层装配实现多表查询
除了在mybatis的sql语句映射中来实现多表查询外,我们还可以分别查询数据库表,然后再业务层Java代码中进行关联装配。
//查询学生信息
List<Student> list = studentMapper.selByPage(pi);
//查询出每个学生对应的老师信息
TeacherMapper teacherMapper = session.getMapper(TeacherMapper.class);
for (Student student : list) {
student.setTeacher(teacherMapper.selById(student.getTid()));
}