Mybatis的注解开发

132 阅读9分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第12天,点击查看活动详情

MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。

iBATIS一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Access Objects(DAO)

MyBatis ,是国内最火的持久层框架

采用了ORM思想解决了实体类和数据库表映射的问题。对JDBC进行了封装,屏蔽了JDBCAPI底层的访问细节,避免我们与jdbc的api打交 道,就能完成对数据的持久化操作。

O--Object java对象

R- Relation 关系,就是数据库中的一张表

M-mapping 映射

首先给大家介绍MyBatis的含义

  MyBatis是一个支持普通sql查询,存储过程和高级映射的优秀持久层框架。MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装。MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录。

源码分析:一般都是从helloworld入手

1、根据xml配置文件(全局配置文件mybatis-config.xml)创建一个SqlsessionFactory对象,mybatis-config.xml有数据源一些环境信息

2、sql映射文件EmployeeMapper.xml配置了每一个sql,以及sql的封装规则等。

3、将sql映射文件注册在全局配置文件中

4、写代码:

  • 根据全局配置文件得到sqlsessionFactory
  • 使用SqlSession工程进行crud、sqlseesion就代表和数据库进行会话,用完close
  • 使用sql标识告知mybatis来执行哪个sql,sql都是保存在sql映射文件中

测试类SqlSessionFactoryBuilder处打断点

第一步:根据mybatis-config.xml全局配置文件创建SqlSessionFactory对象、就是把配置文件的详细信息解析保存在了configuration对象中,返回包含了configuration的defaultSqsessionFactory对象

注意:mappedSatement对象代表一个增删改查的详细标签

1.Mybatis的注解开发

是使用注解将IUserDao.xml的配置进行注解

步骤:

  • 环境搭建
  • 单表CRUD操作(代理Dao方式)
  • 多表查询的配置

image.png

image.png

最输入项目名称

下面进行设置Maven工程的配置,因为我是自己配置的所有不会出现什么麻烦

image.png

导入依赖

image.png

设置实现User类其中加入Account类是为了实现多对多的数据模式

package com.mybatisD.daomain;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

/**
 * @author :程序员徐大大
 * @description:TODO
 * @date :2022-01-23 14:39
 */
public class User implements Serializable {
    private Integer id;
    private String username;
    private String address;
    private String sex;
    private Date birthday;
    //一对多关系映射,一个用户对应多个账户
    private List<Account> accounts;

    public List<Account> getAccounts() {
        return accounts;
    }

    public void setAccounts(List<Account> accounts) {
        this.accounts = accounts;
    }

    public Integer getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getSex() {
        return sex;
    }

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

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", address='" + address + '\'' +
                ", sex='" + sex + '\'' +
                ", birthday=" + birthday +
                '}';
    }
}


设置Account类其中加入User为的是实现多对多数据模式

package com.mybatisD.daomain;

import java.io.Serializable;

/**
 * @author :程序员徐大大
 * @description:TODO
 * @date :2022-01-23 16:57
 */
public class Account implements Serializable {
    private Integer id;
    private Integer uid;
    private double money;
    //多对一(mybatis中称之为一对一)的映射,一个账户只能属于一个用户
    private User user;

    public User getUser() {
        return user;
    }

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

    public Integer getId() {
        return id;
    }

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

    public Integer getUid() {
        return uid;
    }

    public void setUid(Integer uid) {
        this.uid = uid;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", uid=" + uid +
                ", money=" + money +
                '}';
    }
}

IUserDao接口类

package com.mybatisD.dao;

import com.mybatisD.daomain.User;
import org.apache.ibatis.annotations.*;
import org.apache.ibatis.mapping.FetchType;

import java.util.List;
/**
 * @author: 徐大大
 * @description: TODO
 * @date: 2022-01-23 14:50
 * 在mybatis中针对CRUD一共有四个注解
 * @Select @Insert @Update @Delete
 */
@CacheNamespace(blocking = true) //注解开启二级缓存,要在SqlMapConfig.xml里面开启二级缓存
public interface IUserDao {

    @Select("select * from user")
    List<User> findAll();//查询所有用户

    /**
     * @return
     * @description: 保存用户
     * @date: 2022-01-23 15:28
     */
    @Insert("insert into user (username,address,sex,birthday) values(#{username},#{address},#{sex},#{birthday})")
    void saveUser(User user);

    /**
     * @return
     * @description: 更新用户
     * @date: 2022-01-23 15:28
     */
    @Update("update user set username=#{username} ,sex=#{sex},address=#{address},birthday=#{birthday} where id =#{id}")
    void updateUser(User user);

    /**
     * @return
     * @description: 删除用户
     * @date: 2022-01-23 15:28
     */
    @Delete("delete from user where id = #{id}")
    void deleteUser(Integer id);

    /**
     * @return
     * @description: 根据ID查询用户
     * @date: 2022-01-23 15:28
     */
    @Select("select * from user where id = #{id}")
    User findById(Integer id);

    /**
     * @return
     * @description: 根据根据用户名称模糊查询
     * @date: 2022-01-23 15:28
     */
    @Select("select * from user where username like #{username}")
    //需要在测试类加入双%%号
    // @Select("select * from user where username like '%${value}%'")
    List<User> findUserByName(String username);

    /**
     * @return
     * @description: 查询用户总数量
     * @date: 2022-01-23 15:28
     */
    @Select("select count(*) from user")
    int findTotalUser();

    /**
     * @description: 根据ID查询但数据库于User实现类的id不一样
     * 其中id= userMap是可以配置所有的方法中都可以使用属性的别名
     * 在其他方法中使用@ResultMap(value={"userMap"})
     * @date: 2022-01-23 15:28
     * @return
     */
    /*@Select("select * from user where id = #{id}")
    @Results(id="userMap" ,value = {
            @Result(id = true,column = "id" , property = "userId"),
            @Result(id = true,column = "address" , property = "userAddress"),
            @Result(id = true,column = "sex" , property = "userSex"),
            @Result(id = true,column = "birthday" , property = "userBirthday"),
    })
    User findByOutherId(Integer id);*/

    @Select("select * from user")
    @Results(value = {
            @Result(property = "accounts", column = "id", many = @Many(select = "com.mybatisD.dao.IAccount.findAccountByUid",fetchType = FetchType.LAZY))//LAZY是延迟
    })
    List<User> findAllAccount();//查询所有用户

}

IAccountDao接口类

package com.mybatisD.dao;

import com.mybatisD.daomain.Account;
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 IAccountDao {
    /**
     * @description: 查询获取每个账户所属的用户信息
     * 多表查询
     * @date: 2022-01-23 16:28
     * @return
     */
    @Select("select * from account")
    @Results(value = {
            @Result(id = true,column = "id",property = "id"),
            @Result(column = "uid",property = "uid"),
            @Result(column = "money",property = "money"),
            @Result(property = "user" ,column = "uid" ,one=@One(select = "com.mybatisD.dao.IUserDao.findById" ,fetchType = FetchType.EAGER))
    })
    List<Account> findAll();
    /**
     * @description: 根据用户id查询账户信息
     * @date: 2022-01-23 18:14
     * @param
     * @return
     */
    @Select("select * from account where uid = #{id}")
    List<Account> findAccountByUid(Integer id);
}

SqlMapConfig.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属性:用于指定配置文件的位置,是按照类路径的写法来写平且必须存在与类路径下
    -->
    <properties resource="jdbc.properties">
    </properties>
    <!--开启二级缓存-->
    <typeAliases>
        <package name="com.mybatisD.daomain"/>
    </typeAliases>
    <!--配置环境-->
    <environments default="mysql">
        <!--配置mysql环境-->
        <environment id="mysql">
            <!--配置事物类型-->
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <!--配置连接数据库的4个信息-->
                <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>
        <mapper resource="com/mybatisD/dao/IUserDao.xml"/>
     <mapper resource="com/mybatisD/dao/IAccountDao.xml"/>
    </mappers>-->
    <!--如果要是用注解来配置的话,此处应该使用class属性指定被注解的dao全限定类名-->
    <!--<mappers>
        <mapper class="com.mybatisD.dao.IUserDao"/>
    </mappers>-->
    <!--配置别名-->

    <!--指定带有注解的dao接口所在位置-->
    <mappers>
        <!--package标签用于指定doa接口所在的包,当指定了之后就不需要在写mapper以及resource或者class了-->
        <package name="com.mybatisD.dao"/>
    </mappers>
</configuration>

jdbc.properties数据连接

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/newsql?characterEncoding=utf-8
jdbc.username=root
jdbc.password=

日志信息配置log4j.properties

log4j.rootCategory=debug,CONSOLE, LOGFILE
log4j.logger.org.apache.axis.enterprise=FATAL, CONSOLE
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
log4j.appender.LOGFILE=org.apache.log4j.FileAppender
log4j.appender.LOGFILE.File=d:\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

User测试实现类

package com.mybateisT.TestD;

import com.mybatisD.dao.IUserDao;
import com.mybatisD.daomain.User;
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 org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.List;

/**
 * @author :程序员徐大大
 * @description:TODO
 * @date :2022-01-23 14:53
 */
public class MybatisAnnoTest {
    /**
     * @author: 测试基于注解的mybatis使用
     * @description: TODO
     * @date: 2022-01-23 14:50
     * @return
     */
    private InputStream in;
    private SqlSessionFactory factory;
    private SqlSession session;
    private IUserDao userDao;
    @Before
    public void init() throws IOException {
        //1.获取字节输入流
         in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.根据字节输入流构建SqlSessionFactory
        factory = new SqlSessionFactoryBuilder().build(in);
        //3.根据SqlSessionFactory生产一个SqlSession
        session = factory.openSession();
        //4.使用Selsession获取Dao代理对象
        userDao = session.getMapper(IUserDao.class);
        }
        @After
        public void destory() throws IOException {
        session.commit();
        session.close();
        in.close();
        }
    @Test
    public void TestFindAll() throws IOException {
        //5.执行Dao方法
        List<User> users = userDao.findAll();
        for (User user: users) {
            System.out.println(user);
        }
    }
    @Test
    public void TestSave() {
        User user = new User();
        user.setUsername("test to annotation");
        user.setAddress("北极");
        user.setSex("女");
        user.setBirthday(new Date());
        userDao.saveUser(user);
        System.out.println(user);
    }
    @Test
    public void TestUpdate() {
        User user = new User();
        user.setId(3);
        user.setUsername("mybatis to annotation update");
        user.setAddress("北极");
        user.setSex("nv");
        user.setBirthday(new Date());
        userDao.updateUser(user);
        System.out.println(user);
    }
    @Test
    public void TestDelete() {
        List<User> users = userDao.findAll();
        userDao.deleteUser(12);
        for (User user:users) {
            System.out.println(user);
        }
    }
    @Test
    public void TestFindOne() {
        User users = userDao.findById(1);
        System.out.println(users);
    }
    @Test
    public void TestFindName() {
        List<User> users = userDao.findUserByName("%赵%");
        for (User user: users) {
            System.out.println(users);
        }
    }
    @Test
    public void TestTotal() {
        int total = userDao.findTotalUser();
        System.out.println(total);
    }
    @Test
    public void testFindAccount(){
        List<User> users = userDao.findAllAccount();
        for (User user:users) {
            System.out.println("-----用户信息------");
           System.out.println(user);
            System.out.println(user.getAccounts());
        }
    }
}

Account测试实现类

package com.mybateisT.TestD;

import com.mybatisD.dao.IAccount;
import com.mybatisD.dao.IUserDao;
import com.mybatisD.daomain.Account;
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 org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

/**
 * @author :程序员徐大大
 * @description:TODO
 * @date :2022-01-23 17:39
 */
public class AccountTest {
    private InputStream in;
    private SqlSessionFactory factory;
    private SqlSession session;
    private IAccount iAccount;
    @Before
    public void init() throws IOException {
        //1.获取字节输入流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.根据字节输入流构建SqlSessionFactory
        factory = new SqlSessionFactoryBuilder().build(in);
        //3.根据SqlSessionFactory生产一个SqlSession
        session = factory.openSession();
        //4.使用Selsession获取Dao代理对象
        iAccount = session.getMapper(IAccount.class);
    }
    @After
    public void destory() throws IOException {
        session.commit();
        session.close();
        in.close();
    }

    /**
     * @author: 程序员小徐同学
     * @description: 查询所有账户,并且获取每个账户所属用户信息
     * @date: 2022-01-23 17:40
     * @param
     * @return
     */
    @Test
    public void testFindAllAccount(){
        List<Account> accounts = iAccount.findAll();
        for (Account account:accounts) {
            System.out.println("--------每个账户信息---------");
            System.out.println(account);
            System.out.println(account.getUser());
        }
    }
}

二级缓存测试实现类

package com.mybateisT.TestD;

import com.mybatisD.dao.IUserDao;
import com.mybatisD.daomain.User;
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 org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

/**
 * @author :程序员徐大大
 * @description:测试二级缓存
 * @date :2022-01-23 18:34
 */
public class SecondLevelCatchTest {
    /**
     * @author: 测试二级缓存
     * @description: TODO
     * @date: 2022-01-23 14:50
     * @return
     */
    private InputStream in;
    private SqlSessionFactory factory;
    private SqlSession session;
    private IUserDao userDao;
    @Before
    public void init() throws IOException {
        //1.获取字节输入流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.根据字节输入流构建SqlSessionFactory
        factory = new SqlSessionFactoryBuilder().build(in);
        //3.根据SqlSessionFactory生产一个SqlSession
        session = factory.openSession();
        //4.使用Selsession获取Dao代理对象
        userDao = session.getMapper(IUserDao.class);
    }
    @After
    public void destory() throws IOException {
        in.close();
    }
    @Test
    public void TestFindOne() throws IOException {
        //5.执行Dao方法
        User user = userDao.findById(1);
        System.out.println(user);
        session.close();//释放一级缓存
        SqlSession session1 = factory.openSession();//再次打开session
        IUserDao userDao1 = session1.getMapper(IUserDao.class);
        User user1 = userDao1.findById(1);
        System.out.println(user1);
        System.out.println(user==user1);

    }
}





注解开发配置二级缓存现在SqlMapConfig.xml先配置

```xml
<!--开启二级缓存-->
    <typeAliases>
        <package name="com.mybatisD.daomain"/>
    </typeAliases>

在IUserDao接口类加入注解@CacheNamespace开启二级缓存

/**
 * @author: 徐大大
 * @description: TODO
 * @date: 2022-01-23 14:50
 * 在mybatis中针对CRUD一共有四个注解
 * @Select @Insert @Update @Delete
 */
@CacheNamespace(blocking = true) //注解开启二级缓存,要在SqlMapConfig.xml里面开启二级缓存
public interface IUserDao {

编写测类

package com.mybateisT.TestD;

import com.mybatisD.dao.IUserDao;
import com.mybatisD.daomain.User;
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 org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

/**
 * @author :程序员徐大大
 * @description:测试二级缓存
 * @date :2022-01-23 18:34
 */
public class SecondLevelCatchTest {
    /**
     * @author: 测试二级缓存
     * @description: TODO
     * @date: 2022-01-23 14:50
     * @return
     */
    private InputStream in;
    private SqlSessionFactory factory;
    private SqlSession session;
    private IUserDao userDao;
    @Before
    public void init() throws IOException {
        //1.获取字节输入流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.根据字节输入流构建SqlSessionFactory
        factory = new SqlSessionFactoryBuilder().build(in);
        //3.根据SqlSessionFactory生产一个SqlSession
        session = factory.openSession();
        //4.使用Selsession获取Dao代理对象
        userDao = session.getMapper(IUserDao.class);
    }
    @After
    public void destory() throws IOException {
        in.close();
    }
    @Test
    public void TestFindOne() throws IOException {
        //5.执行Dao方法
        User user = userDao.findById(1);
        System.out.println(user);
        session.close();//释放一级缓存
        SqlSession session1 = factory.openSession();//再次打开session
        IUserDao userDao1 = session1.getMapper(IUserDao.class);
        User user1 = userDao1.findById(1);
        System.out.println(user1);
        System.out.println(user==user1);

    }
}

image.png

Resources 类,顾名思义就是资源,用于读取资源文件。其有很多方法通过加载并解析资源文件,返回不同类型的 IO 流对象。

SqlSessionFactoryBuilder

用来创建 SqlSessionFactory 的创造者,在整个项目中,只会使用一次,就是用来创建 SqlSessionFactory 的。用后即丢

SqlSessionFactory

创建 sqlSession 的单例工厂。在整个项目中,应该只有一个 SqlSessionFactory 的单例

SqlSession

一个 SqlSession 对应着一次数据库会话,一次会话以SqlSession 对象的创建开始,以 SqlSession 对象的关闭结束。

SqlSession 接口对象是线程不安全的,所以每次数据库会话结束前,需要马上调用其 close()方法,将其关闭。再次需要会话,再次创建。

改进 MybatisUtil

在 快速开始 栏目中,我们将获取 Mybatis 的 sqlSession 对象的过程,封装成了一个工具类

但是,sqlSession 并没有作为一个成员变量,存储在 MybatisUtil 中,这样,我们对 sqlSession 执行 close ,必须要手动去调用我们外部获得的 sqlSession ,其实,我们可以将 sqlSession 变成一个成员变量,然后,在 MybatisUtil 中,写一个close 方法。

但是,我们要注意,sqlSession 是线程不安全的,每次使用完后,我们都需要进行 close,然后,在下次使用的时候,再次连接 sqlSession ,如果把 sqlSession ,作为一个静态成员变量,放在 MybatisUtil 中,势必会引发线程相关的问题。

为了解决这个问题,我们需要来介绍一下 ThreadLocal

1、 ThreadLocal

2、 使用 ThreadLocal 来改写

2.还有什么问题可以留言给我,会尽心解答的,觉得文章不错的话点个赞把