多表模型
- 一对一:在任意一方建立外键,关联对方的主键。
- 一对多:在多的一方建立外键,关联一的一方的主键。
- 多对多:借助中间表,中间表至少两个字段,分别关联两张表的主键。
xml方式配置多表操作
使用到的标签及属性说明
- 标签及属性说明
- resultMap标签:配置字段和对象属性的映射关系标签。
- id属性:定义此映射配置的唯一标识。
- type属性:设置此映射配置对应的的实体类对象
- id标签:配置主键映射关系标签。
- result属性:配置非主键映射关系标签
- column 属性:表中字段名称
- property 属性: 实体对象变量名称
- association属性:配置被包含对象的映射关系标签。(一对一关系时使用此标签)
- property 属性:被包含对象的变量名
- javaType 属性:被包含对象的数据类型
- collection标签:配置被包含对象的映射关系标签(一对多或多对多关系时使用此标签)
- property 属性:被包含对象的变量名
- ofType属性:被包含对象的数据类型
- resultMap标签:配置字段和对象属性的映射关系标签。
一对一
例子:一个学生对应一个身份证号码,一个身份证号码对应一个学生,这就是一对一的关系。
-
实体类
//学生实体类 public class Student { private Integer id; //主键id private String name; //学生姓名 private Integer age; //学生年龄 private Card card; //学生所对应的身份证信息 //...构造与get、set方法省略 } //身份证实体类 public class Card { private Integer id; //主键id private String number; //身份证号 //...构造与get、set方法省略 }
-
接口
//关于学生的接口 public interface StudentMapper { /** * 查询全部学生及其身份证号码 */ List<Student> selectAll(); }
-
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"> <!--namespace: 设置此xml文件对应的接口的全路径类名--> <mapper namespace="com.itheima.one_to_one.StudentMapper"> <!--配置结果映射--> <resultMap id="studentMap" type="student"> <!--配置结果集中的主键字段与实体类中的属性的映射--> <id property="id" column="id"/> <!--配置结果集中的其他字段与实体类中的属性的映射--> <result property="name" column="name"/> <result property="age" column="age"/> <!--配置一对一的关系映射--> <association property="card" javaType="card"> <id property="id" column="id"/> <result property="number" column="number"/> </association> </resultMap> <!-- 查询所有学生信息及其身份证信息 id:对应接口中方法名 resultMap:表示将此sql语句查询的结果映射到哪里,一般写上自己配置的映射的id --> <select id="selectAll" resultMap="studentMap"> SELECT s.id, s.name, s.age, c.id, c.number FROM student s, card c WHERE s.id=c.pid; </select> </mapper>
一对多
例子:一个班级对应多个学生,一个学生只能对应一个班级,这就是一对多的关系。
-
实体类
//学生实体类 public class Student { private Integer id; //主键id private String name; //学生姓名 private Integer age; //学生年龄 //...构造与get、set方法省略 } //班级实体类 public class Classes { private Integer id; //主键id private String name; //班级名称 private List<Student> students; //班级中所有学生对象,一个班级对应多个学生,所以用集合类型 //...构造与get、set方法省略 }
-
接口
/** * 关于班级的接口 */ public interface ClassesMapper { /** * 查询所有班级的记录,以及每个班级下的所有学生 */ List<Classes> selectAll(); }
-
xml配置文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--namespace:设置此xml文件对应的接口--> <mapper namespace="com.itheima.one_to_many.ClassesMapper"> <!--配置结果映射--> <resultMap id="classesMap" type="classes"> <id property="id" column="id"/> <result property="name" column="name"/> <!--配置一对多的关系映射--> <collection property="students" ofType="student"> <id property="id" column="sid"/> <result property="name" column="sname"/> <result property="age" column="age"/> </collection> </resultMap> <!--查询所有班级的记录,以及每个班级下的所有学生--> <select id="selectAll" resultMap="classesMap"> SELECT c.*, s.id sid, s.name sname, age FROM classes c INNER JOIN student s WHERE c.id=s.cid </select> </mapper>
多对多
例子:一个学生能选择多门课程,一门课程可以被多个学生选择,这就是多对多的关系。
-
实体类
//学生实体类 public class Student { private Integer id; //主键id private String name; //学生姓名 private Integer age; //学生年龄 private List<Course> courses; //学生所选择的课程对象,多对多的关系用集合类型 //...构造与get、set方法省略 } //课程实体类 public class Course { private Integer id; //主键id private String name; //课程名称 private List<Student> students; //选取此课程的学生对象,多对多用集合类型 //...构造与get、set方法省略 }
-
接口
/** * 关于课程的接口 */ public interface CourseMapper { /** * 查询全部课程,以及每门课程所选的学生的记录 */ List<Course> selectAll(); }
-
xml配置文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--namespace:设置此文件所对应的接口--> <mapper namespace="com.itheima.many_to_many.CourseMapper"> <!--配置结果映射--> <resultMap id="courseMap" type="course"> <id property="id" column="cid"/> <result property="name" column="cname"/> <!--配置关系映射--> <!-- 多对多关系使用ofType设置属性对应的类型 一对一关系使用javaType设置属性对应的类型 --> <collection property="students" ofType="student"> <id property="id" column="sid"/> <result property="name" column="sname"/> <result property="age" column="age"/> </collection> </resultMap> <!--查询全部课程,以及每门课程所选的学生的记录--> <select id="selectAll" resultMap="courseMap"> SELECT sc.cid, c.name cname, sc.sid, s.name sname, s.age FROM course c INNER JOIN stu_cr sc ON c.id=sc.cid INNER JOIN student s ON s.id=sc.sid; </select> </mapper>
方法测试
这里只测试一个,其他的方法测试都是一样的步骤.
/**
* 多对多的测试
*/
public class ManyToManyTest {
@Test
public void test01() throws Exception {
//读取mybatis核心配置文件
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
//创建sql会话工厂生成器
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
//获取sql会话工厂
SqlSessionFactory factory = builder.build(inputStream);
//获取sql会话
SqlSession sqlSession = factory.openSession(true);
//获取代理对象
CourseMapper mapper = sqlSession.getMapper(CourseMapper.class);
//调用方法
List<Course> courses = mapper.selectAll();
//处理结果
courses.forEach(element -> System.out.println(element));
//关闭资源
sqlSession.close();
inputStream.close();
}
}
注意
- 要导入所需的jar包
- mybatis核心配置文件中要进行相应的配置。
- 要在mybatis核心配置文件中指定扫描的包或类,这样加载核心配置文件的时候,才能扫描到sql映射配置文件。
<!--配置包扫描-->
<mappers>
<!--这里直接配置扫描包-->
<package name="com.itheima"/>
</mappers>
XML配置实现多表查询方式二
在一个xml配置中,调用另一个xml配置中的查询方法。
<!--配置实体类属性和数据库表中列的对应关系-->
<resultMap id="BaseResultMap" type="com.itheima.domain.system.Dept">
<id column="dept_id" property="id"/>
<result column="dept_name" property="deptName"/>
<result column="parent_id" property="parentId"/>
<result column="state" property="state"/>
<!--
关联关系
property:一方中另一方的属性名
javaType:另一方属性名的数据类型
column:传递给select属性指定的方法的参数字段。
select:接下来要调用的方法。
-->
<association
property="parent"
javaType="com.itheima.domain.system.Dept"
column="parent_id"
select="com.itheima.dao.system.DeptDao.findById"
/>
</resultMap>
注解的方式实现多表操作
使用注解的方式操作数据库,可以简化我们的代码,并且不需要写slq映射配置文件了。
常用注解介绍
- @Select(“查询的 SQL 语句”):执行查询操作注解
- @Insert(“新增的 SQL 语句”):执行新增操作注解
- @Update(“修改的 SQL 语句”):执行修改操作注解
- @Delete(“删除的 SQL 语句”):执行删除操作注解
mybatis注解方式单表操作示例
-
实体类
//学生实体类 public class Student { private Integer id; private String name; private Integer age; }
-
接口
//与学生相关的接口 public interface StudentMapper { //查询全部 @Select("SELECT * FROM student") List<Student> selectAll(); //根据id查询 @Select("SELECT * FROM student WHERE id=#{id}") Student selectById(Integer id); //新增数据 @Insert("INSERT INTO student VALUES(NULL, #{name}, #{age})") Integer insert(Student stu); //修改数据 @Update("UPDATE student SET name=#{name}, age=#{age} WHERE id=#{id}") Integer update(Student stu); //删除数据 @Delete("DELETE FROM student WHERE id=#{id}") Integer delete(Integer id); }
-
测试类
@Test public void test01() throws Exception { //读取mybatis核心配置文件 InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); //创建sql会话工厂生成器 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); //获取sql会话工厂 SqlSessionFactory factory = builder.build(inputStream); //获取sql会话 SqlSession sqlSession = factory.openSession(true); //4.获取StudentMapper接口的实现类对象 StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); //5.调用实现类的方法,接收结果 List<Student> students = mapper.selectAll(); //6.处理结果 students.forEach(student -> System.out.println(student)); //7.释放资源 sqlSession.close(); //关闭资源 sqlSession.close(); inputStream.close(); }
这里只测试一个方法,测试其它方法是一样的步骤。
mybatis多表操作常用注解介绍
- @Results:封装映射关系的父注解。
- Result[] value():定义了 Result 数组
- @Result:封装映射关系的子注解。
- column 属性:查询出的结果集中的字段名称
- property 属性:要封装的实体对象中的属性名称
- javaType 属性:被包含对象的数据类型
- one 属性:一对一查询固定属性
- many 属性:一对多查询固定属性
- @One:一对一查询的注解。
- select 属性:指定调用某个接口中的方法执行。
- @Many:一对多查询的注解。
- select 属性:指定调用某个接口中的方法执行。
一对一
-
实体类
//人实体类 public class Person { private Integer id; //主键id private String name; //人的姓名 private Integer age; //人的年龄 //...构造与get、set方法省略 } //身份证实体类 public class Card { private Integer id; //主键id private String number; //身份证号 private Person p; //所属人的对象 //...构造与get、set方法省略 }
-
接口
/** * 关于人的接口 */ public interface PersonMapper { //根据id查询 @Select("SELECT * FROM person WHERE id=#{id}") public abstract Person selectById(Integer id); } /** * 查询全部身份证信息以及对应的人的信息 * @return */ @Select("SELECT * FROM card") //配置属性与字段映射 @Results({ //普通属性与结果集字段配置映射 @Result(property = "id", column = "id"), @Result(property = "number", column = "number"), /* 配置一对一的映射关系 property:被包含对象的属性名 javaType:被包含对象的属性对应的数据类型 select:要调用执行的方法,指定方法所在类的全限定名+方法名 column:执行调用方法所需的参数,值为执行sql语句后的结果集中的字段名 one、@One:用于表示一对一的关系 */ @Result(property = "p", javaType = Student.class, column = "pid", one = @One( select = "com.itheima.one_to_one.PersonMapper.selectById" )) }) List<Card> selectAll();
一对多
-
实体类
//班级实体类 public class Classes { private Integer id; //主键id private String name; //班级名称 private List<Student> students; //班级中所有学生对象 //...构造与get、set方法省略 } //学生实体类 public class Student { private Integer id; //主键id private String name; //学生姓名 private Integer age; //学生年龄 //...构造与get、set方法省略 }
-
接口
//关于学生的接口 public interface StudentMapper { //根据cid查询student表 @Select("SELECT * FROM student WHERE cid=#{cid}") public abstract List<Student> selectByCid(Integer cid); } //关于班级的接口 public interface ClassesMapper { //查询全部班级记录及其每个班级下的学生记录 @Select("SELECT * FROM classes") @Results({ @Result(property = "id", column = "id"), @Result(property = "name", column = "name"), /* 配置一对多的映射关系 property:被包含对象的属性名 javaType:被包含对象的属性对应的数据类型 select:要调用执行的方法,指定方法所在类的全限定名+方法名 column:执行调用方法所需的参数,值为执行sql语句后的结果集中的字段名 many、@many:用于表示一对多的关系 */ @Result(property = "students", javaType = List.class, column = "id", many = @Many( select = "com.itheima.one_to_many.StudentMapper.selectByCid" )) }) List<Classes> selectAll(); }
多对多
-
实体类
//学生实体类 public class Student { private Integer id; //主键id private String name; //学生姓名 private Integer age; //学生年龄 private List<Course> courses; //学生所选择的课程对象 //...构造与get、set方法省略 } //课程实体类 public class Course { private Integer id; //主键id private String name; //课程名称 private List<Student> students; //选取此课程的学生对象,多对多用集合类型 //...构造与get、set方法省略 }
-
接口
//关于课程的接口 public interface CourseMapper { //根据学生id查询所选课程 @Select("SELECT c.id,c.name FROM stu_cr sc,course c WHERE sc.cid=c.id AND sc.sid=#{id}") public abstract List<Course> selectBySid(Integer id); } //关于学生的接口 public interface StudentMapper { //查询全部 @Select("SELECT DISTINCT s.id,s.name,s.age FROM student s,stu_cr sc WHERE sc.sid=s.id") @Results({ @Result(property = "id", column = "id"), @Result(property = "name", column = "name"), @Result(property = "age", column = "age"), @Result(property = "courses", javaType = List.class, column = "id" , many = @Many( select = "com.itheima.many_to_many.CourseMapper.selectBySid" )) }) List<Student> selectAll(); }
测试(多对多)
public class Test01 {
@Test
public void selectAll() throws Exception{
//1.加载核心配置文件
InputStream is = Resources.getResourceAsStream("MyBatisConfig.xml");
//2.获取SqlSession工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
//3.通过工厂对象获取SqlSession对象
SqlSession sqlSession = sqlSessionFactory.openSession(true);
//4.获取StudentMapper接口的实现类对象
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
//5.调用实现类对象中的方法,接收结果
List<Student> list = mapper.selectAll();
//6.处理结果
for (Student student : list) {
System.out.println(student.getId() + "," + student.getName() + "," + student.getAge());
List<Course> courses = student.getCourses();
for (Course cours : courses) {
System.out.println("\t" + cours);
}
}
//7.释放资源
sqlSession.close();
is.close();
}
}
测试方法都大同小异,只是获取代理对象后,调用的方法不同。
注意点
-
要记得在mybatis核心配置文件中配置映射扫描
<!--配置扫描包--> <mappers> <package name="com.itheima"/> </mappers>
-
column是执行sql后的结果集中的字段,当做参数,传递至要调用的方法,作为方法的参数。
@Result(property = "courses", javaType = List.class, column = "id" , many = @Many( select = "com.itheima.many_to_many.CourseMapper.selectBySid" ))
-
多注意观察报错信息,加油吧 _