Mybatis框架学习17- Mybatis 注解开发

31 篇文章 0 订阅
18 篇文章 0 订阅

Mybatis 注解开发

​ 这几年来注解开发越来越流行,Mybatis 也可以使用注解开发方式,这样我们就可以减少编写 Mapper 映射文件了。

​ 这个是一般来说,一个 Mybatis 项目的目录结构:

在这里插入图片描述

​ 这个里面有两个 xml 文件,一个是主配置文件 sqlMapConfig.xml,另一个 IUserDao.xml 映射配置文件。

1. Mybatis 注解开发的环境搭建

  1. 不适用骨架创建一个maven项目

  2. 在 pom.xml 文件中导入以下的依赖

    <dependencies>
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.5.2</version>
            </dependency>
    
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.17</version>
            </dependency>
    
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.17</version>
            </dependency>
    
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
            </dependency>
        </dependencies>s
    
  3. 在 java 文件夹下创建 domain 包,创建好实体类

    package com.mybatis.domain;
    
    import java.io.Serializable;
    import java.util.Date;
    
    public class User implements Serializable {
    
        private Integer id;
        private String username;
        private String address;
        private String sex;
        private Date birthday;
    
        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 +
                    '}';
        }
    }
    
    
  4. 在 java 文件夹在创建好 dao 类的包,并创建好 dao 接口类:

    package com.mybatis.dao;
    
    import com.mybatis.domain.User;
    import org.apache.ibatis.annotations.Select;
    
    import java.util.List;
    
    /**
     * 在 Mybatis 中针对 CRUD 一共有4个注解
     * @Select @Insert @Delete @Update
     */
    public interface IUserDao {
    
        /**
         * 查询所有用户
         * @return
         */
        @Select("select * from user")
        List<User> findAll();
    }
    
    
  5. 接着在 resources 资源文件夹下创建主配置文件 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="jdbcConfig.properties"></properties>
    <!--    配置别名-->
        <typeAliases>
            <package name="com.mybatis.domain"></package>
        </typeAliases>
    <!--    配置环境变量-->
        <environments default="mysql">
            <environment id="mysql">
                <transactionManager type="JDBC"></transactionManager>
                <dataSource type="POOLED">
                    <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>
    <!--    指定带有注解的dao接口所在位置-->
        <mappers>
            <package name="com.mybatis.dao"/>
        </mappers>
    </configuration>
    
  6. 然后,为了使数据库连接池能够正常的使用,需要创建一个存放有连接配置的配置文件:

    jdbc.driver=com.mysql.cj.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/day23?serverTimezone=Asia/Shanghai
    jdbc.username=root
    jdbc.password=root
    
  7. 至此,环境已经搭建完毕。

  8. 以下是目录结构:

    在这里插入图片描述

  9. 在测试包中创建测试类来测试我们的项目:

    package com.mybatis.test;
    
    import com.mybatis.dao.IUserDao;
    import com.mybatis.domain.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;
    
    public class AnnotationTest {
    
        private SqlSession sqlSession;
        private InputStream in;
        private IUserDao iUserDao;
    
        @Before
        public void init() throws IOException {
            in = Resources.getResourceAsStream("SqlMapConfig.xml");
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
            sqlSession = factory.openSession(true);
            iUserDao = sqlSession.getMapper(IUserDao.class);
        }
    
        @After
        public void destroy() throws IOException {
            if (sqlSession != null) {
                sqlSession.close();
            }
            if (in != null) {
                in.close();
            }
        }
    
        @Test
        public void testFindAll() {
            List<User> all = iUserDao.findAll();
            for (User user: all) {
                System.out.println(user);
            }
        }
    
    }
    
  10. 最后查询的结果为:

    2019-09-09 20:33:33,725 96     [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Opening JDBC Connection
    2019-09-09 20:33:34,705 1076   [           main] DEBUG source.pooled.PooledDataSource  - Created connection 1217467887.
    2019-09-09 20:33:34,708 1079   [           main] DEBUG m.mybatis.dao.IUserDao.findAll  - ==>  Preparing: select * from user 
    2019-09-09 20:33:34,732 1103   [           main] DEBUG m.mybatis.dao.IUserDao.findAll  - ==> Parameters: 
    2019-09-09 20:33:34,760 1131   [           main] DEBUG m.mybatis.dao.IUserDao.findAll  - <==      Total: 6
    User{id=41, username='老王', address='北京', sex='男', birthday=Tue Feb 27 17:47:08 CST 2018}
    User{id=42, username='小二王', address='北京金燕龙', sex='女', birthday=Fri Mar 02 15:09:37 CST 2018}
    User{id=43, username='update user clear cache', address='江苏南通海安', sex='男', birthday=Sun Mar 04 11:34:34 CST 2018}
    User{id=45, username='传智播客', address='北京金燕龙', sex='男', birthday=Sun Mar 04 12:04:06 CST 2018}
    User{id=46, username='老王', address='北京', sex='女', birthday=Wed Mar 07 17:37:26 CST 2018}
    User{id=48, username='小马宝莉', address='北京修正', sex='女', birthday=Thu Mar 08 11:44:00 CST 2018}
    2019-09-09 20:33:34,762 1133   [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@489115ef]
    2019-09-09 20:33:34,762 1133   [           main] DEBUG source.pooled.PooledDataSource  - Returned connection 1217467887 to pool.
    

2. Mybatis 常用的基础注解说明

  • @Insert:实现新增
  • @Update:实现更新
  • @Delete:实现删除
  • @Select:实现查询
  • @Result:实现结果集的封装
  • @Results:可以与 @Result 一起使用,封装多个结果集
  • @ResultMap:实现引用 @Result 定义的封装
  • @One:实现一对一结果集封装
  • @Many:实现一对多结果集封装
  • @SelectProvider:实现动态 SQL 映射
  • @CacheNamespace:实现注解二级缓存的使用
  • @Param:通常传入 Mybatis 的只有一个,当传入 Mybatis 的参数大于或等于两个的时候,需要对传入的每一个参数进行注释,好让 Mybatis 将参数和 SQL 中的参数进行对应。

3. Mybatis 使用注意事项 - 以查询为例

​ 根据上面的测试类和之前所以写的注解,会发现,我们根本没有写映射配置文件,那么,Mybatis 是怎么知道传入的数据类型和封装的数据类型呢?

​ 我们原先的做法是:

​ 在和 resources 文件夹下创建,和 java dao 类所在包,相同结构的文件夹组。并在该文件夹组下创建一个映射配置文件:

<?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.itheima.dao.IUserDao">
    <!-- 查询所有 -->
    <select id="findAll" resultType="user">
        select * from user
    </select>
</mapper>

​ 而在这个配置文件中,父标签 mapper 中的 namespace 属性,和其字标签 select 标签中的 id 属性,都是该映射配置文件中的唯一标识。

​ 而 select 标签天的 resultType 属性和 sql 语句,都是 Mybatis 在执行时,所需要用到的。

​ 然后,打开现在使用注解的接口,此时,该项目中已经没有映射配置文件了:

package com.mybatis.dao;

import com.mybatis.domain.User;
import org.apache.ibatis.annotations.Select;

import java.util.List;

/**
 * 在 Mybatis 中针对 CRUD 一共有4个注解
 * @Select @Insert @Delete @Update
 */
public interface IUserDao {

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

​ 其和之前的映射配置文件的差距在,映射配置文件啰里啰嗦写了一堆,而注解一句 sql 语句就搞定了。同时,这一句注解,将所有的映射配置文件中的事情都说清了。

​ 仔细观察,会发现,select 注解注解的方法,其实是知道的。知道了该方法,同时也就能找到该方法的类(IUserDao)和该方法的全限定类名(com.mybatis.dao.IUserDao)。所以当这三个一出场,就是之前的映射配置文件中,所需要的那些属性。最后只剩一个结果类型,结果类型就在 List中的 User。

​ 这里需要注意,如果我我们的同时拥有映射配置文件和注解,运行 Mybatis 都会报错。

​ 所以,我们在开发的时候,通常要么全部用注解,要么全部都用 xml 。

4. Mybatis 注解开发进行 CRUD 操作

4.1 使用 Mybatis 注解开发进行保存操作

  1. 首先,在 dao 接口中创建一个新的方法,并且标志注解:

    
        /**
         * 保存用户
         * @param user
         */
        @Insert("insert into user (username, address, sex, birthday) values (#{username},#{address}, #{sex},#{birthday})")
        void saveUser(User user);
    
  2. 在测试类中进行测试:

    package com.mybatis.test;
    
    import com.mybatis.dao.IUserDao;
    import com.mybatis.domain.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;
    
    public class AnnotationTest {
    
        private SqlSession sqlSession;
        private InputStream in;
        private IUserDao iUserDao;
    
        @Before
        public void init() throws IOException {
            in = Resources.getResourceAsStream("SqlMapConfig.xml");
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
            sqlSession = factory.openSession(true);
            iUserDao = sqlSession.getMapper(IUserDao.class);
        }
    
        @After
        public void destroy() throws IOException {
            if (sqlSession != null) {
                sqlSession.close();
            }
            if (in != null) {
                in.close();
            }
        }
    
    
        @Test
        public void testInsertUser() {
            User user = new User();
            user.setAddress("江苏南通海安软件园NIIT培训中心");
            user.setUsername("卢本伟牛逼");
            user.setBirthday(new Date());
    
            iUserDao.saveUser(user);
        }
    }
    

4.2 使用 Mybatis 注解开发进行更新操作

  1. 首先,在 dao 接口中创建一个新的方法,并且标志注解:

        /**
         * 更新用户信息
         * @param user
         */
        @Update("update user set username = #{username}, sex = #{sex}, address = #{address}, birthday = #{birthday} where id = #{id}")
        void updateUser(User user);
    
  2. 然后,在测试类中进行测试:

    package com.mybatis.test;
    
    import com.mybatis.dao.IUserDao;
    import com.mybatis.domain.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;
    
    public class AnnotationTest {
    
        private SqlSession sqlSession;
        private InputStream in;
        private IUserDao iUserDao;
    
        @Before
        public void init() throws IOException {
            in = Resources.getResourceAsStream("SqlMapConfig.xml");
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
            sqlSession = factory.openSession(true);
            iUserDao = sqlSession.getMapper(IUserDao.class);
        }
    
        @After
        public void destroy() throws IOException {
            if (sqlSession != null) {
                sqlSession.close();
            }
            if (in != null) {
                in.close();
            }
        }
    
        @Test
        public void testUpdateUser() {
            User user = new User();
            user.setAddress("江苏南通海安软件园NIIT培训中心");
            user.setUsername("卢本伟牛逼");
            user.setBirthday(new Date());
            user.setId(60);
            user.setSex("男");
    
            iUserDao.updateUser(user);
        }
    }
    

4.3 使用 Mybatis 注解开发进行删除操作

  1. 首先,在 dao 接口中创建一个新的方法,并且标志注解:

        /**
         * 根据 id 删除用户信息
         * @param id
         */
        @Update("delete from user where id = #{id}")
        void deleteUser(int id);
    
  2. 然后,在测试类中进行测试:

    package com.mybatis.test;
    
    import com.mybatis.dao.IUserDao;
    import com.mybatis.domain.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;
    
    public class AnnotationTest {
    
        private SqlSession sqlSession;
        private InputStream in;
        private IUserDao iUserDao;
    
        @Before
        public void init() throws IOException {
            in = Resources.getResourceAsStream("SqlMapConfig.xml");
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
            sqlSession = factory.openSession(true);
            iUserDao = sqlSession.getMapper(IUserDao.class);
        }
    
        @After
        public void destroy() throws IOException {
            if (sqlSession != null) {
                sqlSession.close();
            }
            if (in != null) {
                in.close();
            }
        }    
    	@Test
        public void testDeleteUser() {
            int id = 60;
            iUserDao.deleteUser(id);
        }
    }
    

4.4 使用 Mybatis 注解开发进行查询操作

​ 最后上面的引入已经使用了注解开发进行查询操作,这里不再赘述。

4.5 使用 Mybatis 注解开发进行查询单个用户信息操作

  1. 首先,在 dao 接口中创建一个新的方法,并且标志注解:

        /**
         * 根据 id 查找用户
         * @param id
         */
        @Select("select * from user where id = #{id}")
        User findById(int id);
    }
    
  2. 然后,在测试类中进行测试:

    package com.mybatis.test;
    
    import com.mybatis.dao.IUserDao;
    import com.mybatis.domain.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;
    
    public class AnnotationTest {
    
        private SqlSession sqlSession;
        private InputStream in;
        private IUserDao iUserDao;
    
        @Before
        public void init() throws IOException {
            in = Resources.getResourceAsStream("SqlMapConfig.xml");
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
            sqlSession = factory.openSession(true);
            iUserDao = sqlSession.getMapper(IUserDao.class);
        }
    
        @After
        public void destroy() throws IOException {
            if (sqlSession != null) {
                sqlSession.close();
            }
            if (in != null) {
                in.close();
            }
        }
    
        @Test
        public void testFindAll() {
            List<User> all = iUserDao.findAll();
            for (User user: all) {
                System.out.println(user);
            }
        }
    
        
        @Test
        public void testFindById() {
            int id = 48;
            User byId = iUserDao.findById(id);
            System.out.println(byId);
        }
    }
    

4.6 使用 Mybatis 注解开发进行模糊查询操作

  1. 首先,在 dao 接口中创建一个新的方法,并且标志注解:

        /**
         * 根据用户名进行模糊查询
         * @param username
         * @return
         */
        @Select("select * from user where username like #{username}")
        List<User> findUserByName(String username);
    
  2. 然后,在测试类中进行测试:

    package com.mybatis.test;
    
    import com.mybatis.dao.IUserDao;
    import com.mybatis.domain.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;
    
    public class AnnotationTest {
    
        private SqlSession sqlSession;
        private InputStream in;
        private IUserDao iUserDao;
    
        @Before
        public void init() throws IOException {
            in = Resources.getResourceAsStream("SqlMapConfig.xml");
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
            sqlSession = factory.openSession(true);
            iUserDao = sqlSession.getMapper(IUserDao.class);
        }
    
        @After
        public void destroy() throws IOException {
            if (sqlSession != null) {
                sqlSession.close();
            }
            if (in != null) {
                in.close();
            }
        }
        
    	@Test
        public void testFindByName() {
            String username = "%王";
            List<User> users = iUserDao.findUserByName(username);
            for (User user : users) {
                System.out.println(user);
            }
        }
    }
    

4.7 使用 Mybatis 注解开发进行总记录条数的查询

  1. 首先,在 dao 接口中创建一个新的方法,并且标志注解:

        /**
         * 查询总用户数量
         * @return
         */
        @Select("select count(*) from user")
        int findTotal();
    }
    

    这里需要注意 count(*) 中间不能分开。

    count (*) 这样是错误的

  2. 在测试类中进行测试

    package com.mybatis.test;
    
    import com.mybatis.dao.IUserDao;
    import com.mybatis.domain.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;
    
    public class AnnotationTest {
    
        private SqlSession sqlSession;
        private InputStream in;
        private IUserDao iUserDao;
    
        @Before
        public void init() throws IOException {
            in = Resources.getResourceAsStream("SqlMapConfig.xml");
            SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
            sqlSession = factory.openSession(true);
            iUserDao = sqlSession.getMapper(IUserDao.class);
        }
    
        @After
        public void destroy() throws IOException {
            if (sqlSession != null) {
                sqlSession.close();
            }
            if (in != null) {
                in.close();
            }
        }
    	@Test
        public void testFindTotal() {
            int total = iUserDao.findTotal();
            System.out.println(total);
        }
    }
    

4.8 使用@Param对 Mybatis 中的多参数进行注解 - 修改于 2019/9/13

我们来看这样的一个例子,如果我们需要规定,查询时一次只查询出5条数据,在使用 SQL 语句时,我们的操作是:

select * from user limit 0, 5;

这一段 SQL 语句的意思是,查询区间为 (0, 5] 中的数据。
反应到注解上就是:

@Select("select * from user limit ${begin}, #{end}
List<User> findLimit(Integer begin, Integer end);

如果这么写,Mybatis 会报错,如果你传入的数据为 begin=0, end=10,那么错误信息为:

SQL 语句错误,错误在 "limit 10, null"

这里的原因就是,当你传入的参数只有一个的时候,没话讲,Mybatis 知道将你传入的数据填入到 #{} 中,但是,当你传入两个参数的时候,Mybatis 就知道怎么将参数和 #{} 进行对应。所以,需要 @Param 注解:

@Select("select * from user limit ${begin}, ${end})
List<User> findLimit(@Param("bengin") Integer begin, @Param("end") Integer end);

这个 @Param 注解稍微有一点奇怪,它注解在参数上面的。

5. Mybatis 进行多表操作(使用注解进行复杂关系映射的开发)

5.1 Mybatis 复杂关系映射注解的说明

5.1.1 Results 和 Result 注解

@Results 注解,代替的是标签resultMap

在这里插入图片描述
其中 id 为该封装结果集注解的唯一标识,value 为 @Result 数组。

该注解中可以使用单个 @Result 注解,也可以使用 @Result 集合

@Results ( values =  
   { 
    @Result(), 
    @Result()
	} 
) 

@Result 注解

代替了 id 标签 和 result 标签

@Result 中的属性介绍:

在这里插入图片描述

  • id 是否是主键字段

  • column 数据库的列名

  • property 需要装配的属性名

  • one 需要使用的 @One 注解( @Result ( one = @One ( ) ) )

    @Result(
    	one = @One ()
    )
    
  • many 需要使用的 @Many 注解 ( @Result ( many = @Many ( ) ) )

    @Result(
    	many = @Many()
    )
    
5.1.2 One 注解

@One 注解 (一对一, 多对一)

代替了 association 标签,是多表查询的关键,在注解中用来指定子查询返回单一对象。

在这里插入图片描述

@One 注解属性介绍:

  • select 指定用来多表查询的 sqlmapper
  • fetchType 会覆盖全局的配置参数 lazyLoadingEnabled

使用格式:

@Result(
	column = "",
    property = "",
    one = @One(select = "")
)

@Many 注解(一对多)

代替了 collection 标签,是多表查询的关键,在注解中用来指定子查询返回对象集合。

在这里插入图片描述

注意:聚集元素用来处理 “ 一对多 ” 的关系。需要指定映射的 Java 实体类的属性,属性的 javaType (一般为 ArrayList)但是注解中可以不定义

使用格式:

@Result ( property=" “, column=” ", many = @Many(select = " " ) )

@Result(
	property="",
    column="",
    many=@Many(select="")
)

5.2 Mybatis 注解建立实体类属性和数据库表中列的对应关系

​ 在使用注解时,可以通过使用 @Results 和 @Result 注解的组合,来完成当实体类属性名和数据库表中的列名不相同时,建立两者之间关系的任务。

​ 在 Dao 接口的对应方法上,添加以下注释:

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

​ (这里的实体类中的属性我并没有去修改)

​ 但是,当我们我去使用通过 id 查找数据时,会发现数据并没有封装进去。这个时候,@Results 注解中的 id 属性派上用场了。

​ 在 @Results 中写入 id 属性,并命名为 “userMap”:

    /**
     * 查询所有用户
     *
     * @return
     */
    @Select("select * from user")
    @Results(id = "userMap",value = {
            @Result(id = true, column = "id", property = "id"),
            @Result(column = "username", property = "username"),
            @Result(column = "sex", property = "sex"),
            @Result(column = "address", property = "address"),
            @Result(column = "birthday", property = "birthday"),
    })
    List<User> findAll();

​ 然后,在 dao 接口中的通过 id 查询用户的方法就可以通过这个 id 获取配置信息(使用 @ResultMap 注解):

    /**
     * 根据 id 查找用户
     * @param id
     */
    @Select("select * from user where id = #{id}")
    @ResultMap(value = {"userMap"})
    User findById(int id);

5.3 Mybatis 注解开发一对一(多对一)查询操作

5.3.1 环境的搭建

​ 在之前的环境的基础上,为了实现了一对一(多对一)的查询,我们得在数据库中额外创建一个表:

DROP TABLE IF EXISTS `account`;

CREATE TABLE `account` (
  `ID` int(11) NOT NULL COMMENT '编号',
  `UID` int(11) default NULL COMMENT '用户编号',
  `MONEY` double default NULL COMMENT '金额',
  PRIMARY KEY  (`ID`),
  KEY `FK_Reference_8` (`UID`),
  CONSTRAINT `FK_Reference_8` FOREIGN KEY (`UID`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;



insert  into `account`(`ID`,`UID`,`MONEY`) values (1,46,1000),(2,45,1000),(3,46,2000);
5.3.2 Mybatis 注解开发一对一(多对一)查询操作

​ 然后在 domain 文件下创建一个 Account 实体类:

package com.mybatis.domain;

import java.io.Serializable;

public class Account implements Serializable {

    private Integer id;
    private Integer uid;
    private Double money;

    // 多对一 ( mybatis 中称之为一对一 )的映射
    private 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;
    }

    public User getUser() {
        return user;
    }

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

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

​ 最后,在 dao 文件夹下创建 IAccount 接口文件:

package com.mybatis.dao;

import com.mybatis.domain.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 {

    /**
     * 查询所有账户,并且获取每个账户所属的用户信息
     * @return
     */
    @Select("select * from account")
    @Results(id = "accountMap", 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.mybatis.dao.IUserDao.findById",
                            fetchType = FetchType.EAGER
                    )
            )
    })
    List<Account> findAll();

}

​ 这里的 @One 注解是一对一的映射关系。

@Result(property = "user", column = "uid",
                    one = @One(
                            select = "com.mybatis.dao.IUserDao.findById",
                            fetchType = FetchType.EAGER
                    )

​ 这一段代码的意思是:封装的是 user, 通过 uid 字段查询数据库中的user表, 使用的是一对一映射关系;在一对一映射关系中,select 表示通过 findByid 这个方法,使用 uid 字段,将查询出来的数据,封装到 User 对象中,fetchtype 表示是否是延迟查询。

5.4 Mybatis 注解开发一对多(多对多)查询操作

5.4.1 一对多

​ 因为我们需要在查询用户的信息的同时,能够根据要求查询出账户的信息。所以首先,现在 User 实体类中,完成对 Account 实体类的映射。

package com.mybatis.domain;

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

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 +
                '}';
    }
}

​ 然后,在 IUserDao 这个接口中,在 @Results 注解中加入对 account 表的映射:

    /**
     * 查询所有用户
     *
     * @return
     */
    @Select("select * from user")
    @Results(id = "userMap", value = {
            @Result(id = true, column = "id", property = "id"),
            @Result(column = "username", property = "username"),
            @Result(column = "sex", property = "sex"),
            @Result(column = "address", property = "address"),
            @Result(column = "birthday", property = "birthday"),
            @Result(property = "accounts", column = "id", many = @Many(
                    select = "com.mybatis.dao.IAccountDao.findByUid",
                    fetchType = FetchType.LAZY
                )
            )
    })
    List<User> findAll();

​ 这里的映射,指的是封装在 accounts 这个 List 容器中,通过使用 id 字段和 IAccount 接口中的 findByUid 方法,来查询出所需要的数据。使用的查询方式是延迟查询。

5.4.2 多对多

由于多对多中,在数据库中是有三张表的,其中两种表用来存储信息,而最后一张表用来存储另外两个表之间的关系。
在这里插入图片描述
这样的话,就必须通过 user_role,来查询两个的数据。实际上操作起来很像一对多。

  • 首先在 User 实体类中创建对 Role 实体的引用:
      private List<Role> roles;
    
     public List<Role> getRoles() {
         return roles;
     }
    
     public void setRoles(List<Role> roles) {
         this.roles = roles;
     }
    
  • 然后在 UserDao 中进行查询的 sql 语句的编辑:
      /**
      * 查找所有用户
      * @return
      */
     @Select("select * from user where id in (select uid from user_role )")
     @Results(id = "userMap" , value = {
             @Result(id = true, property = "id", column = "id"),
             @Result(property = "address", column = "address"),
             @Result(property = "sex", column = "sex"),
             @Result(property = "username", column = "username"),
             @Result(property = "birthday", column = "birthday"),
             @Result(property = "roles",column = "id", many = @Many(
                     select = "review.mybatis.many.to.many.dao.RoleDao.findAllById",
                     fetchType = FetchType.LAZY
             ))
     })
     List<User> findAll();
    
    这里的首先查询关系表中,和 Role 表有关系的 User 表中的数据。然后再通过 RoleDao 中的 findAllById 方法查询 Role 表中和 User 表有关的数据
  • 接下来,在 AccountDao 接口类中,编写 findAllById 方法的 Sql 语句:
       /**
       * 通过 uid 查询所有的角色信息
       * @return
       */
      @Select("select * from role where id in (select rid from user_role where uid = #{uid})")
      @Results(id = "roleMap", value = {
              @Result(id = true, property = "roleID", column = "id"),
              @Result(property = "roleName", column = "role_name"),
              @Result(property = "roleDesc", column = "role_desc")
      })
      List<Role> findAllById(Integer uid);
    
  • 最后,在测试类中运行的结果为:
    User{id=41, address='北京', sex='男', username='老王', birthday=Tue Feb 27 17:47:08 CST 2018}
    Role{roleID=1, roleName='院长', roleDesc='管理整个学院'}
    Role{roleID=2, roleName='总裁', roleDesc='管理整个公司'}
    2019-09-12 11:21:15,060 914    [           main] DEBUG o.many.dao.RoleDao.findAllById  - ==>  Preparing: select * from role 		  where id in (select rid from user_role where uid = ?) 
    2019-09-12 11:21:15,060 914    [           main] DEBUG o.many.dao.RoleDao.findAllById  - ==> Parameters: 45(Integer)
    2019-09-12 11:21:15,061 915    [           main] DEBUG o.many.dao.RoleDao.findAllById  - <==      Total: 1
    User{id=45, address='北京金燕龙', sex='男', username='传智播客', birthday=Sun Mar 04 12:04:06 CST 2018}
    Role{roleID=1, roleName='院长', roleDesc='管理整个学院'}
    

6. Mybatis 注解开发使用缓存

6.1 Mybatis 注解开发,使用一级缓存

​ 一级缓存和使用配置文件一样,是不需要任何配置和注解的。直接使用即可。数据以 Map 的格式存放在 sqlSession 对象中。

​ 如果使用的是对象存放数据,那么从 sqlSession 取出的数据为对象,每次取的为同一个对象。

6.2 Mybatis 注解开发,使用二级缓存

​ 使用二级缓存,首先需要在主配置文件中的全局设置中,开启二级缓存(默认是开启的):

    <settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>

​ 然后,在 IuserDao 接口类中,在类名处,写上注解:

@CacheNamespace(blocking = true)
public interface IUserDao {

    /**
     * 查询所有用户
     *
     * @return
     */
    @Select("select * from user")
    @Results(id = "userMap", value = {
            @Result(id = true, column = "id", property = "id"),
            @Result(column = "username", property = "username"),
            @Result(column = "sex", property = "sex"),
            @Result(column = "address", property = "address"),
            @Result(column = "birthday", property = "birthday"),
            @Result(property = "accounts", column = "id", many = @Many(
                    select = "com.mybatis.dao.IAccountDao.findByUid",
                    fetchType = FetchType.LAZY
                )
            )
    })
    List<User> findAll();


    /**
     * 根据 id 查找用户
     *
     * @param id
     */
    @Select("select * from user where id = #{id}")
    @ResultMap(value = {"userMap"})
    User findById(int id);

    /**
     * 根据用户名进行模糊查询
     *
     * @param username
     * @return
     */
    @Select("select * from user where username like #{username}")
    @ResultMap(value = {"userMap"})
    List<User> findUserByName(String username);

}

​ 最后,在测试类中进行测试:

package com.mybatis.test;

import com.mybatis.dao.IUserDao;
import com.mybatis.domain.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;

public class SecondLevelCacheTest {
    private SqlSession sqlSession;
    private InputStream in;
    private IUserDao iUserDao;
    private SqlSessionFactory factory;

    @Before
    public void init() throws IOException {
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        factory = new SqlSessionFactoryBuilder().build(in);
        sqlSession = factory.openSession(true);
        iUserDao = sqlSession.getMapper(IUserDao.class);
    }

    @After
    public void destroy() throws IOException {
//        if (sqlSession != null) {
//            sqlSession.close();
//        }
        if (in != null) {
            in.close();
        }
    }

    @Test
    public void testFindOne() {
        User user = iUserDao.findById(48);
        System.out.println(user);
        sqlSession.close();

        SqlSession session = factory.openSession();
        IUserDao userDao = session.getMapper(IUserDao.class);
        User user1 = userDao.findById(48);
        System.out.println(user1);
        session.close();
        sqlSession.close();
    }
}

​ 由于这里使用的是同一个 SqlSessionFactory 所以,二级缓存存放的位置一样。

​ 这里是最后的结果:

2019-09-10 20:01:48,870 103    [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Opening JDBC Connection
2019-09-10 20:01:49,731 964    [           main] DEBUG source.pooled.PooledDataSource  - Created connection 769798433.
2019-09-10 20:01:49,735 968    [           main] DEBUG .mybatis.dao.IUserDao.findById  - ==>  Preparing: select * from user where id = ? 
2019-09-10 20:01:49,767 1000   [           main] DEBUG .mybatis.dao.IUserDao.findById  - ==> Parameters: 48(Integer)
2019-09-10 20:01:49,813 1046   [           main] DEBUG .mybatis.dao.IUserDao.findById  - <==      Total: 1
2019-09-10 20:01:49,814 1047   [           main] DEBUG atis.dao.IAccountDao.findByUid  - ==>  Preparing: select * from account where uid = ? 
2019-09-10 20:01:49,814 1047   [           main] DEBUG atis.dao.IAccountDao.findByUid  - ==> Parameters: 48(Integer)
2019-09-10 20:01:49,817 1050   [           main] DEBUG atis.dao.IAccountDao.findByUid  - <==      Total: 0
User{id=48, username='小马宝莉', address='北京修正', sex='女', birthday=Thu Mar 08 11:44:00 CST 2018}
2019-09-10 20:01:49,824 1057   [           main] DEBUG ansaction.jdbc.JdbcTransaction  - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@2de23121]
2019-09-10 20:01:49,824 1057   [           main] DEBUG source.pooled.PooledDataSource  - Returned connection 769798433 to pool.
2019-09-10 20:01:49,828 1061   [           main] DEBUG       com.mybatis.dao.IUserDao  - Cache Hit Ratio [com.mybatis.dao.IUserDao]: 0.5
User{id=48, username='小马宝莉', address='北京修正', sex='女', birthday=Thu Mar 08 11:44:00 CST 2018}

​ 发现,在查询过一次之后,并没有查询第二次,而是在缓存中查询。(缓存命中率为0.5)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值