MyBatis框架学习V

Talking is Cheap,Show Me the Code

引子

之前无论是使用实体类映射文件的方式,还是使用注解的方式,都只是对单个表进行的增删改查的操作。但在实际项目中,经常是关联表的查询,比如:最常见到的多对一,一对多,多对多等。这些查询是要如何进行呢,MyBatis要怎么进行支持的呢——这次我学习了多个表之间的关联查询。

过程

对于多个表之间的关联查询,学习主要分为三个部分——一对多,多对一,多对多

一对多

在现实中,实体类一对多的场景有很多,我们随便抽象一个:一个人和他发送的朋友圈时间的关系。(一个人可以发送多条朋友圈,但是每条朋友圈只对应一个发送人)

  • 准备工作
    首先我们需要创建两个实体类对应的表,不妨命名为person表和post表,建表的语句大致如下,并且插入几条数据
CREATE TABLE [person] (
  [id] int  NOT NULL PRIMARY KEY,
  [name] varchar(255) NOT NULL,
  [mobile] varchar(255) NOT NULL,
  [create] datetime DEFAULT ('0000-00-00 00:00:00') NOT NULL
);
CREATE TABLE [post] (
  [id] int  NOT NULL PRIMARY KEY,
  [pid] int  NOT NULL,
  [title] varchar(255)  NOT NULL,
  [time] datetime DEFAULT ('0000-00-00 00:00:00') NOT NULL
);
插入几条数据
  • 创建实体类
    需要创建的实体类分别是Person和Post,属性与表属性一保持一致
//person实体类
public class Person {
    private int id;
    private String name;
    private String mobile;
    private String createTime;
    private List<Post> posts;
  //此处省略setter和getter方法
}
//post实体类
public class Post {
    private int id;
    private int pid;
    private String title;
    private String createTime;
    private Person person;
 //此处省略setter和getter方法
}
  • 创建实体类配置文件
    在Java实体对象对中,一对多可以根据List和Set来实现,两者在MyBatis中都是通过collection标签来配合来加以实现。
<?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.bean.personMapper">
    <!-- Person 级联朋友圈查询 方法配置 (一个人对多条朋友圈)  -->

    <resultMap type="Person" id="resultPersonMap">
        <result property="id" column="id" />
        <result property="name" column="name" />
        <result property="mobile" column="mobile" />
        <result property="createTime" column="create"/>
        <collection property="posts" ofType="com.bean.Post" column="pid">
            <result property="id"         column="id"     javaType="int"    jdbcType="INTEGER"/>
            <result property="title"      column="title"  javaType="string" jdbcType="VARCHAR"/>
            <result property="createTime" column="time" javaType="string" jdbcType="VARCHAR"/>
        </collection>
    </resultMap>

    <select id="getPerson" resultMap="resultPersonMap" parameterType="int">
        SELECT per.*,p.*
        FROM person per, post p
        WHERE per.id=p.pid AND per.id=#{id}
    </select>

</mapper>

从配置文件中,可以看到这次所作的查询其实只有一个——id为getPerson的一个查询,传入的参数是用户的id然后联合查询获取到结果——resultPersonMap,这个就是之前resultMap标签声明的结果集,结果集的属性(也就是实体类的属性)和数据表一一对应,而collection标签对应的是通过pid(用户id)查询出来的朋友圈记录。

  • 创建项目配置文件
    创建Configuration.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>
    <!--声明实体类-->
    <typeAliases>
        <typeAlias alias="Person" type="com.bean.Person" />
        <typeAlias alias="Post" type="com.bean.Post" />
    </typeAliases>

    <!--声明数据库连接参数-->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <property name="driver" value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />
                <property name="url" value="jdbc:sqlserver://localhost:1433;DatabaseName=ssm" />
                <property name="username" value="sa" />
                <property name="password" value="xiaogezzZ" />
            </dataSource>
        </environment>
    </environments>

    <!--实体类映射文件声明-->
    <mappers>
        <mapper resource="com/bean/Person.xml" />
    </mappers>

</configuration>
  • 测试类
public class Main {
        private static SqlSessionFactory sqlSessionFactory;
        private static Reader reader;

        static {
            try {
                reader = Resources.getResourceAsReader("config/Configure.xml");
                sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        public static SqlSessionFactory getSession() {
            return sqlSessionFactory;
        }

        public static void main(String[] args) {
            // TODO Auto-generated method stub
            SqlSession session = sqlSessionFactory.openSession();
            try {
                int id = 1;
                Person person = session.selectOne("com.bean.personMapper.getPerson", id);
                System.out.println("Name: " + person.getName());
                System.out.println("Mobile: " + person.getMobile());
                List<Post> posts = person.getPosts();
                for(Post p : posts) {
                    System.out.println("----***----");
                    System.out.println("Title: " + p.getTitle());
                    System.out.println("PieceCreateTime: " + p.getCreateTime());
                }
            } finally {
                session.close();
            }
        }
    }
  • 运行结果


    运行结果
  • 注意点
    在实体类映射文件中,查询语句中id需要指定明确,不然会报下面的错误


    id不明确

    在创建表的时候需要注意,两个表尽量不要有相同名称的属性,不然在结果中出现的结果和实际结果不一致。比如,我们把post表的time属性改名和person表中的create属性相同,都为create,那么根据表属性,实体类映射文件中Collection对应的写法应该为

<collection property="posts" ofType="com.bean.Post" column="pid">
            <result property="id" column="id" javaType="int" jdbcType="INTEGER"/>
            <result property="title" column="title" javaType="string" jdbcType="VARCHAR"/>
            <result property="createTime" column="create" javaType="string" jdbcType="VARCHAR"/>
</collection>

这样的写法按照表结构和实体类来说都是没问题的,但是一运行,结果会是这样
错误的结果

我们发现每条朋友圈的创建时间都是一样的,而且与实际创建时间不符合——原因是,我们尽管按照表结构和实体类属性来对column赋值,但这样会让程序运行过程中与person表中的create属性相混淆,导致得到的这个值是查询的person的创建时间。

多对一

在一对多的情形中,一个人对应多条朋友圈——反过来即是多对一的情形。根据朋友圈的发送人的id,查找到对应的这个人员的信息。

  • 实体类映射文件
    因为是多对一的情形,一条朋友圈与人员之间的关系是关联关系,因此要修改为association标签
<?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.bean.personMapper">
    <!-- Post 级联朋友圈查询 方法配置 (一条(或多条)朋友圈对应一个人员)  -->

    <resultMap type="Post" id="resultPostsMap">
        <result property="id" column="id" />
        <result property="title" column="title" />
        <result property="createTime" column="create"/>
        <association property="person" javaType="Person">
            <id property="id" column="pid"/>
            <result property="name" column="name"/>
            <result property="mobile" column="mobile"/>
        </association>
    </resultMap>

    <select id="getPosts" resultMap="resultPostsMap" parameterType="int">
        SELECT per.*,p.*
        FROM person per, post p
        WHERE per.id=p.pid AND p.id=#{id}
    </select>

</mapper>
  • 测试类修改
 public static void main(String[] args) {
            // TODO Auto-generated method stub
            SqlSession session = sqlSessionFactory.openSession();
            try {
                int id = 1;
                Post post = session.selectOne("com.bean.personMapper.getPosts", id);
                System.out.println("title: "+post.getTitle());
                System.out.println("PersonName: "+post.getPerson().getName());
                System.out.println("PersonMobile: "+post.getPerson().getMobile());
            } finally {
                session.close();
            }
        }

多对多

MyBatis3.0 添加了association和collection标签专门用于对多个相关实体类数据进行级联查询,之前学习的一对多,多对一情形便是其中之一。但仍不支持多个相关实体类数据的级联保存和级联删除操作。因此在进行实体类多对多映射表设计时,需要专门建立一个关联对象类对相关实体类的关联关系进行描述。
考虑这样的一个情形:大学里一个学生选修多门课程,而一门课程被多个学生选修。这样学生与课程之间就构成了多对多的关系。

  • 创建student表和course表,插入几条数据
create table student(
    sid int not null PRIMARY key,
    sname varchar(255) not null,
    mobile VARCHAR(255) not null DEFAULT '');
CREATE TABLE course(
    cid int not NULL PRIMARY key,
    cname VARCHAR(255) not NULL);
测试数据
  • 创建学生课程映射表
create table student_course(
    sid int not null,
    cid int not null)
    
insert into student_course values(1,1)
insert into student_course values(1,2)
insert into student_course values(2,1)
insert into student_course values(2,3)
insert into student_course values(3,2)
insert into student_course values(3,3)

根据随意插入的数据可知,Tom选了ComputerScience和Art两门课,Jack选了ComputerScience和Java两门课,Sarah选了Art和Java两门课;反过来说,选了CompterScience的有Tom与Jack,选了Art的有Tom与Sarah,选了Java的有Jack与Sarah

  • 创建实体类
    Student实体类
public class Student {
    private int sid;
    private String sname;
    private String mobile;
    private List<Course> courses;
    //此处省略getter和setter方法
}

Course实体类

public class Course {
    private int cid;
    private String cname;
    private List<Student> students;
    //此处省略getter和setter方法
}

StudentCourse实体类

public class StudentCourse {
    private int sid;
    private int cid;
    //此处省略getter和setter方法
}
  • 创建接口映射类
    因为我们可能会不只是查询,可能还会有删除、插入等相关操作,若通过命名空间来调用相关方法不免太过繁琐,使用接口映射类,将要使用的方法声明,在使用的时候可以直接调用。
    StudentMapper映射类
public interface StudentMapper {
    //@Insert("insert into student(sid,name,mobile) values(#{student.getSid()}, #{student.getSName()},#{student.getMobile()})")
    public void insertStudent(Student student);

    //@Select("select * from student where sid = #{sid}")
    public Student getStudent(int sid);

    //@Select("select * from student")
    public List<Student> getAllStudents();
}

CourseMapper映射类

public interface CourseMapper {
    //@Insert("insert into course(cid,cname) values(#{course.getCid()},#{course.getCname()})")
    public void insertCourse(Course course);
    
    //@Select("select * from course where cid=#{cid}")
    public Course getCourse(int cid);

    //@Select("select * from course")
    public List<Course> getAllCourses();
}

StudentCourseMapper映射类

public interface StudentCourseMapper {
    //@Insert("insert into student_course(sid,cid) values(#{studentCourse.getSid()},#{studentCourse.getCid()})")
    public void insertStudentCourse(StudentCourse studentCourse);
    //@Select("select * from student where sid in (select sid from student_course)")
    public List<Student> getStudents();
}

这里因为使用的是映射文件,所以注解都被注释了。另外,如果想要添加其他的方法,比如说是删除、更新等,都可以在对应的映射类中进行声明。

  • 映射文件
    Student映射
<?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.maper.StudentMapper">
    <parameterMap type="Student" id="parameterStudentMap">
        <parameter property="sid"/>
        <parameter property="sname"/>
        <parameter property="mobile"/>
    </parameterMap>

    <insert id="insertStudent" parameterMap="parameterStudentMap">
        insert into student(sid,sname,mobile)
        values(#{sid},#{sname},#{mobile});
    </insert>

    <resultMap type="Student" id="resultStudent">
        <result property="sid" column="sid"/>
        <result property="sname" column="sname"/>
        <collection property="courses" column="sid" select="com.maper.StudentCourseMapper.getCoursesByStudentId"/>
    </resultMap>

    <select id="getStudent" resultMap="resultStudent" parameterType="int">
        SELECT *
        FROM student
        WHERE sid=#{sid}
    </select>
    
    <select id="getAllStudents" resultMap="resultStudent">
        select *
        from student;
    </select>
</mapper>

Course映射

<?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.maper.CourseMapper">

    <parameterMap type="Course" id="parameterCourseMap">
        <parameter property="cid"/>
        <parameter property="cname"/>
    </parameterMap>

    <insert id="insertCourse" parameterMap="parameterCourseMap">
        insert into course (cid,cname)
        VALUES(#{cid},#{cname});
    </insert>

    <resultMap type="Course" id="resultCourseMap">
        <result property="cid" column="cid" />
        <result property="cname" column="cname" />
        <collection property="students" column="cid" select="com.maper.StudentCourseMapper.getStudentsByCourseId" />
    </resultMap>
    <select id="getCourse" resultMap="resultCourseMap" parameterType="int">
        SELECT *
        FROM course
        WHERE cid=#{cid}
    </select>

    <select id="getAllCourses" resultMap="resultCourseMap">
        select * from course
    </select>
</mapper>

StudentCourse映射

<?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.maper.StudentCourseMapper">
    <parameterMap type="StudentCourse" id="parameterStudentCourseMap">
        <parameter property="sid"/>
        <parameter property="cid"/>
    </parameterMap>

    <insert id="insertStudentCourse"  parameterMap="parameterStudentCourseMap">
        INSERT INTO student_course(sid, cid)
        VALUES(#{sid},#{cid})
    </insert>

    <resultMap type="Student" id="resultStudentMap_2">
        <result property="sid" column="sid"/>
        <result property="sname" column="sname"/>
        <result property="mobile" column="mobile"/>
    </resultMap>

    <select id="getStudentsByCourseId" resultMap="resultStudentMap_2" parameterType="int">
        SELECT s.*, sc.cid
        FROM student s, student_course sc
        WHERE s.sid=sc.sid AND sc.cid=#{cid}
    </select>

    <resultMap type="Course" id="resultCourseMap_2">
        <result property="cid" column="cid"/>
        <result property="cname" column="cname"/>
    </resultMap>

    <select id="getCoursesByStudentId" resultMap="resultCourseMap_2" parameterType="int">
        SELECT c.*, sc.sid
        FROM course c, student_course sc
        WHERE c.cid=sc.cid AND sc.sid=#{sid}
    </select>

</mapper>
  • 测试类
import com.bean.Course;
import com.bean.Student;
import com.bean.StudentCourse;
import com.maper.CourseMapper;
import com.maper.StudentCourseMapper;
import com.maper.StudentMapper;
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 java.io.Reader;
import java.util.List;

public class Main {
    private static SqlSessionFactory sqlSessionFactory;
    private static Reader reader;

    static {
        try {
            reader = Resources.getResourceAsReader("config/Configure.xml");
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static SqlSessionFactory getSession() {
        return sqlSessionFactory;
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        AddCourse();
        AddStudent();
        AddStudentCourse();
        GetCourseAndStudents();
        GetStudentAndCourses();
        getAllCoursesAndStudents();
        getAllStudentsAndCourses();
    }

    /*
     * 获取一个学生对应的所有选修课程
     */
    public static void GetStudentAndCourses() {
        SqlSession session = sqlSessionFactory.openSession();
        try {
            int sid = 2;
            StudentMapper studentMapper = session.getMapper(StudentMapper.class);
            Student student = studentMapper.getStudent(sid);
            System.out.println(student);
            System.out.println(student.getSid());
            System.out.println("Student => " + student.getSname() + "," + student.getMobile());
            List<Course> courses = student.getCourses();
            for (Course course : courses) {
                System.out.println("\t:" + course.getCid() + "\t" + course.getCname());
            }
        } finally {
            session.close();
        }
    }

    /*
     * 获取一门课程所有选修的学生
     */
    public static void GetCourseAndStudents() {
        SqlSession session = sqlSessionFactory.openSession();
        try {
            CourseMapper courseMaper = session.getMapper(CourseMapper.class);
            Course course = courseMaper.getCourse(1);
            System.out.println(course);
            System.out.println("Course => " + course.getCname());
            System.out.println("CourseId: " + course.getCid());
            List<Student> students = course.getStudents();
            for (Student student : students) {
                System.out.println("\t:" + student.getSid() + "\t"
                        + student.getSname());
            }
        } finally {
            session.close();
        }
    }

    /*
     * 获取所有课程并且通过课程获取对应的选修了的学生
     */
    public static void getAllCoursesAndStudents() {
        SqlSession session = sqlSessionFactory.openSession();
        try {
            CourseMapper courseMaper = session.getMapper(CourseMapper.class);
            List<Course> courses = courseMaper.getAllCourses();
            for (Course course : courses) {
                System.out.println("Course Id: " + course.getCid() + " Course Name: " + course.getCname());
                List<Student> students = course.getStudents();
                for (Student student : students) {
                    System.out.println("\t:" + student.getSid() + "\t"
                            + student.getSname());
                }
            }
        } finally {
            session.close();
        }
    }

    /*
     * 获取所有学生并且根据学生获取对应的选修课程
     */
    public static void getAllStudentsAndCourses() {
        SqlSession session = sqlSessionFactory.openSession();
        try {
            StudentMapper studentMapper = session.getMapper(StudentMapper.class);
            List<Student> students = studentMapper.getAllStudents();
            for (Student student : students) {
                System.out.println("Student Id: " + student.getSid() + " Student Name: " + student.getSname());
                List<Course> courses = student.getCourses();
                for (Course course : courses) {
                    System.out.println("\t:" + course.getCid() + "\t"
                            + course.getCname());
                }
            }
        } finally {
            session.close();
        }
    }

    /*
     * 向选修关系表中添加数据
     */
    public static void AddStudentCourse() {
        StudentCourse studentCourse = new StudentCourse();
        studentCourse.setCid(1);
        studentCourse.setSid(2);
        SqlSession session = sqlSessionFactory.openSession();
        try {
            StudentCourseMapper studentCourseMaper = session
                    .getMapper(StudentCourseMapper.class);
            studentCourseMaper.insertStudentCourse(studentCourse);
            session.commit();
        } finally {
            session.close();
        }
    }

    /*
     * 添加学生
     */
    public static void AddStudent() {
        SqlSession session = sqlSessionFactory.openSession();
        try {
            Student student = new Student();
            student.setSname("Student-name-1");
            student.setMobile("13888888888");
            StudentMapper studentMaper = session.getMapper(StudentMapper.class);
            studentMaper.insertStudent(student);
            session.commit();
        } finally {
            session.close();
        }
    }

    /*
     * 添加课程
     */
    public static void AddCourse() {
        SqlSession session = sqlSessionFactory.openSession();
        try {
            Course course = new Course();
            course.setCname("课程-1");
            CourseMapper courseMapper = session.getMapper(CourseMapper.class);
            courseMapper.insertCourse(course);
            session.commit();
        } finally {
            session.close();
        }
    }
}
  • 运行结果
    可以看到结果是相对应的。


    getAllStudentsAndCourses()方法运行结果

    getAllCoursesAndStudents()方法运行结果
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 135,253评论 19 139
  • ORA-00001: 违反唯一约束条件 (.) 错误说明:当在唯一索引所对应的列上键入重复值时,会触发此异常。 O...
    我想起个好名字阅读 5,494评论 0 9
  • 优美妈妈在生优美的时候难产死了,优美可能知道妈妈死了出来的时候大哭,优美妈妈当时才二十八岁,优美爸爸叫国强优美出来...
    蓝天白云白雪公主阅读 295评论 5 0
  • 深夜, 你给我一百个幻觉, 我还给你一百个结局。 黑昼, 我有无数个问号, 而你却没有无数的句号给我。 夜色, 你...
    zjlovejz阅读 120评论 0 0
  • 写下这个标题,其实只是无奈!按说,垃圾不就是丢进垃圾桶么?还N种处理方式...... 当然,这道理似乎谁都...
    沙雅达人阅读 247评论 0 0