草庐IT

MyBatis(十六):多对一的处理

jmsstudy 2023-04-16 原文

现在我们就开始更加深入的学习了,今天我们要学习的是多对一的处理。

在正式开始之前我们需要做一些准备工作。

一、在数据库建立两张新的表并插入数据

CREATE TABLE `teacher` (
  `id` INT(10) NOT NULL,
  `name` VARCHAR(30) DEFAULT NULL,
  PRIMARY KEY(`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8

INSERT INTO `teacher`(`id`, `name`) VALUES (1, '刘老师');

CREATE TABLE `student` (
  `id` INT(10) NOT NULL,
  `name` VARCHAR(30) DEFAULT NULL,
  `tid` INT(10) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `fktid` (`tid`),
  CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8

INSERT INTO `student` (`id`,`name`,`tid`) VALUES (1,'小赵','1');
INSERT INTO `student` (`id`,`name`,`tid`) VALUES (2,'小钱','1');
INSERT INTO `student` (`id`,`name`,`tid`) VALUES (3,'小孙','1');
INSERT INTO `student` (`id`,`name`,`tid`) VALUES (4,'小李','1');
INSERT INTO `student` (`id`,`name`,`tid`) VALUES (5,'小周','1');

上述SQL语句建立了一个teacher表,一个student表,student表的tid与teacher表的id相关联。

二、建立一个新的项目

我们建立一个全新的子项目

1.建立MyBatis的核心配置文件mybatis-config.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>

    <properties resource="db.properties" />

    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
    
    <typeAliases>
        <package name="com.jms.pojo"/>
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
</configuration>

2.建立db.properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/MyBaties?useSSL=true&useUnicode=true&characterEncoding=UTF-8
username=root
password=123456

3.建立MyBatisUtil工具类

package com.jms.utils;

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.IOException;
import java.io.InputStream;

//SqlSessionFactory-->SqlSession
public class MyBatisUtil {

    private static SqlSessionFactory sqlSessionFactory;

    //获取SqlSessionFactory对象
    static {
        try {
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //通过SqlSessionFactory获取SqlSession对象,其中包含了面向数据库执行执行SQL命令所需要的方法
    public static SqlSession getSqlSession() {
        return sqlSessionFactory.openSession(true);
    }
}

4.建立实体类

Student

package com.jms.pojo;

public class Student {
    private int id;
    private String name;
    private Teacher teacher;

    public Student() {
    }

    public Student(int id, String name, Teacher teacher) {
        this.id = id;
        this.name = name;
        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 Teacher getTeacher() {
        return teacher;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", teacher=" + teacher +
                '}';
    }
}

Teacher

package com.jms.pojo;

public class Teacher {
    private int id;
    private String name;

    public Teacher(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public 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;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

5.建立Mapper接口

StudentMapper

TeacherMapper

6.建立Mapper.xml配置文件

StudentMapper.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">
<mapper namespace="com.jms.dao.StudentMapper">

</mapper>

TeacherMapper.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">
<mapper namespace="com.jms.dao.TeacherMapper">

</mapper>

6.在核心配置文件mybatis-config.xml中建立映射

    <mappers>
        <mapper resource="com/jms/dao/TeacherMapper.xml"/>
        <mapper resource="com/jms/dao/StudentMapper.xml"/>
    </mappers>

 7.测试

(1)在接口中写一个方法

package com.jms.dao;

import com.jms.pojo.Teacher;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface TeacherMapper {

    List<Teacher> getTeacherList();
}

(2)在Mapper.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接口 -->
<mapper namespace="com.jms.dao.TeacherMapper">
    <select id="getTeacherList" resultType="teacher">
        select * from mybaties.teacher
    </select>
</mapper>

(3)junit测试

import com.jms.dao.TeacherMapper;
import com.jms.pojo.Teacher;
import com.jms.utils.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class MapperTest {
    @Test
    public void test() {
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
        TeacherMapper teacherMapper = sqlSession.getMapper(TeacherMapper.class);
        List<Teacher> teacherList = teacherMapper.getTeacherList();
        for (Teacher teacher : teacherList) {
            System.out.println(teacher);
        }
     sqlSession.close(); } }

 

 

测试结果没有问题。

至此,我们的数据库和项目都搭建完成。

接下来就进行多对一处理的实现。

三、多对一处理的实现

首先我们要清楚我们要做什么?

我们要查询所有的student信息。查询所有student信息不是很简单吗?一般来说确实很简答,但是我们Student类中有一个属性是Teacher对象。Teacher对象还有着自己的id和name。所以我们要查的是student表中的id和name,以及它们的tid所对应的teacher表中的id和name。

我们先利用SQL来查询看看。

select s.id,s.name,t.id,t.name
from student as s,teacher as t
where s.tid = t.id

上表即我们想要得到的结构。

那么在MyBatis中我们如何去实现呢?

我们有两种方法。

1.按照结果嵌套处理

顾名思义,就是先通过查询得到结果,把结果中的四个列对应给Student类的三个属性,最后两列对应给Teacher对象的两个属性。

由于student表和teacher中的id字段和name字段名字相同,所以我们在MyBatis中查询的时候应该给它起别名(不同的话就没有必要了)。

(1)在StudentMapper接口中声明方法

package com.jms.dao;

import com.jms.pojo.Student;

import java.util.List;

public interface StudentMapper {

    List<Student> getStudentList();
}

(2)在StudentMapper.xml中实现接口中的方法

    <!--按照结果嵌套处理-->
    <resultMap id="StudentAndTeacher" type="Student">
        <result property="id" column="sid"/>
        <result property="name" column="sname"/>
        <!--复杂的属性要单独处理-->
        <association property="teacher" javaType="Teacher">
            <result property="id" column="tid"/>
            <result property="name" column="tname"/>
        </association>
    </resultMap>

    <select id="getStudentList" resultMap="StudentAndTeacher">
        select s.id as sid,s.name as sname,t.id as tid,t.name as tname
        from mybaties.student as s,mybaties.teacher as t
        where s.tid = t.id
    </select>

我们看上面的配置,还是常规的select语句和结果映射,唯一有变化的就是结果映射中多了一个对于复杂属性的处理。

官方文档中是这样说明的:

association – 一个复杂类型的关联;许多结果将包装成这种类型

  嵌套结果映射 – 关联可以是 resultMap 元素,或是对其它结果映射的引用

javaType 一个 Java 类的全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)。 如果你映射到一个 JavaBean,MyBatis 通常可以推断类型。然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。

(3)junit测试

import com.jms.dao.StudentMapper;
import com.jms.dao.TeacherMapper;
import com.jms.pojo.Student;
import com.jms.pojo.Teacher;
import com.jms.utils.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class MapperTest {

    @Test
    public void getStudents() {
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
        StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
        List<Student> studentList = studentMapper.getStudentList();
        for (Student student : studentList) {
            System.out.println(student);
        }
     sqlSession.close(); } }

测试结果如下:

 成功得到了想要的结果。

2.按照查询进行嵌套处理

顾名思义,就是查询中嵌套着查询。这个我们要实现,很明显我们需要先对student表进行查询,将结果中的tid作为第二个查询的条件来查询teacher表。

(1)在StudentMapper接口声明方法

List<Student> getStudentList2();

(2)在StudentMapper.xml中实现接口中的方法

    <resultMap id="StudentAndTeacher2" type="Student">
        <!--此处的id和name的映射是可以省略的,写出来只是为了方便理解-->
        <result property="id" column="id"/>
        <result property="name" column="name"/>
        <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
    </resultMap>

    <select id="getStudentList2" resultMap="StudentAndTeacher2">
        select * from mybaties.student
    </select>

    <select id="getTeacher" resultType="Teacher">
        select * from mybaties.teacher where id=#{tid}
    </select>

上面的id和name在隐性映射中已经存在,可以不写这两句,写出来是为了方便理解。

这种方法相比第一种可能难理解一些,但也不是很难。将tid列与teacher属性进行对应,然后嵌套一个查询,这个查询返回的是一个Teacher类型,刚好把查询结果返给对应的teacher属性。

这里令我感到意外的是select的id,竟然还能作为被引用的对象,于是我看了一下官方文档的内容:

Select 元素的属性
属性描述
id

在命名空间中唯一的标识符,可以被用来引用这条语句。

select的id在命名空间的标识符就是说去接口中找方法名,同样说到可以被引用,嗯,学习了。

(3)junit测试

 

 同样得到了结果。

 

(本文仅作个人学习记录用,如有纰漏敬请指正)

 

有关MyBatis(十六):多对一的处理的更多相关文章

  1. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  2. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  3. Ruby-vips 图像处理库。有什么好的使用示例吗? - 2

    我对图像处理完全陌生。我对JPEG内部是什么以及它是如何工作一无所知。我想知道,是否可以在某处找到执行以下简单操作的ruby​​代码:打开jpeg文件。遍历每个像素并将其颜色设置为fx绿色。将结果写入另一个文件。我对如何使用ruby​​-vips库实现这一点特别感兴趣https://github.com/ender672/ruby-vips我的目标-学习如何使用ruby​​-vips执行基本的图像处理操作(Gamma校正、亮度、色调……)任何指向比“helloworld”更复杂的工作示例的链接——比如ruby​​-vips的github页面上的链接,我们将不胜感激!如果有ruby​​-

  4. ruby - Faye WebSocket,关闭处理程序被触发后重新连接到套接字 - 2

    我有一个super简单的脚本,它几乎包含了FayeWebSocketGitHub页面上用于处理关闭连接的内容:ws=Faye::WebSocket::Client.new(url,nil,:headers=>headers)ws.on:opendo|event|p[:open]#sendpingcommand#sendtestcommand#ws.send({command:'test'}.to_json)endws.on:messagedo|event|#hereistheentrypointfordatacomingfromtheserver.pJSON.parse(event.d

  5. ruby - 如何使用 Ruby HTTP::Net 处理 404 错误? - 2

    我正在尝试解析网页,但有时会收到404错误。这是我用来获取网页的代码:result=Net::HTTP::getURI.parse(URI.escape(url))如何测试result是否为404错误代码? 最佳答案 像这样重写你的代码:uri=URI.parse(url)result=Net::HTTP.start(uri.host,uri.port){|http|http.get(uri.path)}putsresult.codeputsresult.body这将打印状态码和正文。

  6. ruby-on-rails - 使用 Ruby 正确处理 Stripe 错误和异常以实现一次性收费 - 2

    我查看了Stripedocumentationonerrors,但我仍然无法正确处理/重定向这些错误。基本上无论发生什么,我都希望他们返回到edit操作(通过edit_profile_path)并向他们显示一条消息(无论成功与否)。我在edit操作上有一个表单,它可以POST到update操作。使用有效的信用卡可以正常工作(费用在Stripe仪表板中)。我正在使用Stripe.js。classExtrasController5000,#amountincents:currency=>"usd",:card=>token,:description=>current_user.email)

  7. ruby-on-rails - Rails 处理 .Erb 与 Nils - 2

    当profile为nil时,总是让我感到悲伤...我该怎么办? 最佳答案 在View中使用变量之前,始终检查变量是否为nil。我确信这个问题有更优雅的解决方案,但这应该能让您入门。 关于ruby-on-rails-Rails处理.Erb与Nils,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/2709605/

  8. ruby-on-rails - 如何在多个环境中处理 OmniAuth 回调? - 2

    我有一个应用程序专门使用Facebook作为身份验证提供程序,并正确设置了生产模式的回调。为了让它工作,您需要为您的Facebook应用程序提供一个站点URL和一个用于回调的站点域,在我的例子中是http://appname.heroku.com和appname。heroku.com分别。问题是我的Controller设置为只允许经过身份验证的session,所以我无法在开发模式下查看我的应用程序,因为Facebook应用程序的域显然没有设置为本地主机。如何在不更改Facebook设置的情况下解决这个问题? 最佳答案 创建另一个域l

  9. python - 请在 Perl 或 Ruby 中引入多处理库 - 2

    在python中,我们可以使用多处理模块。如果Perl和Ruby中有类似的库,你会教它吗?如果您能附上一个简短的示例,我将不胜感激。 最佳答案 ruby:WorkingwithmultipleprocessesinRubyConcurrencyisaMythinRubyPerl:HarnessingthepowerofmulticoreWhyPerlIsaGreatLanguageforConcurrentProgramming此外,Perl的线程是native操作系统线程,因此您可以使用它们来利用多核。

  10. ruby - 现代计算机的功能是否不足以处理字符串而无需使用符号(在 Ruby 中) - 2

    我读过的关于Ruby符号的每一篇文章都在谈论符号相对于字符串的效率。但是,这不是1970年代。我的电脑可以处理一些额外的垃圾收集。我错了吗?我拥有最新最好的奔腾双核处理器和4GBRAM。我认为这应该足以处理一些字符串。 最佳答案 您的计算机可能能够处理“一点点额外的垃圾收集”,但是当“一点点”发生在运行数百万次的内部循环中时呢?如果它在内存有限的嵌入式系统上运行呢?有很多地方你可以随意使用字符串,但在某些地方你不能。这完全取决于上下文。 关于ruby-现代计算机的功能是否不足以处理字符串

随机推荐