Mybatis注解开发指北

Mybatis注解开发指北


目录

文章目录

  • Mybatis注解开发指北
    • @[toc]
    • 0. Mybatis注解开发步骤
    • 1. 导入相关配置文件
    • 2. 配置数据库连接
    • 3. 创建数据库对应的实体类(entity)
    • 4. 创建实体类对应的Dao/Mapper实现增删改查
      • 4.1 Select查询
        • 4.1.1 立即加载EAGER和延迟加载LAZY
      • 4.2 Insert插入
      • 4.3Update更新
      • 4.4Delete删除
    • 5 创建相应的测试类
      • 5.1 使用junit创建测试类

0. Mybatis注解开发步骤

  1. 导入Mybatis、Mysql及相关辅助包
  2. 配置数据库连接
  3. 创建数据库对应的实体类(entity)
  4. 创建实体类对应的Dao/Mapper实现增删改查
  5. 创建相应的测试类

1. 导入相关配置文件

  • 用maven创建工程,在pom.xml中的dependencies中导入如下代码:
<dependency>
	<groupId>org.mybatisgroupId>
  <artifactId>mybatisartifactId>
  <version>3.5.3version>
dependency>
  • 同时可以导入mysql、log4j和junit用来测试和输出文档测试
<dependency>
  <groupId>log4jgroupId>
  <artifactId>log4jartifactId>
  <version>1.2.12version>
dependency>
<dependency>
  <groupId>mysqlgroupId>
  <artifactId>mysql-connector-javaartifactId>
  <version>8.0.19version>
dependency>
<dependency>
  <groupId>junitgroupId>
  <artifactId>junitartifactId>
  <version>4.12version>
  <scope>testscope>
dependency>
  • 在resources下新建log4j.properties文件将以下内容拷贝进去,用于输出日志等相关配置。
# Set root category priority to INFO and its only appender to CONSOLE.
#log4j.rootCategory=INFO, CONSOLE            debug   info   warn error fatal
log4j.rootCategory=debug, CONSOLE, LOGFILE

# Set the enterprise logger category to FATAL and its only appender to CONSOLE.
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE

# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n

# LOGFILE is set to be a File appender using a PatternLayout.
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=/axis.log
log4j.appender.LOGFILE.Append=true
log4j.appender.LOGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.LOGFILE.layout.ConversionPattern=%d{ISO8601} %-6r [%15.15t] %-5p %30.30c %x - %m\n



2. 配置数据库连接

  • 配置数据库连接是为了连接数据库以便进行CRUD操作

  • 配置数据库连接,在resources文件夹中新建SqlMapConfig.xml储存数据库相关配置,配置如下




<configuration>

    <typeAliases>
        <package name="com.database.mybatis.entity"/>
    typeAliases>


    <environments default="mysql">
        <environment id="mysql">

            <transactionManager type="JDBC">transactionManager>

            <dataSource type="POOLED">

              
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/test"/>
                <property name="username" value="root"/>
                <property name="password" value="qwer123"/>
            dataSource>
        environment>
    environments>



    <mappers>
        <package name="com.database.mybatis.dao">package>
    mappers>
configuration>
  • 类型别名的作用:

    • 类型别名的作用是为Java类型折翼短的名字,至于xml配置有关,可以减少类完全限定名的冗余。单个配置的时候可以是:
    <typeAliases>
      <typeAlias alias="Student" type="com.database.mybatis.entity.Student"/>
    typeAliases>
    
    • 也可以进行批量配置,在如下方法中配置别名,Mybatis可以在entity下搜索需要的Java Bean的瘦子米小写的非限定类名作为他的别名,比如com.database.mybatis.entity.Student的别名是student
    <typeAliases>
      <package name="com.database.mybatis.entity"/>
    typeAliases>
    

3. 创建数据库对应的实体类(entity)

  • 创建实体类的作用是为了将存放数据库中返回的数据,以便于进行相应的操作。

  • 创建实体类之前应先建立数据库相应的表,提供测试的数据库表有3个:Teacher、Class、Student。三个表是多对多的关系,EER图如下:

Mybatis注解开发指北_第1张图片

  • sql语句如下:
CREATE DATABASE test;

use test;

CREATE TABLE student(
	id INT PRIMARY KEY,
    teacher_id INT,
    class_id INT,
    student_nub BIGINT NOT NULL,
    student_name VARCHAR(20) NOT NULL,
    student_sex VARCHAR(20) DEFAULT '男',
    student_phone VARCHAR(20)
);

SELECT * FROM student;

INSERT INTO student VALUES(1,1,1,1800300101,'一班一号','男',NULL);

CREATE TABLE class(
	id INT PRIMARY KEY,
    teacher_id INT,
    student_id INT,
    class_nub BIGINT NOT NULL,
    class_name VARCHAR(20) NOT NULL,
    class_maxperson INT NOT NULL,
    class_haveperson INT NOT NULL,
    class_place INT NOT NULL
);

INSERT INTO class VALUES(1,1,1,300101,'三院一年级一号课',80,0,17101);

SELECT * FROM class;

CREATE TABLE teacher(
	id INT PRIMARY KEY,
    student_id INT,
    class_id INT,
    teacher_nub BIGINT NOT NULL,
    teacher_name VARCHAR(20) NOT NULL,
    teacher_sex VARCHAR(20) DEFAULT '男',
    teacher_phone VARCHAR(20)
);

INSERT INTO teacher VALUES(1,1,1,003001,'三院一号教师','男','13113311331');

SELECT * FROM teacher;

alter table student add foreign key(class_id) references class(id);
alter table student add foreign key(teacher_id) references teacher(id);
alter table teacher add foreign key(class_id) references class(id);
alter table teacher add foreign key(student_id) references student(id);
alter table class add foreign key(teacher_id) references teacher(id);
alter table class add foreign key(student_id) references student(id);
  • 创建实体类一般放入entiy包下,目录结构如下:Mybatis注解开发指北_第2张图片

  • 实体类创建时尽量遵循阿里巴巴java开发手册命名规则,养成良好习惯。数据库中表的字段类型需要与Java类型相对应,Java变量名以表字段名全称命名,例如表字段为student_id,则Java变量名应为StudentId。

  • sql变量类型与Java变量对照表如下:

Java类型 SQL类型
boolean BIT
byte TINYINT
short SMALLINT
int INTEGER
long BIGINT
String CHAR,VARCHAR,LONGVARCHAR
java.sql.Date DATE
java.sql.Time TIME
java.sql.Timestamp TIMESTAMP
  • Class实体类如下:
public class Class implements Serializable {
    private Integer id;

    private Integer teacherId;
    private Integer studentId;
    private BigInteger classNub;
    private String className;
    private Integer classMaxPerson;
    private Integer classHavePerson;
    private Integer classPlace;
}
  • 然后创建实体类对应的getter、setter和toString。完整的Class实体类如下:
package com.database.mybatis.entity;

import java.io.Serializable;
import java.math.BigInteger;
import java.util.List;

/**
 * @author linxi
 * @function
 * @project database
 * @package com.database.mybatis.model
 * @date 2020/5/8-1:37 下午
 */
public class Class implements Serializable {
    private Integer id;

    private Integer teacherId;
    private Integer studentId;
    private BigInteger classNub;
    private String className;
    private Integer classMaxPerson;
    private Integer classHavePerson;
    private Integer classPlace;

    @Override
    public String toString() {
        return "Class{" +
                "id=" + id +
                ", teacher_id=" + teacherId +
                ", student_id=" + studentId +
                ", class_nub=" + classNub +
                ", class_name='" + className + '\'' +
                ", class_maxperson=" + classMaxPerson +
                ", class_haveperson=" + classHavePerson +
                ", class_place=" + classPlace +
                '}';
    }

    public Integer getId() {
        return id;
    }

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

    public Integer getTeacherId() {
        return teacherId;
    }

    public void setTeacherId(Integer teacherId) {
        this.teacherId = teacherId;
    }

    public Integer getStudentId() {
        return studentId;
    }

    public void setStudentId(Integer studentId) {
        this.studentId = studentId;
    }

    public BigInteger getClassNub() {
        return classNub;
    }

    public void setClassNub(BigInteger classNub) {
        this.classNub = classNub;
    }

    public String getClassName() {
        return className;
    }

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

    public Integer getClassMaxPerson() {
        return classMaxPerson;
    }

    public void setClassMaxPerson(Integer classMaxPerson) {
        this.classMaxPerson = classMaxPerson;
    }

    public Integer getClassHavePerson() {
        return classHavePerson;
    }

    public void setClassHavePerson(Integer classHavePerson) {
        this.classHavePerson = classHavePerson;
    }

    public Integer getClassPlace() {
        return classPlace;
    }

    public void setClassPlace(Integer classPlace) {
        this.classPlace = classPlace;
    }
}

4. 创建实体类对应的Dao/Mapper实现增删改查

  • Dao和Mapper在注解开发中已经没有明显区别,所以不作区分。在Mybatis注解开发中,只要定义好对应的接口并加上合适的注解即可直接使用,不需要再编写Dao的具体实现类。
  • 因为我们实体类中的变量名与数据库表中的字段名不一致,所以我们需要任意一个用@Results注解使变量名与字段名进行统一,在再其他借口上使用@Resultmap()调用统一关系,代码如下。
/**
     * results的ID用于定义此对应的名称,在其他接口中直接使用@Resultmap("ID")来使用此对应关系,无需重写。
     * 表中主键需要在@Result()中的id属性设为true(默认为false)
     * @resylt() 的column属性为表中字段名,property为实体类变量名
     */
@Results(id = "classDao", value = {
  @Result(id = true,column = "id",property = "id"),
  @Result(column = "teacher_id",property = "teacherId"),
  @Result(column = "student_id",property = "studentId"),
  @Result(column = "class_nub",property = "classNub"),
  @Result(column = "class_name",property = "className"),
  @Result(column = "class_maxperson",property = "classMaxPerson"),
  @Result(column = "class_haveperson",property = "classHavePerson"),
  @Result(column = "class_place",property = "classPlace")
})

4.1 Select查询

  • 接口例子:

    • 查询Class所有内容,返回值为Class类型的List
    /**
         * results的ID用于定义此对应的名称,在其他接口中直接使用@Resultmap("ID")来使用此对应关系,无需重写。
         * 表中主键需要在@Result()中的id属性设为true(默认为false)
         * @resylt() 的column属性为表中字段名,property为实体类变量名
         */
        @Results(id = "classDao", value = {
                @Result(id = true,column = "id",property = "id"),
                @Result(column = "teacher_id",property = "teacherId"),
                @Result(column = "student_id",property = "studentId"),
                @Result(column = "class_nub",property = "classNub"),
                @Result(column = "class_name",property = "className"),
                @Result(column = "class_maxperson",property = "classMaxPerson"),
                @Result(column = "class_haveperson",property = "classHavePerson"),
                @Result(column = "class_place",property = "classPlace")
        })
    @Select("select * from class")
    List<Class> findAllClass();
    
    • 通过id查询
    @ResultMap("classDao")
    @Select("select * from class where id=#{id}")
    List<Class> findClassById(Integer classId);
    
    • 模糊查询

      • 方式一:
      @Select("select * from class where class_name like '%${value}%'")
      

      ​ 这种方式直接传送参数进来就可,不需要加%号。例如传送张三作为value即相当于:select * from class where class_name like %张三%。\

      例子:

      @ResultMap("classDao")
      @Select("select * from class where class_name like '%${value}%'")
      List<Class> findClassByClassName(String className);
      
      • 方式二:
      @Select("select * from class where class_name like #{username}")
      

      这种方式传送参数时需要带%,例如传送%张三%作为value即相当于:select * from class where class_name like %张三%。

      例子:

      		@ResultMap("classDao")
          @Select("select * from class where class_name like #{username}")
          List<Class> findClassByClassName(String className);
      
    • 多表查询:多表对应关系有两种:一对一和一对多。

      • 一对一:假设有表A,B;A表中有字段id和b_id,b_id对应B表的id(id为唯一值),所以A表的每一列都有唯一一个B与其对应,则此关系为一对一。一对一关系需要用@one注解映射。

        • 假设查询某节课及其对应的老师,因为每个课程只有一个老师,所以关系为一对一。
        • 首先需要在Class实体类中建立Teacher类型变量用于存储老师信息,并建立相应的gettter和setter。
        private Teacher teacher;
        
        public Teacher getTeacher() {
          return teacher;
        }
        
        public void setTeacher(Teacher teacher) {
          this.teacher = teacher;
        }
        
        • 第二步,需要在TeacherDao中建立通过classID查询teacher的接口。
        @Select("select * from teacher where class_id=#{class_id}")
        Teacher findTeacherByClassId(Integer classId);
        
        • 第三步,在ClassDao中建立通过classId查询课程及其对应老师的接口。实质上是查询class但是class实体类中的Teacher中要包含课程对应的老师。多表查询的@one注解是属于@result的参数。

        Mybatis注解开发指北_第3张图片

        @Results(value = {
          @Result(id = true, column = "id", property = "id"),
          @Result(column = "teacher_id", property = "teacherId"),
          @Result(column = "student_id", property = "studentId"),
          @Result(column = "class_nub", property = "classNub"),
          @Result(column = "class_name", property = "className"),
          @Result(column = "class_maxperson", property = "classMaxPerson"),
          @Result(column = "class_haveperson", property = "classHavePerson"),
          @Result(column = "class_place", property = "classPlace"),
        
          @Result(property = "teacher", column = "teacherId",
                  one = @One(select =
                             "com.database.mybatis.dao.TeacherDao.findTeacherByClassId",
                             fetchType = FetchType.EAGER))
        })
        @Select("select * from class where id = #{id}")
        Class findClassAndTeacherByClassId(Integer classId);
        
      • 一对多:假设有表A,B;A表中有字段id和b_room,b_room对应B表的room(room不唯一),所以A表的每一列都有多个B与其对应,则此关系为一对多。一对多关系需要用@many注解映射

        • 假设查询上某节课的所有学生,因为一个课程有多名学生,所以关系为一对多。
        • 首先需要在Class实体类中建立List类型变量用于存储老师信息,并建立相应的gettter和setter。
        private List<Student> students;
        
        public List<Student> getStudents() {
          return students;
        }
        
        public void setStudents(List<Student> students) {
          this.students = students;
        }
        
        • 第二步,需要在StudentDao中建立通过classID查询student的接口。
        @Select("select * from student where class_id=#{class_id}")
        List<Student> findStudentByClassId(Integer classId);
        
        • 第三步,在ClassDao中建立通过classId查询课程及其对应学生的接口。实质上是查询class但是class实体类中List中要包含课程对应的学生。多表查询的@many注解是属于@result的参数。
        @Select("select * from class where id=#{id}")
        @Results(value = {
          @Result(id = true,column = "id",property = "id"),
          @Result(column = "teacher_id",property = "teacherId"),
          @Result(column = "student_id",property = "studentId"),
          @Result(column = "class_nub",property = "classNub"),
          @Result(column = "class_name",property = "className"),
          @Result(column = "class_maxperson",property = "classMaxPerson"),
          @Result(column = "class_haveperson",property = "classHavePerson"),
          @Result(column = "class_place",property = "classPlace"),
        
          @Result(property = "students", column = "id",
                  many = @Many(select =
                               "com.database.mybatis.dao.StudentDao.findStudentByClassId",
                               fetchType = FetchType.LAZY))
        })
        List<Class> findClassAndStudentsByClassId(Integer classId);
        

4.1.1 立即加载EAGER和延迟加载LAZY

  • 延迟加载和立即加载的区别:
    • 顾名思义,立即加载就是在sql语句执行的时候直接从数据库取出数据,而延迟加载就是虽然sql语句执行,但是只有在用到相应的数据的时候才从数据库加载出来。
  • 什么时候用延迟加载和立即加载:
    • 延迟加载多用于查询一对多的时候,立即加载多用于查询一对一的时候,但并不绝对。
  • 为什么要用延迟加载:
    • 使用延迟加载的目的是为了减少系统资源的消耗,例如查询课程和选择此课程的学生的时候,当一个课程中只有几十个学生的时候,我们使用延迟加载和立即加载对系统资源的消耗并不明显,但是当一个课程有几千万个学生的时候,同时我们并不是立刻使用所有学生的信息。我们如果使用立即加载就会造成大量浪费系统资源,此时使用延迟加载的随用随取模式就会有很大的优势。

4.2 Insert插入

  • 插入数据到表中要使用@Insert注解

例子:

@ResultMap("classDao")
@Insert("insert into class(id,teacher_id,student_id,class_nub,class_name,class_maxperson,class_haveperson,class_place) values(#{id},#{teacher_id},#{student_id},#{class_nub},#{class_name},#{class_maxperson},#{class_haveperson},#{class_place})")
void insertClass(Class aClass);

此中的所有字段都在Class实体类中,只要在#{}中填入对应的字段名或者别名并将Class作为参数传入,Mybatis就会在Class中自动查找对应的关系。


4.3Update更新

  • 更新数据到表中要使用@Update注解

例子:

@ResultMap("classDao")
@Update("update class set teacher_id=#{teacher_id},student_id=#{student_id},class_nub=#{class_nub},class_name=#{class_name},class_maxperson=#{class_maxperson},class_haveperson=#{class_haveperson},class_place=#{class_place} where id=#{id}")
void updateClassById(Class aClass);

4.4Delete删除

  • 从表中删除数据使用@Delete注解

例子:

@ResultMap("classDao")
@Delete("delete from class where id=#{id}")
void deleteClassById(Integer classId);

5 创建相应的测试类

  • text.java下创建测试类,测试类的目录结构最好与项目结构相同。所以我们在text.java.com.database.mybatis下创建测试类testClass.java

Mybatis注解开发指北_第4张图片

  • 想要能够使用我们创建好的接口操作数据库需要如下几个步骤:
    1. 读取数据库配置文件:用于连接数据库及其相关配置
    2. 使用建造者模式创建SqlSessionFactory工厂:使用建造者模式可以隐藏创建工厂的细节,直接将我们需要的工厂的“图纸”传入即可建造出我们想要的工厂。
    3. 使用工厂模式创建SqlSession对象:使用工厂模式创建SqlSession对象可以解耦,可以在不改变源码的方式进行操作
    4. 使用SqlSession创建Dao接口的代理对象执行方法:在不修改源码的基础上对已有方法增强,可是已实现不写Dao的实现类,就可以实现功能。
    5. 释放资源

例子:

//读取数据库配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//建造者模式创建SqlSessionFactory工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//使用工厂模式生产SqlSession对象
SqlSession session = factory.openSession();
//使用SqlSession创建Dao接口的代理对象执行方法
ClassDao classDao = session.getMapper(ClassDao.class);
//使用代理对象执行方法
List<Class> aClass = classDao.findAll();
for (Class aClass1 : aClass) {
  System.out.println(aClass1);
  System.out.println(aClass1.getStudents());
}      
  • 但是在实际生产环境中我们可以更简单的进行操作:
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml").getMapper(ClassDao.class);

List<Class> aClass = classDao.findAll();
for (Class aClass1 : aClass) {
  System.out.println(aClass1);
  System.out.println(aClass1.getStudents());
}   

5.1 使用junit创建测试类

  • 创建TestClass.java
  • 在进行测试前我们需要进行上述配置拿到Dao的代理对象
public class TestClass {

  private InputStream in;
  private SqlSessionFactory factory;
  private SqlSession session;
  private ClassDao classDao;

  @Before
  public void init() throws IOException {
    in = Resources.getResourceAsStream("SqlMapConfig.xml");
    factory = new SqlSessionFactoryBuilder().build(in);
    session = factory.openSession();
    classDao = session.getMapper(ClassDao.class);
  }
  • 在测试结束后我们需要释放资源,所以增加如下方法:
@After
public void destroy() throws IOException {
  session.commit();
  session.close();
  in.close();
}
  • 以通过id查询课程为例创建测试类。
    1. 创建testFindClassById方法。
    2. 通过代理对象调用findClassById方法并传入参数。
    3. 打印查询结果。
@Test
public void testFindClassById(){
  List<Class> classes = classDao.findClassById(1);
  for (Class aClass : classes) {
    System.out.println(aClass);
  }
}

你可能感兴趣的:(Mybatis,mysql,java)