草庐IT

MyBatis注解开发---实现自定义映射关系和关联查询

会洗碗的CV工程师 2023-07-05 原文

目录

一、使用注解实现自定义映射关系

1. 编写注解方法

2. 编写测试方法

3. 查看运行结果

二、使用注解实现一对一关联查询

1. 编写注解方法

2. 编写测试方法

3. 查看运行结果

三、使用注解实现一对多关联查询

1. 编写注解方法

2. 编写测试方法

3. 查看运行结果

四、注解文件和映射文件开发对比


一、使用注解实现自定义映射关系

        当POJO属性名与数据库列名不一致时,需要自定义实体类和结果集的映射关系,在MyBatis注解开发中,使用 @Results 定义并使用自定义映射,使用 @ResultMap 使用自定义映射,用法如下:

        前戏:为了体验这个效果,我们可以修改一下User实体类代码,如下

package com.example.pojo;

import java.io.Serializable;

public class User implements Serializable {
    private int id;
    private String username1;
    private String sex1;
    private String address1;

    public User(String programmer, String man, String shangHai) {
        this.username1 = programmer;
        this.sex1 = man;
        this.address1 = shangHai;
    }

    public User(int i, String programmer_1, String woman, String shenzhen) {
        this.id = i;
        this.username1 = programmer_1;
        this.sex1 = woman;
        this.address1 = shenzhen;
    }

    public User() {

    }
 
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername1() {
        return username1;
    }

    public void setUsername1(String username1) {
        this.username1 = username1;
    }

    public String getSex1() {
        return sex1;
    }

    public void setSex1(String sex1) {
        this.sex1 = sex1;
    }

    public String getAddress1() {
        return address1;
    }

    public void setAddress1(String address1) {
        this.address1 = address1;
    }

    @Override
    public String toString() {
        return "User[ " +
                "id=" + id +
                ", username1='" + username1 + '\'' +
                ", sex1='" + sex1 + '\'' +
                ", address1='" + address1 + '\'' +
                " ]";
    }
}

1. 编写注解方法

    // 查询所有用户
    @Results(id="userDiyMapper",value = {
            @Result(id = true,property = "id",column = "id"),
            @Result(property = "username1",column = "username"),
            @Result(property = "sex1",column = "sex"),
            @Result(property = "address1",column = "address")
    })
    @Select("select * from user")
    List<User> findAll1();

    // 根据id查询
    @ResultMap("userDiyMapper")
    @Select("select * from user where id = #{id}")
    User findById(int id);

注意啊:这里property对应的是实体类属性名,column对应的就是数据库表的列名 

2. 编写测试方法

    @Test
    public void testFindAll1(){
        List<User> all = userMapper.findAll1();
        all.forEach(System.out::println);
        System.out.println("---------------------");
        System.out.println(userMapper.findById(5));
    }

        看看能否查询出所有用户和id为5的用户,并且留意对应的属性名 

3. 查看运行结果

        OK,看来都是符合我们的预期的。 

二、使用注解实现一对一关联查询

        在MyBatis的注解开发中对于多表查询只支持分解查询,不支持连接查询。

        这里我们采用学生表和班级表做对比,所以我们先新建Student实体类和Classes实体类

Classes实体类 

package com.example.pojo;

import java.util.List;

public class Classes {
    private int cid;
    private String className;
    private List<Student> studentList;

    public int getCid() {
        return cid;
    }

    public void setCid(int cid) {
        this.cid = cid;
    }

    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }

    public List<Student> getStudentList() {
        return studentList;
    }

    public void setStudentList(List<Student> studentList) {
        this.studentList = studentList;
    }

    @Override
    public String toString() {
        return "Classes[ " +
                "cid=" + cid +
                ", className='" + className + '\'' +
                ", studentList=" + studentList + 
                " ]";
    }
}

Student实体类

package com.example.pojo;

public class Student {
    private int sid;
    private String name;
    private int age;
    private String sex;
    private Classes classes;

    public int getSid() {
        return sid;
    }

    public void setSid(int sid) {
        this.sid = sid;
    }

    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 String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Classes getClasses() {
        return classes;
    }

    public void setClasses(Classes classes) {
        this.classes = classes;
    }

    @Override
    public String toString() {
        return "Student[ " +
                "sid=" + sid +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                ", classes=" + classes +
                " ]";
    }
}

1. 编写注解方法

StudentMapper添加查询所有用户

package com.example.mapper;

import com.example.pojo.Student;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.mapping.FetchType;

import java.util.List;

public interface StudentMapper {
    @Select("select * from student")
    // 自定义映射关系
    @Results(id = "studentMapper",value = {
            @Result(id = true,property = "sid",column = "sid"),
            @Result(property = "name",column = "name"),
            @Result(property = "age",column = "age"),
            @Result(property = "sex",column = "sex"),
            /**
             * property:属性名
             * column:调用从表方法时传入的参数列
             * one:表示该属性是一个对象
             * select:调用的是从表方法
             * fetchType:加载方式
             */
            @Result(property = "classes",column = "classId",
                one = @One(select = "com.example.mapper.ClassesMapper.findByCid",
                        fetchType = FetchType.EAGER)
                )
    })
    List<Student> findAll();
}

ClassesMapper添加根据id查询班级

package com.example.mapper;

import com.example.pojo.Classes;
import org.apache.ibatis.annotations.Many;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.mapping.FetchType;

import java.util.List;

public interface ClassesMapper {
    // 根据id查询班级
    @Select("select * from classes where cid = #{cid}")
    Classes findByCid(Integer id);
}

2. 编写测试方法

// 测试一对一查询
    @Test
    public void testFindAllStudent(){
        StudentMapper studentMapper = session.getMapper(StudentMapper.class);
        List<Student> all = studentMapper.findAll();
        all.forEach(System.out::println);
    }

        看看能否查询出学生对应的班级,如果可以则查询成功 

3. 查看运行结果

        OK,看图我们是已经成功查询出每个学生对应的班级的 

三、使用注解实现一对多关联查询

        在这里我们主要实现查询所有班级的时候把对应的学生列表也查询出来。

1. 编写注解方法

StudentMapper添加根据班级id查询学生

// 根据班级Id查询学生
    @Select("select * from student where ClassId = #{classId}")
    List<Student> findByClassId(int classId);

Classes添加查询所有班级

// 查询所有班级
    @Select("select * from classes")
    @Results(id = "classMapper",value = {
            @Result(id = true,property = "cid",column = "cid"),
            @Result(property = "className",column = "className"),
            // many:表示该属性是一个集合
            @Result(property = "studentList",column = "cid",
            many = @Many(select = "com.example.mapper.StudentMapper.findByClassId",
                        fetchType = FetchType.LAZY))
    })
    List<Classes> findAll();

2. 编写测试方法

// 测试一对多查询
    @Test
    public void findAllClasses(){
        ClassesMapper classesMapper = session.getMapper(ClassesMapper.class);
        List<Classes> all = classesMapper.findAll();
        all.forEach(System.out::println);
    }

        观察能否查询出班级对应的学生列表

3. 查看运行结果

OK,确实也是可以查询出来了的。 

四、注解文件和映射文件开发对比

注解开发更快,映射文件更方便。

MyBatis中更推荐使用映射文件开发,SpringSpringBoot更推荐注解方式。具体使用要视项目情况而定。它们的优点对比如下:

映射文件:

  • 代码与Sql语句是解耦的,修改时只需修改配置文件,无需修改源码。
  • Sql语句集中,利于快速了解和维护项目。
  • 级联查询支持连接查询和分解查询两种方式,注解开发只支持分解查询。

注解:

  • 配置简单,开发效率高。
  • 类型安全,在编译期即可进行校验,不用等到运行时才发现错误。

我个人也是比较喜欢映射文件开发,主要更喜欢方便维护和了解。

有关MyBatis注解开发---实现自定义映射关系和关联查询的更多相关文章

  1. ruby - Facter::Util::Uptime:Module 的未定义方法 get_uptime (NoMethodError) - 2

    我正在尝试设置一个puppet节点,但ruby​​gems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由ruby​​gems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby

  2. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

  3. ruby - 使用 C 扩展开发 ruby​​gem 时,如何使用 Rspec 在本地进行测试? - 2

    我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当

  4. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

    我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

  5. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

    我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

  6. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  7. Ruby Sinatra 配置用于生产和开发 - 2

    我已经在Sinatra上创建了应用程序,它代表了一个简单的API。我想在生产和开发上进行部署。我想在部署时选择,是开发还是生产,一些方法的逻辑应该改变,这取决于部署类型。是否有任何想法,如何完成以及解决此问题的一些示例。例子:我有代码get'/api/test'doreturn"Itisdev"end但是在部署到生产环境之后我想在运行/api/test之后看到ItisPROD如何实现? 最佳答案 根据SinatraDocumentation:EnvironmentscanbesetthroughtheRACK_ENVenvironm

  8. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

  9. ruby - 定义方法参数的条件 - 2

    我有一个只接受一个参数的方法:defmy_method(number)end如果使用number调用方法,我该如何引发错误??通常,我如何定义方法参数的条件?比如我想在调用的时候报错:my_method(1) 最佳答案 您可以添加guard在函数的开头,如果参数无效则引发异常。例如:defmy_method(number)failArgumentError,"Inputshouldbegreaterthanorequalto2"ifnumbereputse.messageend#=>Inputshouldbegreaterthano

  10. ruby - 如何在 Grape 中定义哈希数组? - 2

    我使用Ember作为我的前端和GrapeAPI来为我的API提供服务。前端发送类似:{"service"=>{"name"=>"Name","duration"=>"30","user"=>nil,"organization"=>"org","category"=>nil,"description"=>"description","disabled"=>true,"color"=>nil,"availabilities"=>[{"day"=>"Saturday","enabled"=>false,"timeSlots"=>[{"startAt"=>"09:00AM","endAt"=>

随机推荐