荐 mybatis之注解开发

木河以北 9天前   阅读数 7 0

mybatis注解开发

环境搭建

新建maven工程

先看下完整的测试工程结构

工程结构说明

  • dao包:和数据库交互的接口(增删改查)
  • entity包:实体定义
  • jdbcConfig.properties:jdbc连接的配置文件
  • SqlMapperConfig.xml:mybatis的配置文件

pom.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>mybatis_annotaion</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!-- 打包方式 -->
   <packaging>jar</packaging>

    <!-- 所有依赖-->
    <dependencies>
        <!-- mybatis依赖 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.4</version>
        </dependency>

        <!-- mysql依赖 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.16</version>
        </dependency>

        <!-- 调试工具依赖 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
            <scope>test</scope>
        </dependency>

    </dependencies>

</project>

mybatsi配置

<?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>

    <!-- 引入jdbc配置文件 -->
    <properties resource="com/jdbcConfig.properties"></properties>

    <!-- 环境:配置mybatis的环境 -->
    <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://192.168.1.8:3306/studenms?serverTimezone=UTC"/> <property name="username" value="root"/> <property name="password" value="sa"/> -->
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <package name="com.dao"/>
    </mappers>
</configuration>

这里采用引入jdbc配置文件,也可直接配置,即dataSource节点中注释部分。和dao接口的映射,由于是采用注解开发,因此用package映射。jdbc的配置文件jdbcConfig.properties内如下:

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://192.168.1.8:3306/studenms?serverTimezone=UTC
jdbc.username=root
jdbc.password=sa

注意等号后边的字符串不要加双引号,加了双引号之后,在解析配置时候,会将双引号也解析出来,导致无法连接jdbc


查询实现

数据表说明

  • t_user 用户表

  • t_score 分数表


实体定义


用户信息实体

package com.entity;

import java.util.List;

public class UserEntity {
    private int id;
    private String user;
    private int role;
    private String name;
    private String email;
    private String pwd;

    public int getId() {
        return id;
    }

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

    public String getUser() {
        return user;
    }

    public void setUser(String user) {
        this.user = user;
    }

    public int getRole() {
        return role;
    }

    public void setRole(int role) {
        this.role = role;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }


    @Override
    public String toString() {
        return "UserEntity{" +
                "id=" + id +
                ", user='" + user + '\'' +
                ", role=" + role +
                ", name='" + name + '\'' +
                ", email='" + email + '\'' +
                ", pwd='" + pwd + '\'' +
                '}';
    }
}


分数实体

package com.entity;

public class ScoreEntity {
    private int uId;
    private int id;
    private int scores;

    public int getuId() {
        return uId;
    }

    public void setuId(int uId) {
        this.uId = uId;
    }

    public int getId() {
        return id;
    }

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

    public int getScores() {
        return scores;
    }

    public void setScores(int scores) {
        this.scores = scores;
    }

    @Override
    public String toString() {
        return "Score{" +
                "uId=" + uId +
                ", id=" + id +
                ", scores=" + scores +
                '}';
    }
}


dao接口

package com.dao;

import com.entity.UserEntity;
import java.util.List;

public interface IUserDao {

    @Select("select * from t_user")
    List<UserEntity> findAll();

    @Select("select * from t_user where id=#{id}")
    UserEntity findById(Integer id);
}

package com.dao;

import com.entity.ScoreEntity;

import java.util.List;

public interface IScoreDao {

    @Select("select * from t_score")
    List<ScoreEntity> findAll();

    @Select("select * from t_score where id=#{id}")
    ScoreEntity findById();

    @Select("select * from t_score where id=#{id}")
    List<ScoreEntity> findByUserId(Integer userId);
}


测试程序

import com.dao.IScoreDao;
import com.dao.IUserDao;
import com.entity.ScoreEntity;
import com.entity.UserEntity;
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;
import java.util.List;

public class AnnotationTest {
    public static void main(String[] args) throws IOException {
// 读取配置文件
        InputStream stream = Resources.getResourceAsStream("com/SqlMapperConfig.xml");
// 使用SQLSession工厂构建器,创建SQLSession
        SqlSessionFactoryBuilder factoryBuilder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = factoryBuilder.build(stream);
// 获取SQLSession对象
        SqlSession session = factory.openSession();
// 获取接口的代理对象(不用实现接口)

        //region 获取用户数据
        IUserDao userDao = session.getMapper(IUserDao.class);
        List<UserEntity> userList = userDao.findAll();
        for (UserEntity userEntity : userList) {
            System.out.println("=====每个用户数据=====");
            System.out.println(userEntity);

        }
        //endregion

        //region 获取分数数据
// IScoreDao scoreDao = session.getMapper(IScoreDao.class);
// List<ScoreEntity> scoreList = scoreDao.findByUserId(123);
// for (ScoreEntity scoreEntity : scoreList) {
// System.out.println("===每类课程数据====");
// System.out.println(scoreEntity);
// }
        //endregion
    }
}

执行获取用户数据代码块结果如下:

执行获取分数数据代码块结果如下:

发现一个问题,图中红色部分的字段值要么为0,要么是null,通过上面的数据表可看出,数据库中是有数据的,那为什么没有获取到数据呢?

这是因为mybatis有个要求,实体中定义的字段名称必须和数据表中字段名称保持一致(大小写可忽略),名称一致的字段被读取到了,因此给赋值了,名称不一致的字段没有被读取到,就给了默认值,String类型默认null,Integer类型默认0.

那怎么将自己定义的字段与数据库字段一一对应拿起来,除了名称保持一致,还有什么办法呢?

使用@Results注解,在@select后,加上@Results,先来看一下@Results 和 @Result源码

@Results源码

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Results {
    String id() default "";

    Result[] value() default {};
}

有id 和 value属性,

id是一个唯一标识的名称

value 它的类型Result[]类型,用于存放映射字段关系

@Result源码

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Repeatable(Results.class)
public @interface Result {
    boolean id() default false;

    String column() default "";

    String property() default "";

    Class<?> javaType() default void.class;

    JdbcType jdbcType() default JdbcType.UNDEFINED;

    Class<? extends TypeHandler> typeHandler() default UnknownTypeHandler.class;

    One one() default @One;

    Many many() default @Many;
}

通过查看@Result注解源码发现,它有id,column,property……属性

id表示主键,默认为false;

column表示列,即数据表的列;

property表示属性,即实体中定义的字段;

因此我们就可以用它来进行字段映射

    @Select("select * from t_user")
    @Results(id = "userMap",value = {
            @Result(id = true,column = "id",property = "id"),
            @Result(column = "role",property = "role"),
            @Result(column = "name",property = "name"),
            @Result(column = "email",property = "email"),
            @Result(column = "name",property = "name"),
            @Result(column = "password",property = "pwd"),
    })
    List<UserEntity> findAll();

    @Select("select * from t_user where id=#{id}")
    @ResultMap(value = "userMap")
    UserEntity findById(Integer id);

这里又产生一个问题 @Results注解是写在findAll()方法上,那findById(Integer id)方法怎么办?又要重新写一遍映射?

有@ResultMap以供使用,在@Results中有id属性,它是唯一标识,给它取个名,其它需要用到这个映射的地方直接用id即可,例如:findById(Integer id)方法上的@ResultMap(value = “userMap”)

映射完成后再来执行测试程序,看一下结果:

可以看到,数据全部获取成功。

看到这里,又产生一个问题,这一顿操作都是针对单表啊,实际情况肯定最起码都是2个表以上联查,这怎么办?

往下看,使用mybatis进行一对一一对多的联查

多表联合

  • 一对一:一个学科的分数只能是属于一个用户的,分数和用户的关系就是一对一
  • 一对多:一个用户可以有多个科目的分数,用户和分数的关系就是一对多
  • 即时加载:即查询时候一次性将所有数据全部取出,例如
  • 延时加载:需要多少数据就获取多少数据

例如当要查看用户信息,用户又关联的有分数数据,那么就可以先查询用户信息,需要查看分数时,再查相关的分数数据,这就是延时加载的体现,减少不必要的查询消耗,提升效率。如果采用即时加载,查看一个用户信息时候,会把这个用户相关联的分数也会查询出来,假如关联的分数数据有十万、百万级,而又只看了用户信息,没看关联的分数,但是后台却又把这百万级数据给加载出来,这会大大影响体验。

mybatis实现一对一查询

使用@one注解实现一对一,例如图中红色部分

@one源码如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface One {
    String select() default "";

    FetchType fetchType() default FetchType.DEFAULT;
}

select属性也是注解,它用来查询关联的表,这里可写sql语句,可调用方法,调用方法时,这里填写的一定是方法的全限定路径,由包名、类名、方法名组成。

fetchType这个属性指定了加载方式,即延时加载 和 即时加载

根据分数查询用户,在ScoreEntity中增加UserEntity类型的属性,每类课程数据只对应了一个用户,结果如下

mybatis实现一对多查询

使用@many注解,如图中红色部分

@many源码如下:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Many {
    String select() default "";

    FetchType fetchType() default FetchType.DEFAULT;
}

源码与@one相同,区别在于sql语句的返回值,因此当返回多条数据时,使用@many

根据用户查询分数,在UserEntity中增加List类型属性,由于用户和分数时一对多关系,查询结果如下:

可以看到用户123,124都关联了两条分数数据,用户125关联了1条,这就是一对多的体现。

注意事项

mybatis有两种开发方式,除了本文介绍的注解开发之外,另一种是对每个dao接口进行sql的配置,然而这两种方式不能并存,只能选择一种方式进行开发,否则在程序编译期间,会抛出找不到dao接口映射的异常


注意:本文归作者所有,未经作者允许,不得转载

全部评论: 0

    我有话说: